diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 5008ddfc..00000000 Binary files a/.DS_Store and /dev/null differ diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..05392e5d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,18 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.php] +indent_size = 4 + +[composer.json] +indent_size = 4 + +[composer.lock] +indent_size = 4 diff --git a/.github/ISSUE_TEMPLATE/REPORT_IN_ENGLISH.md b/.github/ISSUE_TEMPLATE/REPORT_IN_ENGLISH.md new file mode 100644 index 00000000..36d01112 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/REPORT_IN_ENGLISH.md @@ -0,0 +1,20 @@ +--- +name: Report a bug +about: Report a bug in English + +--- + +# Tell me your versions +- **PHP** + +- **JDK** + +- **PHPJava** + +# Summary + +# Code + +# Expect + +# Actual diff --git a/.github/ISSUE_TEMPLATE/REPORT_IN_JAPANESE.md b/.github/ISSUE_TEMPLATE/REPORT_IN_JAPANESE.md new file mode 100644 index 00000000..20bd367c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/REPORT_IN_JAPANESE.md @@ -0,0 +1,19 @@ +--- +name: バグ報告 +about: 日本語でのバグ報告 +--- +# 実行時のバージョン +- **PHP** + +- **JDK** + +- **PHPJava** + +# 概要 + +# 記述したコード + +# 期待する結果 + +# 実際の結果 + diff --git a/.github/ISSUE_TEMPLATE/REQUEST_IMPLEMENTATION_IN_ENGLISH.md b/.github/ISSUE_TEMPLATE/REQUEST_IMPLEMENTATION_IN_ENGLISH.md new file mode 100644 index 00000000..4caa7417 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/REQUEST_IMPLEMENTATION_IN_ENGLISH.md @@ -0,0 +1,12 @@ +--- +name: Request implementation +about: Request implementation in English + +--- + +# What do you want to implement? + +# Example Code + +# Expect + diff --git a/.github/ISSUE_TEMPLATE/REQUEST_IMPLEMENTATION_IN_JAPANESE.md b/.github/ISSUE_TEMPLATE/REQUEST_IMPLEMENTATION_IN_JAPANESE.md new file mode 100644 index 00000000..282a2d87 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/REQUEST_IMPLEMENTATION_IN_JAPANESE.md @@ -0,0 +1,12 @@ +--- +name: 実装の要望 +about: 日本語で実装の要望 + +--- + +# 実装を要望するにあたっての概要を教えてください + +# コードの一例 + +# 期待する結果 + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..e8aa2181 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,24 @@ +name: php-java test +on: push +jobs: + test: + runs-on: ${{ matrix.operating-system }} + strategy: + matrix: + operating-system: [ ubuntu-18.04 ] + php: [ '7.3', '7.4' ] + name: PHP ${{ matrix.php }} + steps: + - name: Checkout + uses: actions/checkout@master + + - name: Setup PHP and Run test + uses: nanasess/setup-php@master + with: + php-version: ${{ matrix.php }} + - run: | + curl -s https://get.sdkman.io | bash + source "${HOME}/.sdkman/bin/sdkman-init.sh" + sdk install kotlin + composer install + composer run tests diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..70baaf38 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +.DS_Store +old +.idea +vendor +composer.lock +.php_cs.cache +.phpunit.result.cache +tests/Cases/caches/ +!tests/Cases/caches/.gitkeep +!tests/Cases/caches/compiler/.gitkeep +tools +cghooks.lock diff --git a/.php_cs.dist b/.php_cs.dist new file mode 100644 index 00000000..b23db937 --- /dev/null +++ b/.php_cs.dist @@ -0,0 +1,37 @@ +in(__DIR__ . '/src') + ->in(__DIR__ . '/tests'); + +return PhpCsFixer\Config::create() + ->setRules([ + '@PSR2' => true, + '@PhpCsFixer' => true, + 'array_syntax' => [ + 'syntax' => 'short', + ], + 'blank_line_after_opening_tag' => false, + 'blank_line_before_statement' => [ + 'statements' => [], + ], + 'concat_space' => [ + 'spacing' => 'one', + ], + 'increment_style' => [ + 'style' => 'post', + ], + 'no_multiline_whitespace_before_semicolons' => true, + 'no_superfluous_phpdoc_tags' => true, + 'ordered_class_elements' => false, + 'php_unit_internal_class' => false, + 'php_unit_test_class_requires_covers' => false, + 'phpdoc_align' => [ + 'align' => 'left', + ], + 'phpdoc_separation' => false, + 'phpdoc_to_comment' => false, + 'phpdoc_var_without_name' => false, + 'single_blank_line_before_namespace' => false, + 'yoda_style' => false, + ]) + ->setFinder($finder); diff --git a/JavaTest/build.xml b/JavaTest/build.xml deleted file mode 100644 index 8cd24b09..00000000 --- a/JavaTest/build.xml +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - - - - Builds, tests, and runs the project JavaTest. - - - diff --git a/JavaTest/build/built-jar.properties b/JavaTest/build/built-jar.properties deleted file mode 100644 index 4d862759..00000000 --- a/JavaTest/build/built-jar.properties +++ /dev/null @@ -1,4 +0,0 @@ -#Sat, 27 Sep 2014 16:16:26 +0900 - - -D\:\\PHPJava\\JavaTest= diff --git a/JavaTest/build/classes/javatest/JavaTest$testClass$testClass2.class b/JavaTest/build/classes/javatest/JavaTest$testClass$testClass2.class deleted file mode 100644 index 32d943b4..00000000 Binary files a/JavaTest/build/classes/javatest/JavaTest$testClass$testClass2.class and /dev/null differ diff --git a/JavaTest/build/classes/javatest/JavaTest$testClass.class b/JavaTest/build/classes/javatest/JavaTest$testClass.class deleted file mode 100644 index f9de7514..00000000 Binary files a/JavaTest/build/classes/javatest/JavaTest$testClass.class and /dev/null differ diff --git a/JavaTest/build/classes/javatest/JavaTest.class b/JavaTest/build/classes/javatest/JavaTest.class deleted file mode 100644 index a8c34408..00000000 Binary files a/JavaTest/build/classes/javatest/JavaTest.class and /dev/null differ diff --git a/JavaTest/dist/JavaTest.jar b/JavaTest/dist/JavaTest.jar deleted file mode 100644 index 4582f54b..00000000 Binary files a/JavaTest/dist/JavaTest.jar and /dev/null differ diff --git a/JavaTest/dist/README.TXT b/JavaTest/dist/README.TXT deleted file mode 100644 index 123b9552..00000000 --- a/JavaTest/dist/README.TXT +++ /dev/null @@ -1,32 +0,0 @@ -======================== -BUILD OUTPUT DESCRIPTION -======================== - -When you build an Java application project that has a main class, the IDE -automatically copies all of the JAR -files on the projects classpath to your projects dist/lib folder. The IDE -also adds each of the JAR files to the Class-Path element in the application -JAR files manifest file (MANIFEST.MF). - -To run the project from the command line, go to the dist folder and -type the following: - -java -jar "JavaTest.jar" - -To distribute this project, zip up the dist folder (including the lib folder) -and distribute the ZIP file. - -Notes: - -* If two JAR files on the project classpath have the same name, only the first -JAR file is copied to the lib folder. -* Only JAR files are copied to the lib folder. -If the classpath contains other types of files or folders, these files (folders) -are not copied. -* If a library on the projects classpath also has a Class-Path element -specified in the manifest,the content of the Class-Path element has to be on -the projects runtime path. -* To set a main class in a standard Java project, right-click the project node -in the Projects window and choose Properties. Then click Run and enter the -class name in the Main Class field. Alternatively, you can manually type the -class name in the manifest Main-Class element. diff --git a/JavaTest/manifest.mf b/JavaTest/manifest.mf deleted file mode 100644 index 328e8e5b..00000000 --- a/JavaTest/manifest.mf +++ /dev/null @@ -1,3 +0,0 @@ -Manifest-Version: 1.0 -X-COMMENT: Main-Class will be added automatically by build - diff --git a/JavaTest/nbproject/build-impl.xml b/JavaTest/nbproject/build-impl.xml deleted file mode 100644 index 21b9380e..00000000 --- a/JavaTest/nbproject/build-impl.xml +++ /dev/null @@ -1,1413 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must set src.dir - Must set test.src.dir - Must set build.dir - Must set dist.dir - Must set build.classes.dir - Must set dist.javadoc.dir - Must set build.test.classes.dir - Must set build.test.results.dir - Must set build.classes.excludes - Must set dist.jar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must set javac.includes - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - No tests executed. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must set JVM to use for profiling in profiler.info.jvm - Must set profiler agent JVM arguments in profiler.info.jvmargs.agent - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must select some files in the IDE or set javac.includes - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - To run this application from the command line without Ant, try: - - java -jar "${dist.jar.resolved}" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must select one file in the IDE or set run.class - - - - Must select one file in the IDE or set run.class - - - - - - - - - - - - - - - - - - - - - - - Must select one file in the IDE or set debug.class - - - - - Must select one file in the IDE or set debug.class - - - - - Must set fix.includes - - - - - - - - - - This target only works when run from inside the NetBeans IDE. - - - - - - - - - Must select one file in the IDE or set profile.class - This target only works when run from inside the NetBeans IDE. - - - - - - - - - This target only works when run from inside the NetBeans IDE. - - - - - - - - - - - - - This target only works when run from inside the NetBeans IDE. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must select one file in the IDE or set run.class - - - - - - Must select some files in the IDE or set test.includes - - - - - Must select one file in the IDE or set run.class - - - - - Must select one file in the IDE or set applet.url - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must select some files in the IDE or set javac.includes - - - - - - - - - - - - - - - - - - - - Some tests failed; see details above. - - - - - - - - - Must select some files in the IDE or set test.includes - - - - Some tests failed; see details above. - - - - Must select some files in the IDE or set test.class - Must select some method in the IDE or set test.method - - - - Some tests failed; see details above. - - - - - Must select one file in the IDE or set test.class - - - - Must select one file in the IDE or set test.class - Must select some method in the IDE or set test.method - - - - - - - - - - - - - - Must select one file in the IDE or set applet.url - - - - - - - - - Must select one file in the IDE or set applet.url - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/JavaTest/nbproject/genfiles.properties b/JavaTest/nbproject/genfiles.properties deleted file mode 100644 index 92b78354..00000000 --- a/JavaTest/nbproject/genfiles.properties +++ /dev/null @@ -1,8 +0,0 @@ -build.xml.data.CRC32=e3ffc9d9 -build.xml.script.CRC32=74429054 -build.xml.stylesheet.CRC32=8064a381@1.75.1.48 -# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. -# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. -nbproject/build-impl.xml.data.CRC32=e3ffc9d9 -nbproject/build-impl.xml.script.CRC32=11fe2361 -nbproject/build-impl.xml.stylesheet.CRC32=876e7a8f@1.75.1.48 diff --git a/JavaTest/nbproject/private/private.properties b/JavaTest/nbproject/private/private.properties deleted file mode 100644 index 9ce0e3cf..00000000 --- a/JavaTest/nbproject/private/private.properties +++ /dev/null @@ -1,2 +0,0 @@ -compile.on.save=true -user.properties.file=C:\\Users\\memory\\AppData\\Roaming\\NetBeans\\8.0.1\\build.properties diff --git a/JavaTest/nbproject/private/private.xml b/JavaTest/nbproject/private/private.xml deleted file mode 100644 index 6807a2ba..00000000 --- a/JavaTest/nbproject/private/private.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/JavaTest/nbproject/project.properties b/JavaTest/nbproject/project.properties deleted file mode 100644 index b2e9ea5a..00000000 --- a/JavaTest/nbproject/project.properties +++ /dev/null @@ -1,73 +0,0 @@ -annotation.processing.enabled=true -annotation.processing.enabled.in.editor=false -annotation.processing.processor.options= -annotation.processing.processors.list= -annotation.processing.run.all.processors=true -annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output -build.classes.dir=${build.dir}/classes -build.classes.excludes=**/*.java,**/*.form -# This directory is removed when the project is cleaned: -build.dir=build -build.generated.dir=${build.dir}/generated -build.generated.sources.dir=${build.dir}/generated-sources -# Only compile against the classpath explicitly listed here: -build.sysclasspath=ignore -build.test.classes.dir=${build.dir}/test/classes -build.test.results.dir=${build.dir}/test/results -# Uncomment to specify the preferred debugger connection transport: -#debug.transport=dt_socket -debug.classpath=\ - ${run.classpath} -debug.test.classpath=\ - ${run.test.classpath} -# \u914d\u7f6ejar\u304b\u3089\u9664\u5916\u3059\u308b\u5fc5\u8981\u304c\u3042\u308bbuild.classes.dir\u5185\u306e\u30d5\u30a1\u30a4\u30eb -dist.archive.excludes= -# This directory is removed when the project is cleaned: -dist.dir=dist -dist.jar=${dist.dir}/JavaTest.jar -dist.javadoc.dir=${dist.dir}/javadoc -excludes= -includes=** -jar.compress=false -javac.classpath= -# Space-separated list of extra javac options -javac.compilerargs= -javac.deprecation=false -javac.processorpath=\ - ${javac.classpath} -javac.source=1.7 -javac.target=1.7 -javac.test.classpath=\ - ${javac.classpath}:\ - ${build.classes.dir} -javac.test.processorpath=\ - ${javac.test.classpath} -javadoc.additionalparam= -javadoc.author=false -javadoc.encoding=${source.encoding} -javadoc.noindex=false -javadoc.nonavbar=false -javadoc.notree=false -javadoc.private=false -javadoc.splitindex=true -javadoc.use=true -javadoc.version=false -javadoc.windowtitle= -main.class=javatest.JavaTest -manifest.file=manifest.mf -meta.inf.dir=${src.dir}/META-INF -mkdist.disabled=false -platform.active=default_platform -run.classpath=\ - ${javac.classpath}:\ - ${build.classes.dir} -# Space-separated list of JVM arguments used when running the project. -# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value. -# To set system properties for unit tests define test-sys-prop.name=value: -run.jvmargs= -run.test.classpath=\ - ${javac.test.classpath}:\ - ${build.test.classes.dir} -source.encoding=UTF-8 -src.dir=src -test.src.dir=test diff --git a/JavaTest/nbproject/project.xml b/JavaTest/nbproject/project.xml deleted file mode 100644 index 6a319e33..00000000 --- a/JavaTest/nbproject/project.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - org.netbeans.modules.java.j2seproject - - - JavaTest - - - - - - - - - diff --git a/JavaTest/src/javatest/JavaTest.java b/JavaTest/src/javatest/JavaTest.java deleted file mode 100644 index 767ede0c..00000000 --- a/JavaTest/src/javatest/JavaTest.java +++ /dev/null @@ -1,162 +0,0 @@ - -package javatest; - -public class JavaTest { - - long z = -22222222222222222L; - static int c = 100; - static String b = "Hello World"; - - public static void main (String[] args) { - - String x = "String"; - - // new instance - JavaTest _a = new JavaTest(); - - // test call - _a.javaTest(); - - - int t = 2; - //t *= 1; - //t = ~1; - t <<= 1; - System.out.println(t); - t >>= 1; - System.out.println(t); - t -= 1; - System.out.println(t); - t += 1; - System.out.println(t); - t = 1; - System.out.println(t); - t >>>= 1; - System.out.println(t); - t |= 1; - System.out.println(t); - t &= 1; - System.out.println(t); - t = 345321; - System.out.println((short) t); -/* - long t1 = 111; - t1 *= 1; - t1 = ~1; - t1 <<= 1; - t1 >>= 1; - t1 -= 1; - t1 += 1; - t1 = 1; - t1 >>>= 1; - t1 |= 1; - t1 &= 1; - - double t2 = 111; - t2 *= 1; - t2 = ~1; - t2 -= 1; - t2 += 1; - t2 = 1; - - boolean _b = false; - _b = true && true; - _b = true && false; - _b = true || true; - _b = true || false;*/ - - try { - - for (int i = 0; i < JavaTest.c; i++) { - - StringBuilder b = new StringBuilder(); - - switch (i + 1) { - - case -1: - - b.append("a"); - - break; - case 1: - - b.append("b"); - - break; - case 2: - - b.append("c"); - - break; - - } - - if (!x.equals(i + "")) { - - - System.out.println("Test:" + JavaTest.b + "/" + x + "*****" + i + "/" + b); - - } - - if (i == 10) { - - throw new NullPointerException(); - - } - - } - - } catch (NullPointerException e) { - - System.out.println("ぬるぷっぷー"); - - } - - } - - public static String test (int n, String m, int l, int i, int v, int k) { - - int j = 1; - - for (; j <= 10; j++) { - - j++; - - } - - return "Java emulate by php " + n + "/" + m + "/" + l + "/" + i + "/" + v + "/" + k + "/" + j; - - } - - public void javaTest () { - - testClass _c = new testClass(); - _c.t(); - - } - - public class testClass { - - public void t () { - - System.out.println("testClass.t method." + JavaTest.this.z); - - testClass2 _c = new testClass2(); - _c.t(); - - - } - - public class testClass2 { - - public void t () { - - System.out.println("testClass2.t method." + JavaTest.this.z); - - } - - } - - } - -} \ No newline at end of file diff --git a/PHPJava b/PHPJava new file mode 100755 index 00000000..36e35079 --- /dev/null +++ b/PHPJava @@ -0,0 +1,11 @@ +#!/usr/bin/php +AttributeNameIndex = $this->Class->getJavaBinaryStream()->readUnsignedShort(); - $this->AttributeLength = $this->Class->getJavaBinaryStream()->readUnsignedInt(); - - $cpInfo = $this->Class->getCpInfo(); - - $classAttributeName = 'Java' . $cpInfo[$this->AttributeNameIndex]->getString() . 'Attribute'; - - $this->AttributeData = new $classAttributeName($Class); - - - } - - public function getAttributeData () { - - return $this->AttributeData; - - } - - public function getAttributeNameIndex () { - - return $this->AttributeNameIndex; - - } - - public function getAttributeLength () { - - return $this->AttributeLength; - - } - -} \ No newline at end of file diff --git a/PHPJava/Attributes/JavaBootstrapMethodsAttribute.php b/PHPJava/Attributes/JavaBootstrapMethodsAttribute.php deleted file mode 100644 index e745a0c8..00000000 --- a/PHPJava/Attributes/JavaBootstrapMethodsAttribute.php +++ /dev/null @@ -1,13 +0,0 @@ -MaxStack = $this->Class->getJavaBinaryStream()->readUnsignedShort(); - $this->MaxLocals = $this->Class->getJavaBinaryStream()->readUnsignedShort(); - $this->CodeLength = $this->Class->getJavaBinaryStream()->readUnsignedInt(); - - // read opcode - $this->Code = array(); - - for ($i = 0; $i < $this->CodeLength; $i++) { - - $this->Code[$i] = $this->Class->getJavaBinaryStream()->readUnsignedByte(); - $this->RawCode .= chr($this->Code[$i]); - - } - - // read exception table - $this->ExceptionTableLength = $this->Class->getJavaBinaryStream()->readUnsignedShort(); - - for ($i = 0; $i < $this->ExceptionTableLength; $i++) { - - $this->ExceptionTables[$i] = new JavaStructureExceptionTable($this->Class); - - $this->ExceptionTables[$i]->setStartPc($this->Class->getJavaBinaryStream()->readUnsignedShort()); - $this->ExceptionTables[$i]->setEndPc($this->Class->getJavaBinaryStream()->readUnsignedShort()); - $this->ExceptionTables[$i]->setHandlerPc($this->Class->getJavaBinaryStream()->readUnsignedShort()); - $this->ExceptionTables[$i]->setCatchType($this->Class->getJavaBinaryStream()->readUnsignedShort()); - - } - - $this->AttributesCount = $this->Class->getJavaBinaryStream()->readUnsignedShort(); - - for ($i = 0; $i < $this->AttributesCount; $i++) { - - $this->AttributeInfo[] = new JavaAttributeInfo($this->Class); - - } - - } - - public function getExceptionTables () { - - return $this->ExceptionTables; - - } - - public function getCode () { - - return $this->RawCode; - - } - - public function getOpCodes () { - - return $this->Code; - - } - - public function getOpCodeLength () { - - return $this->CodeLength; - - } - -} \ No newline at end of file diff --git a/PHPJava/Attributes/JavaConstantValueAttribute.php b/PHPJava/Attributes/JavaConstantValueAttribute.php deleted file mode 100644 index 3156370d..00000000 --- a/PHPJava/Attributes/JavaConstantValueAttribute.php +++ /dev/null @@ -1,13 +0,0 @@ -NumberOfClasses = $this->Class->getJavaBinaryStream()->readUnsignedShort(); - - for ($i = 0; $i < $this->NumberOfClasses; $i++) { - - $this->Classes[$i] = new JavaStructureClasses($this->Class); - - $this->Classes[$i]->setInnerClassInfoIndex($this->Class->getJavaBinaryStream()->readUnsignedShort()); - $this->Classes[$i]->setOuterClassInfoIndex($this->Class->getJavaBinaryStream()->readUnsignedShort()); - $this->Classes[$i]->setInnerNameIndex($this->Class->getJavaBinaryStream()->readUnsignedShort()); - $this->Classes[$i]->setInnerClassAccessFlag($this->Class->getJavaBinaryStream()->readUnsignedShort()); - - } - - } - - public function getClasses () { - - return $this->Classes; - - } - -} \ No newline at end of file diff --git a/PHPJava/Attributes/JavaLineNumberTableAttribute.php b/PHPJava/Attributes/JavaLineNumberTableAttribute.php deleted file mode 100644 index 6de564f4..00000000 --- a/PHPJava/Attributes/JavaLineNumberTableAttribute.php +++ /dev/null @@ -1,32 +0,0 @@ -LineNumberTableLength = $this->Class->getJavaBinaryStream()->readUnsignedShort(); - - for ($i = 0; $i < $this->LineNumberTableLength; $i++) { - - $this->LineNumberTables[$i] = new JavaStructureLineNumberTable($Class); - - $this->LineNumberTables[$i]->setStartPc($this->Class->getJavaBinaryStream()->readUnsignedShort()); - $this->LineNumberTables[$i]->setLineNumber($this->Class->getJavaBinaryStream()->readUnsignedShort()); - - } - - } - - public function getLineNumberTables () { - - return $this->LineNumberTables; - - } - - -} \ No newline at end of file diff --git a/PHPJava/Attributes/JavaLocalVariableTableAttribute.php b/PHPJava/Attributes/JavaLocalVariableTableAttribute.php deleted file mode 100644 index 8470175e..00000000 --- a/PHPJava/Attributes/JavaLocalVariableTableAttribute.php +++ /dev/null @@ -1,22 +0,0 @@ -LocalVariableTableLength = $this->Class->getJavaBinaryStream()->readUnsignedShort(); - - for ($i = 0; $i < $this->LocalVariableTableLength; $i++) { - - $this->LocalVariableTables[] = new JavaStructureLocalVariableTable($Class); - - } - - } - -} \ No newline at end of file diff --git a/PHPJava/Attributes/JavaLocalVariableTypeTableAttribute.php b/PHPJava/Attributes/JavaLocalVariableTypeTableAttribute.php deleted file mode 100644 index 94780ca3..00000000 --- a/PHPJava/Attributes/JavaLocalVariableTypeTableAttribute.php +++ /dev/null @@ -1,13 +0,0 @@ -SourceFileIndex = $this->Class->getJavaBinaryStream()->readUnsignedShort(); - - - } - - public function getSourceFileIndex () { - - return $this->SourceFileIndex; - - } - - -} \ No newline at end of file diff --git a/PHPJava/Attributes/JavaStackMapTableAttribute.php b/PHPJava/Attributes/JavaStackMapTableAttribute.php deleted file mode 100644 index 96c91210..00000000 --- a/PHPJava/Attributes/JavaStackMapTableAttribute.php +++ /dev/null @@ -1,31 +0,0 @@ -NumberOfEntries = $this->Class->getJavaBinaryStream()->readUnsignedShort(); - - for ($i = 0; $i < $this->NumberOfEntries; $i++) { - - $this->StackMapFrames[] = new JavaStructureStackMapFrame($Class); - - - } - - } - - public function getStackMapFrames () { - - return $this->StackMapFrames; - - } - -} - \ No newline at end of file diff --git a/PHPJava/Attributes/JavaSyntheticAttribute.php b/PHPJava/Attributes/JavaSyntheticAttribute.php deleted file mode 100644 index 571682b1..00000000 --- a/PHPJava/Attributes/JavaSyntheticAttribute.php +++ /dev/null @@ -1,13 +0,0 @@ -Class = &$Class; - - } - - public function getClass () { - - return $this->Class; - - } - -} \ No newline at end of file diff --git a/PHPJava/Common/JavaBinaryStream.php b/PHPJava/Common/JavaBinaryStream.php deleted file mode 100644 index 49e65843..00000000 --- a/PHPJava/Common/JavaBinaryStream.php +++ /dev/null @@ -1,102 +0,0 @@ -Handle = &$handle; - - } - - - public final function read ($bytes = 1) { - - $this->Offset += $bytes; - - return fread($this->Handle, $bytes); - - } - - public function readByte () { - - return current(unpack('c', $this->read(1))); - - } - - public function readUnsignedByte () { - - return (int) sprintf('%u', ord($this->read(1))); - - } - - public function readUnsignedInt () { - - return base_convert(bin2hex($this->read(4)), 16, 10); - - } - - public function readUnsignedShort () { - - return (int) sprintf('%u', hexdec(bin2hex($this->read(2)))); - - } - - public function readInt () { - - return BinaryTools::toSigned($this->readUnsignedInt(), 4); - - } - - public function readShort () { - - $short = (int) sprintf('%u', hexdec(bin2hex($this->read(2)))); - - return (($short & 0x8000) > 0) ? ($short - 0xFFFF - 1) : $short ; - - } - - public function readUnsignedLong () { - - if (PHP_INT_MAX === 2147483647) { - - return base_convert(bin2hex($this->read(8)), 16, 10); - - } - - return (int) sprintf('%u', hexdec(bin2hex($this->read(8)))); - - } - - public function readLong () { - - return BinaryTools::toSigned($this->readUnsignedLong(), 8); - - } - - public function seek ($bytes) { - - $this->Offset += $bytes; - - fseek($this->Handle, $bytes, SEEK_CUR); - - } - - public function setOffset ($pointer) { - - $this->Offset = $pointer; - - fseek($this->Handle, $pointer, SEEK_SET); - - } - - public function getOffset () { - - return $this->Offset; - - } - - -} \ No newline at end of file diff --git a/PHPJava/Common/JavaEnum.php b/PHPJava/Common/JavaEnum.php deleted file mode 100644 index 93714047..00000000 --- a/PHPJava/Common/JavaEnum.php +++ /dev/null @@ -1,36 +0,0 @@ -getConstants(); - - $keys = array_keys($constants); - $values = array_values($constants); - - foreach ($values as $i => $constantValue) { - - if ($value === $constantValue) { - - return $keys[$i]; - - } - - } - - return null; - - } - - public function getValues () { - - $reflectionClass = new ReflectionClass($this); - - return array_values($reflectionClass->getConstants()); - - } - -} diff --git a/PHPJava/Common/JavaInvoker.php b/PHPJava/Common/JavaInvoker.php deleted file mode 100644 index 49c89e96..00000000 --- a/PHPJava/Common/JavaInvoker.php +++ /dev/null @@ -1,58 +0,0 @@ -Class = &$Class; - $this->Platform = __DIR__ . '/../Platform'; - - } - - public function getClass () { - - return $this->Class; - - } - - public function loadInnerClass ($class) { - - if ($this->getClass()->getClassFile() . '/../' . $class . '.class') { - - $this->Loaded[] = str_replace('.', '/', $class); - - - } - - } - - public function loadPlatform ($class) { - - $className = str_replace('.', '/', $class); - - if (is_file($this->Platform . '/' . $className . '.php')) { - - if (in_array($className, $this->Loaded)) { - - return; - } - - require_once $this->Platform . '/' . $className . '.php'; - - $this->Loaded[] = str_replace('.', '/', $class); - - } else { - - throw new JavaPlatformHasNotClass($class); - - } - - } - -} \ No newline at end of file diff --git a/PHPJava/Common/JavaStatement.php b/PHPJava/Common/JavaStatement.php deleted file mode 100644 index ef4215c1..00000000 --- a/PHPJava/Common/JavaStatement.php +++ /dev/null @@ -1,126 +0,0 @@ -MethodName = $methodName; - $this->Invoker = &$invoker; - $this->ByteCodeStream = &$byteCodeStream; - $this->Stacks = &$stacks; - $this->Localstorage = &$localstorage; - $this->CpInfo = &$CpInfo; - $this->AttributeData = &$attributeData; - $this->Pointer = $pointer; - - } - - public function pushStack ($value) { - - $this->Stacks[] = $value; - - } - - public function pushStackByReference (&$value) { - - $this->Stacks[] = &$value; - - } - - public function dupStack () { - - $this->pushStack($this->Stacks[sizeof($this->Stacks) - 1]); - - } - - public function getStack () { - - return array_pop($this->Stacks); - - } - - public function popStack () { - - array_pop($this->Stacks); - - } - - public function getStacks () { - - return $this->Stacks; - - } - - public function getOperands () { - - return $this->Operands; - - } - - public function setLocalstorage ($index, $value) { - - $this->Localstorage[(int) $index] = $value; - - } - - - public function getLocalstorage ($index) { - - return $this->Localstorage[(int) $index]; - - } - - public function getLocalstorages () { - - return $this->Localstorage; - - } - - public function getByteCodeStream () { - - return $this->ByteCodeStream; - - } - - public function getPointer () { - - return $this->Pointer; - - } - - public function getCpInfo () { - - return $this->CpInfo; - - } - - public function getInvoker () { - - return $this->Invoker; - - } - - public function getMethodName () { - - return $this->MethodName; - - } - - public function getAttributeData () { - - return $this->AttributeData; - - } - -} \ No newline at end of file diff --git a/PHPJava/Common/JavaStructure.php b/PHPJava/Common/JavaStructure.php deleted file mode 100644 index 4e6f750b..00000000 --- a/PHPJava/Common/JavaStructure.php +++ /dev/null @@ -1,19 +0,0 @@ -Class = &$Class; - - } - - public function getClass () { - - return $this->Class; - - } - -} \ No newline at end of file diff --git a/PHPJava/Common/JavaType.php b/PHPJava/Common/JavaType.php deleted file mode 100644 index 2e9484fc..00000000 --- a/PHPJava/Common/JavaType.php +++ /dev/null @@ -1,38 +0,0 @@ -value = $value; - } - - /** - * 設定されている値を返します。 - * - * @return mixed 設定されている値を返します - */ - public function getValue () { - return $this->value; - } - - /** - * 値を出力します。 - * - * @return string - */ - public function __toString() { - return (string) $this->getValue(); - } - -} diff --git a/PHPJava/Core/JavaArchive.php b/PHPJava/Core/JavaArchive.php deleted file mode 100644 index c71f9fe1..00000000 --- a/PHPJava/Core/JavaArchive.php +++ /dev/null @@ -1,123 +0,0 @@ -open($file)) { - - for ($i = 0; $i < $archive->numFiles; $i++) { - - $name = $archive->getNameIndex($i); - - if ($name[strlen($name) - 1] !== '/') { - - $this->Files[$name] = $archive->getFromIndex($i); - - } - - } - - } - - foreach (glob(__DIR__ . '/../{Common,Exceptions,Enums,Stream,Invoker,Attributes,Structures,Utils}/*.php', GLOB_BRACE) as $file) { - - require_once($file); - - } - - $this->Manipulator = new JavaManipulator(); - - - foreach ($this->Files as $fileName => $data) { - - if (!preg_match('/\.class$/', $fileName)) { - - // load class only - - continue; - - } - - if (strpos($fileName, '$') === false) { - - $this->Manipulator->registerClass(new JavaClass($fileName, $data), $this); - - } - - } - - } - - public function getClassBytecode ($name) { - - $className = str_replace('.', '/', $name); - $name = $className . '.class'; - - // search - foreach ($this->Files as $fileName => $data) { - - if ($name === $fileName) { - - return $data; - - } - - } - - throw new JavaArchiveException('Not found class'); - - } - - public function getClass ($name) { - - $className = str_replace('.', '/', $name); - $name = $className . '.class'; - - // search - foreach ($this->Files as $fileName => $data) { - - if ($name === $fileName) { - - return $this->Manipulator->$className->getClass()->getMethodInvoker(); - - } - - } - - throw new JavaArchiveException('Not found class'); - - } - - public function hasClass ($name) { - - $className = str_replace('.', '/', $name); - $name = $className . '.class'; - - // search - foreach ($this->Files as $fileName => $data) { - - if ($name === $fileName) { - - return true; - - } - - } - - return false; - - } - - public function getFiles () { - - return $this->Files; - - } - -} \ No newline at end of file diff --git a/PHPJava/Core/JavaClass.php b/PHPJava/Core/JavaClass.php deleted file mode 100644 index fcf0b9a2..00000000 --- a/PHPJava/Core/JavaClass.php +++ /dev/null @@ -1,785 +0,0 @@ - null, - 'Instances' => null - ); - - /** - * @var int 読み込まれているメソッドの数を格納します。 - */ - private $MethodCount = 0; - - /** - * @var JavaStructureMethodInfo[] メソッドの情報を格納したオブジェクトを格納します。 - */ - private $Methods = array(); - - /** - * @var int Constant Poolの数を格納します。 - */ - private $cpPool = 0; - - /** - * @var object Constant Poolの情報を格納したオブジェクトを格納します。 - */ - private $cpInfo = array(); - - /** - * @var int アトリビュートの数を格納します。 - */ - private $AttributesCount = 0; - - /** - * @var JavaAttributeInfo[] アトリビュートの情報を格納したオブジェクトを格納します。 - */ - private $AttributeInfo = array(); - - /** - * @var JavaMethodInvoker Javaのメソッドを呼ぶためのオブジェクトを格納します。 - */ - private $MethodInvoker = null; - - /** - * @var string ニーモニックを読み込んだログを書き込みます。 - */ - private $Trace = ''; - - /** - * @var string ニーモニックを読み込んだログを書き込みます。 - */ - private $TraceHeader = ''; - - /** - * @var string ニーモニックを読み込んだログを書き込みます。 - */ - private $TraceBuffering = ''; - - /** - * @var JavaManipulator このクラスを管理しているJARの情報を格納します。 - */ - private $Manipulator = null; - - /** - * @var JavaBinaryStream Javaのバイトコード読んでいくためのクラスを格納します。 - */ - protected $JavaBinaryStream = null; - - /** - * JavaClassを読み込みます。 - * - * @param string $file ファイル名を指定します。 - * @param string|null $byteCode Javaのバイトコードを渡します。nullの場合$fileから読み込みます。 - * @return void - */ - public function __construct ($file, $byteCode = null) { - - $this->ClassFile = $file; - - foreach (glob(__DIR__ . '/../{Common,Exceptions,Enums,Stream,Invoker,Attributes,Structures,Types,Utils}/*.php', GLOB_BRACE) as $loadFile) { - - require_once($loadFile); - - } - - if ($byteCode === null) { - - $this->Handle = fopen($file, 'r'); - - } else { - - $this->Handle = fopen('php://memory', 'rw'); - fwrite($this->Handle, $byteCode); - rewind($this->Handle); - - } - - $this->ClassFields = (object) $this->ClassFields; - $this->ClassFields->Statics = (object) $this->ClassFields->Statics; - $this->ClassFields->Instances = (object) $this->ClassFields->Instances; - - $this->JavaBinaryStream = new JavaBinaryStream($this->Handle); - - // read magic byte - if ($this->MagicBytes !== BinaryTools::toHex($this->JavaBinaryStream->readUnsignedInt())) { - - throw new JavaClassException($this->ClassFile . ' is not java class'); - - } - - // read minor version - $this->MinorVersion = $this->JavaBinaryStream->readUnsignedShort(); - - // read major version - $this->MajorVersion = $this->JavaBinaryStream->readUnsignedShort(); - - // read cp_pool - $this->cpPool = $this->JavaBinaryStream->readUnsignedShort(); - - for ($entry = 1; $entry < $this->cpPool; $entry++) { - - $this->cpInfo[$entry] = $this->__CpInfo($entry); - - if ($this->cpInfo[$entry] instanceof \JavaStructureLong || - $this->cpInfo[$entry] instanceof \JavaStructureDouble) { - - // Java ConstantPool Problem - $entry++; - - } - - } - - // read access flag - $this->AccessFlag = $this->JavaBinaryStream->readUnsignedShort(); - - // read this class - $this->ThisClass = $this->JavaBinaryStream->readUnsignedShort(); - - // read super class - $this->SuperClass = $this->JavaBinaryStream->readUnsignedShort(); - - // read interfaces - $this->InterfaceCount = $this->JavaBinaryStream->readUnsignedShort(); - - for ($i = 0; $i < $this->InterfaceCount; $i++) { - - $this->Interfaces[] = $this->JavaBinaryStream->readUnsignedShort(); - - } - - // read fields - $this->FieldCount = $this->JavaBinaryStream->readUnsignedShort(); - - for ($i = 0; $i < $this->FieldCount; $i++) { - - $this->Fields[] = $this->__Fields(); - - } - - // read methods - $this->MethodCount = $this->JavaBinaryStream->readUnsignedShort(); - - for ($i = 0; $i < $this->MethodCount; $i++) { - - $this->Methods[] = $this->__Method(); - - } - - $this->AttributesCount = $this->JavaBinaryStream->readUnsignedShort(); - - for ($i = 0; $i < $this->AttributesCount; $i++) { - - $this->AttributeInfo[] = new JavaAttributeInfo($this); - - } - - $this->MethodInvoker = new JavaMethodInvoker($this); - - // find clinit - foreach ($this->getMethods() as $method) { - - if ($this->cpInfo[$method->getNameIndex()]->getString() === '') { - - // call clinit - call_user_func_array(array( - $this->MethodInvoker, - '' - ), array()); - - } - - } - - } - - /** - * Constant Poolの情報を取得します。 - */ - private function __CpInfo () { - - // read tag - switch ($this->JavaBinaryStream->readUnsignedByte()) { - - case JavaClassConstantEnum::CONSTANT_Class: - - return new JavaStructureClass($this); - - break; - case JavaClassConstantEnum::CONSTANT_Fieldref: - - return new JavaStructureFieldref($this); - - break; - case JavaClassConstantEnum::CONSTANT_Methodref: - - return new JavaStructureMethodref($this); - - break; - case JavaClassConstantEnum::CONSTANT_InterfaceMethodref: - - return new JavaStructureInterfaceMethodref($this); - - break; - case JavaClassConstantEnum::CONSTANT_String: - - return new JavaStructureString($this); - - break; - case JavaClassConstantEnum::CONSTANT_Integer: - - return new JavaStructureInteger($this); - - break; - case JavaClassConstantEnum::CONSTANT_Float: - - return new JavaStructureFloat($this); - - break; - case JavaClassConstantEnum::CONSTANT_Long: - - return new JavaStructureLong($this); - - break; - case JavaClassConstantEnum::CONSTANT_Double: - - - return new JavaStructureDouble($this); - - break; - case JavaClassConstantEnum::CONSTANT_NameAndType: - - return new JavaStructureNameAndType($this); - - break; - case JavaClassConstantEnum::CONSTANT_Utf8: - - return new JavaStructureUtf8($this); - - break; - - } - - } - - /** - * フィールドの情報を取得します。 - * @return JavaStructureFieldInfo - */ - private function __Fields () { - - return new JavaStructureFieldInfo($this); - - } - - /** - * メソッドの情報を取得します。 - * @return JavaStructureMethodInfo - */ - private function __Method () { - - return new JavaStructureMethodInfo($this); - - } - - /** - * 読み込まれているファイルのハンドルを取得します。 - * @return resource - */ - public function getHandle () { - - return $this->Handle; - - } - - /** - * Constant Poolの情報を取得します。 - * @return object - */ - public function getCpInfo () { - - return $this->cpInfo; - - } - - /** - * メソッドの情報を取得します。 - * @return JavaStructureMethodInfo[] - */ - public function getMethods () { - - return $this->Methods; - - } - - /** - * メソッドの情報を取得します。 - * @return JavaStructureFieldInfo[] - */ - public function getFields () { - - return $this->Fields; - - } - - /** - * Javaのメソッドを呼び出すオブジェクトを取得します。 - * @return JavaMethodInvoker - */ - public function getMethodInvoker () { - - return $this->MethodInvoker; - - } - - /** - * Javaの引数やクラス定義におけるシグネチャを取得します。 - * @return array - */ - public static function parseSignature ($signature) { - - return self::_parseSignature($signature); - - } - - /** - * Javaの引数やクラス定義におけるシグネチャを取得します。 - * @return array - */ - private static function _parseSignature ($signature, $i = 0) { - - $data = array(); - $deepArray = 0; - - for ($size = strlen($signature); $i < $size; ) { - - switch ($signature[$i]) { - - case 'B': - case 'C': - case 'D': - case 'F': - case 'I': - case 'J': - case 'S': - case 'V': - case 'Z': - - $data[] = array( - - 'type' => self::_getSignatureType($signature[$i]), - 'deepArray' => $deepArray - - ); - - $deepArray = 0; - - - - break; - case 'L': - - // class name - $build = ''; - - // read to ; - for ($i++; $i < $size && $signature[$i] !== ';'; $i++) { - - $build .= $signature[$i]; - - } - - $data[] = array( - - 'type' => 'class', - 'deepArray' => $deepArray, - 'className' => $build - - ); - - $deepArray = 0; - - break; - case '[': - - // array - $deepArray++; - - for ($i++; $signature[$i] === '['; $i++) { - - $deepArray++; - - } - - // loop - continue 2; - - break; - case '(': - - $build = ''; - - // read to ) - for ($i++; $i < $size && $signature[$i] !== ')'; $i++) { - - $build .= $signature[$i]; - - } - - $data['arguments'] = ($build !== '') ? self::_parseSignature($build) : array(); - $data['argumentsCount'] = sizeof($data['arguments']); - - break; - - } - - $i++; - - } - - return $data; - - } - - /** - * シグネチャにおける型付を取得します。 - * @return string - */ - public static function _getSignatureType ($signature) { - - switch ($signature) { - - case 'B': return 'byte'; - case 'C': return 'char'; - case 'D': return 'double'; - case 'F': return 'float'; - case 'I': return 'int'; - case 'J': return 'long'; - case 'S': return 'short'; - case 'V': return 'void'; - case 'Z': return 'boolean'; - - } - - return 'Undefined'; - - } - - /** - * メソッド実行時のトレース情報を格納します。 - * - * @param string $methodName 実行されたメソッド名を指定します。 - * @param string $accessibility メソッドにおけるアクセス修飾子を指定します。 - * @param array $signature パースされたシグネチャの情報を指定します。 - * @return void - */ - public function appendMethodTrace ($methodName, $accessibility, $signature) { - - $arguments = array(); - - foreach ($signature['arguments'] as $argument) { - - $arguments[] = str_replace('/', '.', ($argument['type'] === 'class' ? $argument['className'] : $argument['type'])) . str_repeat('[]', $argument['deepArray']); - - } - - $this->TraceHeader .= implode(' ', $accessibility) . ' ' . str_replace('/', '.', ($signature[0]['type'] === 'class' ? $signature[0]['className'] : $signature[0]['type'])) . ' ' . $methodName . ' (' . implode(', ', $arguments) . ')' . "\n"; - - } - - /** - * 実行されたバイトコードのトレースをします。 - * - * @param int $opcode オペレーションコードを指定します。 - * @param int $programCounter オペレーションコードがどこまで実行されたか指定します。 - * @param array $stacks 現状のスタックの状況を指定します。 - * @param array $operands 渡されているオペランドの情報を指定します。 - * @return void - */ - public function appendTrace ($opcode, $programCounter, $stacks, $operands) { - - $mnemonic = new JavaMnemonicEnum(); - - $mnemonicName = preg_replace('/^MNEMONIC_/', '', $mnemonic->getName($opcode)); - - foreach ($operands as $key => $operand) { - - if (is_object($operands[$key])) { - - $operands[$key] = '#' . get_class($operands[$key]); - - } - - } - - $this->Trace .= '#' . $programCounter . "\t" . sprintf('0x%02X', $opcode) . "\t" . $mnemonicName . (strlen($mnemonicName) < 8 ? "\t" : '') . "\t" . sizeof($stacks) . "\t" . implode("\t", $operands) . "\n"; - - } - - /** - * トレースのヘッダー情報を定義します。 - * - * @return string - */ - private function traceHeader () { - - $trace = ''; - - $trace .= $this->TraceHeader; - $trace .= "PC\tOPCODE\tMNEMONIC\tSTACKS\tOPERAND(s)\n"; - $trace .= "----------------------------------------------------------------\n"; - return $trace; - - } - - /** - * トレース情報を別の変数に移し替えます。 - * @return void - */ - public function traceCompletion () { - - $trace = $this->traceHeader(); - $this->TraceBuffering[] = $trace . $this->Trace; - - $this->TraceHeader = ''; - $this->Trace = ''; - - - } - - /** - * 現時点までに実行されたオペコードのトレース情報を出力します。 - * - * @return string - */ - public function traceDump () { - echo $this->traceHeader(); - echo $this->Trace; - } - - /** - * トレース情報を出力します。 - * - * @return void - */ - public function trace () { - - echo implode("\n", $this->TraceBuffering); - - } - - /** - * Javaのバイトコードを読み取っているストリームを返します。 - * - * @return JavaBinaryStream - */ - public function getJavaBinaryStream () { - - return $this->JavaBinaryStream; - - } - - /** - * 読み込んでいる対象のクラスの情報を返します。 - * - * @return object - */ - public function getThisClass () { - - return $this->cpInfo[$this->ThisClass]; - - } - - /** - * 読み込んでいる対象の親クラスの情報を返します。 - * - * @return object - */ - public function getSuperClass () { - - return $this->cpInfo[$this->SuperClass]; - - } - - /** - * 管理しているjarファイルを定義します。 - * - * @return void - */ - public function setManipulator (JavaManipulator &$manipulator) { - - $this->Manipulator = $manipulator; - - } - - /** - * 管理しているjarファイルを返します - * - * @return JavaManipulator - */ - public function getManipulator () { - - return $this->Manipulator; - - } - - /** - * アトリビュートの情報を返します - * - * @return JavaAttributeInfo[] - */ - public function getAttributeInfo () { - - return $this->AttributeInfo; - - } - - /** - * 読み込んでいるクラスファイルの情報を返します - * - * @return string - */ - public function getClassFile () { - - return $this->ClassFile; - - } - - /** - * このクラスにおける静的なメンバを定義します。 - * - * @return void - */ - public function setStatic ($key, $value) { - - $this->ClassFields->Statics->{$key} = $value; - - } - - /** - * このクラスにおける動的なメンバを定義します。 - * - * @return void - */ - public function setInstance ($key, $value) { - - $this->ClassFields->Instances->{$key} = $value; - - } - - /** - * このクラスにおける静的なメンバを返します - * - * @param mixed $key Javaで定義されている変数名を指定 - * @return mixed - */ - public function getStatic ($key) { - - return isset($this->ClassFields->Statics->{$key}) ? $this->ClassFields->Statics->{$key} : null; - - } - - /** - * このクラスにおける動的なメンバを返します - * - * @param mixed $key Javaで定義されている変数名を指定 - * @return mixed - */ - public function getInstance ($key) { - - return isset($this->ClassFields->Instances->{$key}) ? $this->ClassFields->Instances->{$key} : null; - - } - - /** - * Javaのコンストラクタを呼びます。 - * - * @param mixed $... コンストラクタに渡す引数を指定します。 - * @return JavaMethodInvoker - */ - public function construct () { - $instance = new JavaMethodInvoker($this, true); - - $args = func_get_args(); - - // find init - foreach ($this->getMethods() as $method) { - - if ($this->cpInfo[$method->getNameIndex()]->getString() === '') { - - // call init - call_user_func_array(array( - $instance, - '' - ), $args); - break; - - } - - } - - return $instance; - - } - -} \ No newline at end of file diff --git a/PHPJava/Core/JavaDecompile.php b/PHPJava/Core/JavaDecompile.php deleted file mode 100644 index 413c3266..00000000 --- a/PHPJava/Core/JavaDecompile.php +++ /dev/null @@ -1,7 +0,0 @@ -Classes[] = new JavaManipulatorInfo($class, $archive); - - $class->setManipulator($this); - - return $class->getMethodInvoker(); - - } - - public function __get ($className) { - - foreach ($this->Classes as &$class) { - - $cpInfo = $class->getClass()->getCpInfo(); - - if ($cpInfo[$class->getClass()->getThisClass()->getClassIndex()]->getString() === $className) { - - return $class; - - } - - } - - throw new JavaManipulatorException('Not found class'); - - } - - public function getRegisteredClasses () { - - return sizeof($this->Classses); - - } - - public function getRegisteredArchives () { - - return sizeof($this->Archives); - - } - -} \ No newline at end of file diff --git a/PHPJava/Core/JavaManipulatorInfo.php b/PHPJava/Core/JavaManipulatorInfo.php deleted file mode 100644 index be05602c..00000000 --- a/PHPJava/Core/JavaManipulatorInfo.php +++ /dev/null @@ -1,27 +0,0 @@ -Class = $class; - $this->Archive = $archive; - - } - - public function getClass () { - - return $this->Class; - - } - - public function getArchive () { - - return $this->Archive; - - } - -} \ No newline at end of file diff --git a/PHPJava/Enums/JavaAccessFlagEnum.php b/PHPJava/Enums/JavaAccessFlagEnum.php deleted file mode 100644 index 42594b1f..00000000 --- a/PHPJava/Enums/JavaAccessFlagEnum.php +++ /dev/null @@ -1,14 +0,0 @@ -_constructed = $constructed; - } - - /** - * Javaのメンバをコールします。 - * - * @param $fieldName フィールド名 - * @return mixed - */ - public function __get ($fieldName) { - - $cpInfo = $this->getClass()->getCpInfo(); - - foreach ($this->getClass()->getFields() as $fieldInfo) { - - $cpFieldName = $cpInfo[$fieldInfo->getNameIndex()]->getString(); - - if ($fieldName === $cpFieldName) { - - $accessibility = $this->_getAccessiblity($fieldInfo); - $fieldSignature = JavaClass::parseSignature($cpInfo[$fieldInfo->getDescriptorIndex()]->getString()); - - // 静的メンバの場合 - if (in_array('static', $accessibility)) { - return $this->getClass()->getStatic($fieldName); - } - $type = 'JavaType' . ucfirst($fieldSignature[0]['type']); - return new $type($this->getClass()->getInstance($fieldName)); - } - } - - throw new JavaInvokerException('undefined field "' . $fieldName . '"'); - - } - - /** - * Javaのメンバの値を設定します。 - * - * @param string $fieldName フィールド名 - * @param mixed $value 書き換える値 - * @return mixed - */ - public function __set($fieldName, $value) { - - $cpInfo = $this->getClass()->getCpInfo(); - - foreach ($this->getClass()->getFields() as $fieldInfo) { - - $cpFieldName = $cpInfo[$fieldInfo->getNameIndex()]->getString(); - - if ($fieldName === $cpFieldName) { - - $accessibility = $this->_getAccessiblity($fieldInfo); - $fieldSignature = JavaClass::parseSignature($cpInfo[$fieldInfo->getDescriptorIndex()]->getString()); - - // 静的メンバの場合 - if (in_array('static', $accessibility)) { - $this->getClass()->setStatic($fieldName, $value); - return; - } - $this->getClass()->setInstance($fieldName, $value); - return; - } - } - - throw new JavaInvokerException('undefined field "' . $fieldName . '"'); - } - - /** - * Javaのメソッドをエミュレートします。 - * - * @param $fieldName フィールド名 - * @return mixed - */ - public function __call ($methodName, $arguments) { - - $cpInfo = $this->getClass()->getCpInfo(); - - foreach ($this->getClass()->getMethods() as $methodInfo) { - - $cpMethodName = $cpInfo[$methodInfo->getNameIndex()]->getString(); - - if ($methodName === $cpMethodName) { - - $accessibility = $this->_getAccessiblity($methodInfo); - - // メソッドのシグネチャを取得する - $javaArguments = JavaClass::parseSignature($cpInfo[$methodInfo->getDescriptorIndex()]->getString()); - - $argumentsCount = sizeof($arguments); - - if ($argumentsCount !== $javaArguments['argumentsCount']) { - - // 引数の配列の大きさが違う - continue; - - } else { - - foreach ($arguments as $argument) { - - // 型比較 - - - } - - } - - $this->getClass()->appendMethodTrace($methodName, $accessibility, $javaArguments); - - foreach ($methodInfo->getAttributes() as $attribute) { - - $attributeData = $attribute->getAttributeData(); - - if ($attributeData instanceof \JavaCodeAttribute) { - - $handle = fopen('php://memory', 'rw'); - fwrite($handle, $attributeData->getCode()); - rewind($handle); - - $byteCodeStream = new JavaByteCodeStream($handle); - - // 局所変数格納用 - $localstorage = array( - 0 => null, - 1 => null, - 2 => null, - 3 => null - ); - - $i = 0; - if ($this->_constructed) { - $localstorage[0] = $this->getClass(); - $i = 1; - } - - foreach ($arguments as $argument) { - - $localstorage[$i] = $argument; - $i++; - - } - - $stacks = array(); - - $mnemonic = new JavaMnemonicEnum(); - - for (; $byteCodeStream->getOffset() < $attributeData->getOpCodeLength(); ) { - - $opcode = $byteCodeStream->readUnsignedByte(); - - $pointer = $byteCodeStream->getOffset() - 1; - - $name = 'JavaStatement_' . preg_replace('/^MNEMONIC_/', '', $mnemonic->getName($opcode)); - - $mnemonicExecutor = dirname(__DIR__) . '/Statements/' . $name . '.php'; - - if (!is_file($mnemonicExecutor)) { - throw new JavaInvokerException('"' . $name . ' (' . sprintf('0x02X', $opcode) . ')" of mnemonic is not implemented. please report this message to administrator.'); - } - - require_once $mnemonicExecutor; - - $statement = new $name($methodName, $this, $byteCodeStream, $stacks, $localstorage, $cpInfo, $attributeData, $pointer); - $returnValue = $statement->execute(); - - // write trace - $this->getClass()->appendTrace($opcode, $pointer, $stacks, $byteCodeStream->getOperands()); - - if ($returnValue !== null) { - $this->getClass()->traceCompletion(); - return $returnValue; - } - - } - - $this->getClass()->traceCompletion(); - return; - - } - - } - - return; - - } - - - } - - throw new JavaInvokerException('undefined method "' . $methodName . '"'); - - } - - /** - * 型の変換を行います - * - * @param mixed $value 変換対象を指定 - * @return int 変換された型を返します。 - */ - public function valueOf ($value) { - - return (int) $value; - - } - - /** - * アクセス修飾子を取得します。 - * - * @param JavaMethodInfo|JavaFieldInfo $info アクセス修飾子を取得したいストラクチャを指定 - * @return array アクセス修飾子を返します。 - */ - private function _getAccessiblity ($info) { - - $accessFlag = new JavaAccessFlagEnum(); - $accessibility = array(); - - foreach ($accessFlag->getValues() as $value) { - - if (($info->getAccessFlag() & $value) != 0) { - - $accessibility[] = strtolower(preg_replace('/^CONSTANT_/', '', $accessFlag->getName($value))); - - } - - } - - return $accessibility; - } - -} diff --git a/PHPJava/Platform/java/io/PrintStream.php b/PHPJava/Platform/java/io/PrintStream.php deleted file mode 100644 index d89fb0c9..00000000 --- a/PHPJava/Platform/java/io/PrintStream.php +++ /dev/null @@ -1,34 +0,0 @@ -getString() . "\n"; - - } else if (is_string($arg) || - is_int($arg) || - $arg instanceof \JavaType || - $arg instanceof \java\lang\String) { - - echo $arg . "\n"; - - } else if ($arg === null) { - - echo "\n"; - - } else { - - throw new \JavaPlatformException('cannot convert to string'); - - } - - } - -} diff --git a/PHPJava/Platform/java/lang/NullPointerException.php b/PHPJava/Platform/java/lang/NullPointerException.php deleted file mode 100644 index 6a6d89a6..00000000 --- a/PHPJava/Platform/java/lang/NullPointerException.php +++ /dev/null @@ -1,9 +0,0 @@ -Object = $Object; - - } - - public function equals ($Object) { - - if ($this->Object instanceof \JavaStructureUtf8) { - - if ($Object instanceof \Java\lang\String) { - - if ($this->toString() === $Object->toString()) { - - return true; - - } - - } else if ($this->toString() === $Object) { - - return true; - - } - - } - - return false; - - } - - public function toString () { - - return $this->Object->getString(); - - } - - public function rawObject () { - - return $this->Object; - - } - - // php magic method - public function __toString () { - - if (!($this->Object instanceof \JavaStructureUtf8)) { - return (string) $this->Object; - } - - return $this->Object->getString(); - - } - -} diff --git a/PHPJava/Platform/java/lang/StringBuilder.php b/PHPJava/Platform/java/lang/StringBuilder.php deleted file mode 100644 index 90222760..00000000 --- a/PHPJava/Platform/java/lang/StringBuilder.php +++ /dev/null @@ -1,42 +0,0 @@ -sequence .= $arg->toString(); - - } else { - - $this->sequence .= $arg; - - } - - return $this; - - } - - public function toString ($arg = null) { - - return $this->sequence; - - } - - // php magic method - public function __toString () { - - return $this->sequence; - - } - -} \ No newline at end of file diff --git a/PHPJava/Platform/java/lang/System.php b/PHPJava/Platform/java/lang/System.php deleted file mode 100644 index 6d743052..00000000 --- a/PHPJava/Platform/java/lang/System.php +++ /dev/null @@ -1,9 +0,0 @@ -getStack(); - $arrayref = $this->getStack(); - - if (!isset($arrayref[$index])) { - - throw new JavaArrayIndexOutOfBoundsException($this->getMethodName() . ': ' . $index . ' of array index'); - - } - - $this->pushStack($arrayref[$index]); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_aastore.php b/PHPJava/Statements/JavaStatement_aastore.php deleted file mode 100644 index e52961a1..00000000 --- a/PHPJava/Statements/JavaStatement_aastore.php +++ /dev/null @@ -1,18 +0,0 @@ -getStack(); - $index = $this->getStack(); - $arrayref = $this->getStack(); - - $arrayref[$index] = $value; - - } - -} diff --git a/PHPJava/Statements/JavaStatement_aconst_null.php b/PHPJava/Statements/JavaStatement_aconst_null.php deleted file mode 100644 index d5915e14..00000000 --- a/PHPJava/Statements/JavaStatement_aconst_null.php +++ /dev/null @@ -1,14 +0,0 @@ -pushStack(null); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_aload.php b/PHPJava/Statements/JavaStatement_aload.php deleted file mode 100644 index fdbef9be..00000000 --- a/PHPJava/Statements/JavaStatement_aload.php +++ /dev/null @@ -1,16 +0,0 @@ -getByteCodeStream()->readByte(); - - $this->pushStack($this->getLocalstorage($index)); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_aload_0.php b/PHPJava/Statements/JavaStatement_aload_0.php deleted file mode 100644 index b10ea9ec..00000000 --- a/PHPJava/Statements/JavaStatement_aload_0.php +++ /dev/null @@ -1,14 +0,0 @@ -pushStack($this->getLocalstorage(0)); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_aload_1.php b/PHPJava/Statements/JavaStatement_aload_1.php deleted file mode 100644 index f810606f..00000000 --- a/PHPJava/Statements/JavaStatement_aload_1.php +++ /dev/null @@ -1,15 +0,0 @@ -pushStack($this->getLocalstorage(1)); - - - } - -} diff --git a/PHPJava/Statements/JavaStatement_aload_2.php b/PHPJava/Statements/JavaStatement_aload_2.php deleted file mode 100644 index 44906067..00000000 --- a/PHPJava/Statements/JavaStatement_aload_2.php +++ /dev/null @@ -1,14 +0,0 @@ -pushStack($this->getLocalstorage(2)); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_aload_3.php b/PHPJava/Statements/JavaStatement_aload_3.php deleted file mode 100644 index df78b6e2..00000000 --- a/PHPJava/Statements/JavaStatement_aload_3.php +++ /dev/null @@ -1,14 +0,0 @@ -pushStack($this->getLocalstorage(3)); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_anewarray.php b/PHPJava/Statements/JavaStatement_anewarray.php deleted file mode 100644 index 83b48c14..00000000 --- a/PHPJava/Statements/JavaStatement_anewarray.php +++ /dev/null @@ -1,23 +0,0 @@ -getByteCodeStream()->readUnsignedShort(); - - // 空の配列を渡す (nullで埋める) - $count = $this->getStack(); - // need reference - $ref = new ArrayIterator(array_fill(0, $count, null)); - $this->pushStackByReference($ref); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_areturn.php b/PHPJava/Statements/JavaStatement_areturn.php deleted file mode 100644 index 053d45da..00000000 --- a/PHPJava/Statements/JavaStatement_areturn.php +++ /dev/null @@ -1,11 +0,0 @@ -getStack()); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_arraylength.php b/PHPJava/Statements/JavaStatement_arraylength.php deleted file mode 100644 index fd90efc0..00000000 --- a/PHPJava/Statements/JavaStatement_arraylength.php +++ /dev/null @@ -1,13 +0,0 @@ -getStack(); - - $this->pushStack(sizeof($arrayref)); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_astore.php b/PHPJava/Statements/JavaStatement_astore.php deleted file mode 100644 index 95474cc9..00000000 --- a/PHPJava/Statements/JavaStatement_astore.php +++ /dev/null @@ -1,12 +0,0 @@ -getByteCodeStream()->readUnsignedByte(); - $this->setLocalstorage($index, $this->getStack()); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_astore_0.php b/PHPJava/Statements/JavaStatement_astore_0.php deleted file mode 100644 index 2e8265df..00000000 --- a/PHPJava/Statements/JavaStatement_astore_0.php +++ /dev/null @@ -1,11 +0,0 @@ -setLocalstorage(0, $this->getStack()); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_astore_1.php b/PHPJava/Statements/JavaStatement_astore_1.php deleted file mode 100644 index 1ce1a0de..00000000 --- a/PHPJava/Statements/JavaStatement_astore_1.php +++ /dev/null @@ -1,11 +0,0 @@ -setLocalstorage(1, $this->getStack()); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_astore_2.php b/PHPJava/Statements/JavaStatement_astore_2.php deleted file mode 100644 index d81e9e28..00000000 --- a/PHPJava/Statements/JavaStatement_astore_2.php +++ /dev/null @@ -1,11 +0,0 @@ -setLocalstorage(2, $this->getStack()); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_astore_3.php b/PHPJava/Statements/JavaStatement_astore_3.php deleted file mode 100644 index 8622de5f..00000000 --- a/PHPJava/Statements/JavaStatement_astore_3.php +++ /dev/null @@ -1,11 +0,0 @@ -setLocalstorage(3, $this->getStack()); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_athrow.php b/PHPJava/Statements/JavaStatement_athrow.php deleted file mode 100644 index 9c1698b9..00000000 --- a/PHPJava/Statements/JavaStatement_athrow.php +++ /dev/null @@ -1,30 +0,0 @@ -getCpInfo(); - - $objectref = $this->getStack(); - - $className = str_replace('\\', '/', get_class($objectref)); - - foreach ($this->getAttributeData()->getExceptionTables() as $exception) { - - if ($cpInfo[$cpInfo[$exception->getCatchType()]->getClassIndex()]->getString() === $className && - $exception->getStartPc() <= $this->getPointer() && - $exception->getEndPc() >= $this->getPointer()) { - - $this->getByteCodeStream()->setOffset($exception->getHandlerPc()); - return; - - } - - } - - throw new JavaStatementException('exception table was broken.'); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_baload.php b/PHPJava/Statements/JavaStatement_baload.php deleted file mode 100644 index 3512b38b..00000000 --- a/PHPJava/Statements/JavaStatement_baload.php +++ /dev/null @@ -1,11 +0,0 @@ -pushStack($this->getByteCodeStream()->readByte()); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_breakpoint.php b/PHPJava/Statements/JavaStatement_breakpoint.php deleted file mode 100644 index 3c28d1fd..00000000 --- a/PHPJava/Statements/JavaStatement_breakpoint.php +++ /dev/null @@ -1,11 +0,0 @@ -getStack(); - $value2 = $this->getStack(); - - $this->pushStack(BinaryTools::add($value1, $value2, 8)); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_daload.php b/PHPJava/Statements/JavaStatement_daload.php deleted file mode 100644 index 600bb5f4..00000000 --- a/PHPJava/Statements/JavaStatement_daload.php +++ /dev/null @@ -1,17 +0,0 @@ -getStack(); - $arrayref = $this->getStack(); - - $this->pushStack($arrayref[$index]); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_dastore.php b/PHPJava/Statements/JavaStatement_dastore.php deleted file mode 100644 index b006dc10..00000000 --- a/PHPJava/Statements/JavaStatement_dastore.php +++ /dev/null @@ -1,18 +0,0 @@ -getStack(); - $index = $this->getStack(); - $arrayref = $this->getStack(); - - $arrayref[$index] = $value; - - } - -} diff --git a/PHPJava/Statements/JavaStatement_dcmpg.php b/PHPJava/Statements/JavaStatement_dcmpg.php deleted file mode 100644 index 9a74dd52..00000000 --- a/PHPJava/Statements/JavaStatement_dcmpg.php +++ /dev/null @@ -1,11 +0,0 @@ -pushStack(0); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_dconst_1.php b/PHPJava/Statements/JavaStatement_dconst_1.php deleted file mode 100644 index d44caee4..00000000 --- a/PHPJava/Statements/JavaStatement_dconst_1.php +++ /dev/null @@ -1,11 +0,0 @@ -pushStack(1); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_ddiv.php b/PHPJava/Statements/JavaStatement_ddiv.php deleted file mode 100644 index d51b2d6f..00000000 --- a/PHPJava/Statements/JavaStatement_ddiv.php +++ /dev/null @@ -1,11 +0,0 @@ -getByteCodeStream()->readUnsignedByte(); - $this->pushStack($this->getLocalstorage($index)); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_dload_0.php b/PHPJava/Statements/JavaStatement_dload_0.php deleted file mode 100644 index 1d88a1ae..00000000 --- a/PHPJava/Statements/JavaStatement_dload_0.php +++ /dev/null @@ -1,11 +0,0 @@ -getStack(); - $value2 = $this->getStack(); - - $this->pushStack(BinaryTools::multiply($value1, $value2, 8)); - - - - } - -} diff --git a/PHPJava/Statements/JavaStatement_dneg.php b/PHPJava/Statements/JavaStatement_dneg.php deleted file mode 100644 index bb4667e3..00000000 --- a/PHPJava/Statements/JavaStatement_dneg.php +++ /dev/null @@ -1,11 +0,0 @@ -getStack()); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_dstore.php b/PHPJava/Statements/JavaStatement_dstore.php deleted file mode 100644 index 024d2218..00000000 --- a/PHPJava/Statements/JavaStatement_dstore.php +++ /dev/null @@ -1,17 +0,0 @@ -getByteCodeStream()->readUnsignedByte(); - $value = $this->getStack(); - - $this->setLocalstorage($index, BinaryTools::convertDoubleToIEEE754($value)); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_dstore_0.php b/PHPJava/Statements/JavaStatement_dstore_0.php deleted file mode 100644 index 8e53ac99..00000000 --- a/PHPJava/Statements/JavaStatement_dstore_0.php +++ /dev/null @@ -1,11 +0,0 @@ -setLocalstorage(0, $this->getStack()); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_dstore_1.php b/PHPJava/Statements/JavaStatement_dstore_1.php deleted file mode 100644 index d9bdb378..00000000 --- a/PHPJava/Statements/JavaStatement_dstore_1.php +++ /dev/null @@ -1,11 +0,0 @@ -setLocalstorage(1, $this->getStack()); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_dstore_2.php b/PHPJava/Statements/JavaStatement_dstore_2.php deleted file mode 100644 index 6b45400a..00000000 --- a/PHPJava/Statements/JavaStatement_dstore_2.php +++ /dev/null @@ -1,11 +0,0 @@ -setLocalstorage(2, $this->getStack()); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_dstore_3.php b/PHPJava/Statements/JavaStatement_dstore_3.php deleted file mode 100644 index e1bfda96..00000000 --- a/PHPJava/Statements/JavaStatement_dstore_3.php +++ /dev/null @@ -1,11 +0,0 @@ -setLocalstorage(3, $this->getStack()); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_dsub.php b/PHPJava/Statements/JavaStatement_dsub.php deleted file mode 100644 index c3ace5fa..00000000 --- a/PHPJava/Statements/JavaStatement_dsub.php +++ /dev/null @@ -1,14 +0,0 @@ -getStack(); - $rightValue = $this->getStack(); - - $this->pushStack(BinaryTools::sub($leftValue, $rightValue, 8)); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_dup.php b/PHPJava/Statements/JavaStatement_dup.php deleted file mode 100644 index 6fc18a19..00000000 --- a/PHPJava/Statements/JavaStatement_dup.php +++ /dev/null @@ -1,11 +0,0 @@ -dupStack(); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_dup2.php b/PHPJava/Statements/JavaStatement_dup2.php deleted file mode 100644 index cd36b202..00000000 --- a/PHPJava/Statements/JavaStatement_dup2.php +++ /dev/null @@ -1,11 +0,0 @@ -getStack()); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_fstore.php b/PHPJava/Statements/JavaStatement_fstore.php deleted file mode 100644 index ac863f18..00000000 --- a/PHPJava/Statements/JavaStatement_fstore.php +++ /dev/null @@ -1,11 +0,0 @@ -getCpInfo(); - - $cp = $cpInfo[$this->getByteCodeStream()->readUnsignedShort()]; - - $get = $this->getStack(); - - $return = $get->getInstance($cpInfo[$cpInfo[$cp->getNameAndTypeIndex()]->getNameIndex()]->getString()); - - if ($return !== null) { - - $this->pushStack($return); - - } else { - - throw new JavaStatementException('Cannot get ' . $cpInfo[$cpInfo[$cp->getNameAndTypeIndex()]->getNameIndex()]->getString() . ''); - - } - - } - -} diff --git a/PHPJava/Statements/JavaStatement_getstatic.php b/PHPJava/Statements/JavaStatement_getstatic.php deleted file mode 100644 index ba527746..00000000 --- a/PHPJava/Statements/JavaStatement_getstatic.php +++ /dev/null @@ -1,44 +0,0 @@ -getCpInfo(); - - $cp = $cpInfo[$this->getByteCodeStream()->readUnsignedShort()]; - - $class = $cpInfo[$cpInfo[$cp->getClassIndex()]->getClassIndex()]->getString(); - - $signature = JavaClass::parseSignature($cpInfo[$cpInfo[$cp->getNameAndTypeIndex()]->getDescriptorIndex()]->getString()); - - foreach ($this->getInvoker()->getClass()->getFields() as $field) { - - if ($cpInfo[$field->getNameIndex()]->getString() === $cpInfo[$cpInfo[$cp->getNameAndTypeIndex()]->getNameIndex()]->getString()) { - - // push stack - $this->pushStack($this->getInvoker()->getClass()->getStatic($cpInfo[$field->getNameIndex()]->getString())); - - return; - - } - - } - - if (isset($signature[0]['className'])) { - - $this->getInvoker()->loadPlatform($class); - $this->getInvoker()->loadPlatform($signature[0]['className']); - $className = str_replace('/', '\\', $signature[0]['className']); - - $this->pushStack(new $className()); - return; - - } - - throw new JavaStatementException('Has not class or field'); - - - } - -} diff --git a/PHPJava/Statements/JavaStatement_goto.php b/PHPJava/Statements/JavaStatement_goto.php deleted file mode 100644 index 3c7efc8b..00000000 --- a/PHPJava/Statements/JavaStatement_goto.php +++ /dev/null @@ -1,13 +0,0 @@ -getByteCodeStream()->readShort(); - - $this->getByteCodeStream()->setOffset($this->getPointer() + $offset); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_goto_w.php b/PHPJava/Statements/JavaStatement_goto_w.php deleted file mode 100644 index beaf2033..00000000 --- a/PHPJava/Statements/JavaStatement_goto_w.php +++ /dev/null @@ -1,11 +0,0 @@ -getStack(); - - $this->pushStack(base_convert(substr(sprintf('%032s', base_convert($value, 10, 2)), 16), 2, 10)); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_iadd.php b/PHPJava/Statements/JavaStatement_iadd.php deleted file mode 100644 index d1842c6e..00000000 --- a/PHPJava/Statements/JavaStatement_iadd.php +++ /dev/null @@ -1,14 +0,0 @@ -getStack(); - $rightValue = $this->getStack(); - - $this->pushStack(BinaryTools::add($leftValue, $rightValue, 4)); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_iaload.php b/PHPJava/Statements/JavaStatement_iaload.php deleted file mode 100644 index a58db46e..00000000 --- a/PHPJava/Statements/JavaStatement_iaload.php +++ /dev/null @@ -1,14 +0,0 @@ -getStack(); - $arrayref = $this->getStack(); - - $this->pushStack($arrayref[$index]); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_iand.php b/PHPJava/Statements/JavaStatement_iand.php deleted file mode 100644 index 5ff1166c..00000000 --- a/PHPJava/Statements/JavaStatement_iand.php +++ /dev/null @@ -1,14 +0,0 @@ -getStack(); - $value2 = $this->getStack(); - - $this->pushStack(BinaryTools::andBits($value1, $value2, 4)); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_iastore.php b/PHPJava/Statements/JavaStatement_iastore.php deleted file mode 100644 index 87ea436c..00000000 --- a/PHPJava/Statements/JavaStatement_iastore.php +++ /dev/null @@ -1,15 +0,0 @@ -getStack(); - $arrayref = $this->getStack(); - $value = $this->getStack(); - - $value[$arrayref] = $data; - - } - -} diff --git a/PHPJava/Statements/JavaStatement_iconst_0.php b/PHPJava/Statements/JavaStatement_iconst_0.php deleted file mode 100644 index 8be84ef1..00000000 --- a/PHPJava/Statements/JavaStatement_iconst_0.php +++ /dev/null @@ -1,11 +0,0 @@ -pushStack(0); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_iconst_1.php b/PHPJava/Statements/JavaStatement_iconst_1.php deleted file mode 100644 index 85f5b44e..00000000 --- a/PHPJava/Statements/JavaStatement_iconst_1.php +++ /dev/null @@ -1,11 +0,0 @@ -pushStack(1); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_iconst_2.php b/PHPJava/Statements/JavaStatement_iconst_2.php deleted file mode 100644 index 425fc06f..00000000 --- a/PHPJava/Statements/JavaStatement_iconst_2.php +++ /dev/null @@ -1,11 +0,0 @@ -pushStack(2); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_iconst_3.php b/PHPJava/Statements/JavaStatement_iconst_3.php deleted file mode 100644 index 89d1dab6..00000000 --- a/PHPJava/Statements/JavaStatement_iconst_3.php +++ /dev/null @@ -1,11 +0,0 @@ -pushStack(3); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_iconst_4.php b/PHPJava/Statements/JavaStatement_iconst_4.php deleted file mode 100644 index 403714aa..00000000 --- a/PHPJava/Statements/JavaStatement_iconst_4.php +++ /dev/null @@ -1,11 +0,0 @@ -pushStack(4); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_iconst_5.php b/PHPJava/Statements/JavaStatement_iconst_5.php deleted file mode 100644 index 0ea93695..00000000 --- a/PHPJava/Statements/JavaStatement_iconst_5.php +++ /dev/null @@ -1,11 +0,0 @@ -pushStack(5); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_iconst_m1.php b/PHPJava/Statements/JavaStatement_iconst_m1.php deleted file mode 100644 index 8c6d4585..00000000 --- a/PHPJava/Statements/JavaStatement_iconst_m1.php +++ /dev/null @@ -1,11 +0,0 @@ -pushStack(-1); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_idiv.php b/PHPJava/Statements/JavaStatement_idiv.php deleted file mode 100644 index 447bcb7e..00000000 --- a/PHPJava/Statements/JavaStatement_idiv.php +++ /dev/null @@ -1,11 +0,0 @@ -getByteCodeStream()->readShort(); - - $leftOperand = $this->getStack(); - $rightOperand = $this->getStack(); - - if ($leftOperand === $rightOperand) { - - $this->getByteCodeStream()->setOffset($this->getPointer() + $offset); - - } - - } - -} diff --git a/PHPJava/Statements/JavaStatement_if_acmpne.php b/PHPJava/Statements/JavaStatement_if_acmpne.php deleted file mode 100644 index d1b3f4d1..00000000 --- a/PHPJava/Statements/JavaStatement_if_acmpne.php +++ /dev/null @@ -1,20 +0,0 @@ -getByteCodeStream()->readShort(); - - $leftOperand = $this->getStack(); - $rightOperand = $this->getStack(); - - if ($leftOperand !== $rightOperand) { - - $this->getByteCodeStream()->setOffset($this->getPointer() + $offset); - - } - - } - -} diff --git a/PHPJava/Statements/JavaStatement_if_icmpeq.php b/PHPJava/Statements/JavaStatement_if_icmpeq.php deleted file mode 100644 index 80751cba..00000000 --- a/PHPJava/Statements/JavaStatement_if_icmpeq.php +++ /dev/null @@ -1,11 +0,0 @@ -getByteCodeStream()->readShort(); - - $leftOperand = $this->getStack(); - $rightOperand = $this->getStack(); - - if ($leftOperand <= $rightOperand) { - - $this->getByteCodeStream()->setOffset($this->getPointer() + $offset); - - } - - } - -} diff --git a/PHPJava/Statements/JavaStatement_if_icmpgt.php b/PHPJava/Statements/JavaStatement_if_icmpgt.php deleted file mode 100644 index d8179f62..00000000 --- a/PHPJava/Statements/JavaStatement_if_icmpgt.php +++ /dev/null @@ -1,21 +0,0 @@ -getByteCodeStream()->readShort(); - - $leftOperand = $this->getStack(); - $rightOperand = $this->getStack(); - - if ($leftOperand < $rightOperand) { - - $this->getByteCodeStream()->setOffset($this->getPointer() + $offset); - - } - - - } - -} diff --git a/PHPJava/Statements/JavaStatement_if_icmple.php b/PHPJava/Statements/JavaStatement_if_icmple.php deleted file mode 100644 index 9beeb4a8..00000000 --- a/PHPJava/Statements/JavaStatement_if_icmple.php +++ /dev/null @@ -1,11 +0,0 @@ -getByteCodeStream()->readShort(); - - $leftOperand = $this->getStack(); - $rightOperand = $this->getStack(); - - if ($rightOperand < $leftOperand) { - - $this->getByteCodeStream()->setOffset($this->getPointer() + $offset); - - } - } - -} diff --git a/PHPJava/Statements/JavaStatement_if_icmpne.php b/PHPJava/Statements/JavaStatement_if_icmpne.php deleted file mode 100644 index e68bca61..00000000 --- a/PHPJava/Statements/JavaStatement_if_icmpne.php +++ /dev/null @@ -1,20 +0,0 @@ -getByteCodeStream()->readShort(); - - $leftOperand = $this->getStack(); - $rightOperand = $this->getStack(); - - if ($leftOperand != $rightOperand) { - - $this->getByteCodeStream()->setOffset($this->getPointer() + $offset); - - } - - } - -} diff --git a/PHPJava/Statements/JavaStatement_ifeq.php b/PHPJava/Statements/JavaStatement_ifeq.php deleted file mode 100644 index 43c45ee6..00000000 --- a/PHPJava/Statements/JavaStatement_ifeq.php +++ /dev/null @@ -1,20 +0,0 @@ -getByteCodeStream()->readShort(); - - $operand = $this->getStack(); - - if ($operand == 0) { - - $this->getByteCodeStream()->setOffset($this->getPointer() + $offset); - - } - - - } - -} diff --git a/PHPJava/Statements/JavaStatement_ifge.php b/PHPJava/Statements/JavaStatement_ifge.php deleted file mode 100644 index 4ad6d881..00000000 --- a/PHPJava/Statements/JavaStatement_ifge.php +++ /dev/null @@ -1,11 +0,0 @@ -getByteCodeStream()->readShort(); - - $operand = $this->getStack(); - - if ($operand != 0) { - - $this->getByteCodeStream()->setOffset($this->getPointer() + $offset); - - } - - } - -} diff --git a/PHPJava/Statements/JavaStatement_ifnonnull.php b/PHPJava/Statements/JavaStatement_ifnonnull.php deleted file mode 100644 index 16e0933a..00000000 --- a/PHPJava/Statements/JavaStatement_ifnonnull.php +++ /dev/null @@ -1,11 +0,0 @@ -getByteCodeStream()->readUnsignedByte(); - $const = $this->getByteCodeStream()->readByte(); - - $this->setLocalstorage($index, $this->getLocalstorage($index) + $const); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_iload.php b/PHPJava/Statements/JavaStatement_iload.php deleted file mode 100644 index 508e6065..00000000 --- a/PHPJava/Statements/JavaStatement_iload.php +++ /dev/null @@ -1,13 +0,0 @@ -getByteCodeStream()->readUnsignedByte(); - - $this->pushStack($this->getLocalstorage($index)); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_iload_0.php b/PHPJava/Statements/JavaStatement_iload_0.php deleted file mode 100644 index 34a2d685..00000000 --- a/PHPJava/Statements/JavaStatement_iload_0.php +++ /dev/null @@ -1,11 +0,0 @@ -pushStack($this->getLocalstorage(0)); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_iload_1.php b/PHPJava/Statements/JavaStatement_iload_1.php deleted file mode 100644 index a3960caf..00000000 --- a/PHPJava/Statements/JavaStatement_iload_1.php +++ /dev/null @@ -1,11 +0,0 @@ -pushStack($this->getLocalstorage(1)); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_iload_2.php b/PHPJava/Statements/JavaStatement_iload_2.php deleted file mode 100644 index 85d80206..00000000 --- a/PHPJava/Statements/JavaStatement_iload_2.php +++ /dev/null @@ -1,11 +0,0 @@ -pushStack($this->getLocalstorage(2)); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_iload_3.php b/PHPJava/Statements/JavaStatement_iload_3.php deleted file mode 100644 index 8f53fab1..00000000 --- a/PHPJava/Statements/JavaStatement_iload_3.php +++ /dev/null @@ -1,11 +0,0 @@ -pushStack($this->getLocalstorage(3)); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_impdep1.php b/PHPJava/Statements/JavaStatement_impdep1.php deleted file mode 100644 index 1e4c87eb..00000000 --- a/PHPJava/Statements/JavaStatement_impdep1.php +++ /dev/null @@ -1,11 +0,0 @@ -getStack(); - $value2 = $this->getStack(); - - $this->pushStack(BinaryTools::multiply($value1, $value2, 4)); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_ineg.php b/PHPJava/Statements/JavaStatement_ineg.php deleted file mode 100644 index 715dedf6..00000000 --- a/PHPJava/Statements/JavaStatement_ineg.php +++ /dev/null @@ -1,13 +0,0 @@ -getStack(); - - $this->pushStack(BinaryTools::negate($value, 4)); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_instanceof.php b/PHPJava/Statements/JavaStatement_instanceof.php deleted file mode 100644 index 379a6e46..00000000 --- a/PHPJava/Statements/JavaStatement_instanceof.php +++ /dev/null @@ -1,11 +0,0 @@ -getCpInfo(); - - - $cp = $cpInfo[$this->getByteCodeStream()->readUnsignedShort()]; - - // $invokeClassName = '\\' . str_replace('/', '\\', $cpList[$class->getClassIndex()]->getString()); - - - $nameAndTypeIndex = $cpInfo[$cp->getNameAndTypeIndex()]; - - // signature - $signature = JavaClass::parseSignature($cpInfo[$nameAndTypeIndex->getDescriptorIndex()]->getString()); - - $invokeClassName = $this->getStack(); - - $arguments = array(); - - for ($i = 0; $i < $signature['argumentsCount']; $i++) { - - $arguments[] = $this->getStack(); - - } - - - } - -} diff --git a/PHPJava/Statements/JavaStatement_invokestatic.php b/PHPJava/Statements/JavaStatement_invokestatic.php deleted file mode 100644 index bf98cf83..00000000 --- a/PHPJava/Statements/JavaStatement_invokestatic.php +++ /dev/null @@ -1,39 +0,0 @@ -getCpInfo(); - - $cp = $cpInfo[$this->getByteCodeStream()->readUnsignedShort()]; - - $methodName = $cpInfo[$cpInfo[$cp->getNameAndTypeIndex()]->getNameIndex()]->getString(); - - $signature = JavaClass::parseSignature($cpInfo[$cpInfo[$cp->getNameAndTypeIndex()]->getDescriptorIndex()]->getString()); - - $arguments = array(); - - for ($i = 0; $i < $signature['argumentsCount']; $i++) { - - $arguments[] = $this->getStack(); - - } - - krsort($arguments); - - // call invoker - $return = call_user_func_array(array( - $this->getInvoker(), - $methodName - ), $arguments); - - if ($signature[0]['type'] !== 'void') { - - $this->pushStack($return); - - } - - } - -} diff --git a/PHPJava/Statements/JavaStatement_invokevirtual.php b/PHPJava/Statements/JavaStatement_invokevirtual.php deleted file mode 100644 index f6d4fecc..00000000 --- a/PHPJava/Statements/JavaStatement_invokevirtual.php +++ /dev/null @@ -1,61 +0,0 @@ -getCpInfo(); - - $cp = $cpInfo[$this->getByteCodeStream()->readUnsignedShort()]; - - $class = $cpInfo[$cpInfo[$cp->getClassIndex()]->getClassIndex()]->getString(); - - $nameAndTypeIndex = $cpInfo[$cp->getNameAndTypeIndex()]; - - // signature - $signature = JavaClass::parseSignature($cpInfo[$nameAndTypeIndex->getDescriptorIndex()]->getString()); - $arguments = array(); - - for ($i = 0; $i < $signature['argumentsCount']; $i++) { - - $arguments[] = $this->getStack(); - - } - - $invokerClass = $this->getStack(); - - if ($invokerClass instanceof \JavaClass) { - - $result = call_user_func_array(array( - - $invokerClass->getMethodInvoker(), - $cpInfo[$cpInfo[$cp->getNameAndTypeIndex()]->getNameIndex()]->getString() - - ), $arguments); - - - } else { - - // load platform - $this->getInvoker()->loadPlatform($class); - $invokerClassName = '\\' . str_replace('/', '\\', $class); - - $result = call_user_func_array(array( - - $invokerClass, - $cpInfo[$cpInfo[$cp->getNameAndTypeIndex()]->getNameIndex()]->getString() - - ), $arguments); - - } - - if ($signature[0]['type'] !== 'void') { - - $this->pushStack($result); - - } - - - } - -} diff --git a/PHPJava/Statements/JavaStatement_ior.php b/PHPJava/Statements/JavaStatement_ior.php deleted file mode 100644 index 4e86b370..00000000 --- a/PHPJava/Statements/JavaStatement_ior.php +++ /dev/null @@ -1,14 +0,0 @@ -getStack(); - $value2 = $this->getStack(); - - $this->pushStack(BinaryTools::orBits($value1, $value2, 4)); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_irem.php b/PHPJava/Statements/JavaStatement_irem.php deleted file mode 100644 index d82eb18c..00000000 --- a/PHPJava/Statements/JavaStatement_irem.php +++ /dev/null @@ -1,11 +0,0 @@ -getStack()); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_ishl.php b/PHPJava/Statements/JavaStatement_ishl.php deleted file mode 100644 index 68eca89d..00000000 --- a/PHPJava/Statements/JavaStatement_ishl.php +++ /dev/null @@ -1,14 +0,0 @@ -getStack(); - $value2 = $this->getStack(); - - $this->pushStack(BinaryTools::shiftLeft($value1, $value2, 4)); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_ishr.php b/PHPJava/Statements/JavaStatement_ishr.php deleted file mode 100644 index 00ede8c8..00000000 --- a/PHPJava/Statements/JavaStatement_ishr.php +++ /dev/null @@ -1,15 +0,0 @@ -getStack(); - $value2 = $this->getStack(); - - $this->pushStack(BinaryTools::shiftRight($value1, $value2, 4)); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_istore.php b/PHPJava/Statements/JavaStatement_istore.php deleted file mode 100644 index c5a1e0e8..00000000 --- a/PHPJava/Statements/JavaStatement_istore.php +++ /dev/null @@ -1,12 +0,0 @@ -getByteCodeStream()->readUnsignedByte(); - $this->setLocalstorage($index, $this->getStack()); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_istore_0.php b/PHPJava/Statements/JavaStatement_istore_0.php deleted file mode 100644 index afa182a7..00000000 --- a/PHPJava/Statements/JavaStatement_istore_0.php +++ /dev/null @@ -1,11 +0,0 @@ -setLocalstorage(0, $this->getStack()); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_istore_1.php b/PHPJava/Statements/JavaStatement_istore_1.php deleted file mode 100644 index 0243f967..00000000 --- a/PHPJava/Statements/JavaStatement_istore_1.php +++ /dev/null @@ -1,11 +0,0 @@ -setLocalstorage(1, $this->getStack()); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_istore_2.php b/PHPJava/Statements/JavaStatement_istore_2.php deleted file mode 100644 index f183ad65..00000000 --- a/PHPJava/Statements/JavaStatement_istore_2.php +++ /dev/null @@ -1,11 +0,0 @@ -setLocalstorage(2, $this->getStack()); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_istore_3.php b/PHPJava/Statements/JavaStatement_istore_3.php deleted file mode 100644 index 04094cc1..00000000 --- a/PHPJava/Statements/JavaStatement_istore_3.php +++ /dev/null @@ -1,11 +0,0 @@ -setLocalstorage(3, $this->getStack()); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_isub.php b/PHPJava/Statements/JavaStatement_isub.php deleted file mode 100644 index c7be6b4e..00000000 --- a/PHPJava/Statements/JavaStatement_isub.php +++ /dev/null @@ -1,14 +0,0 @@ -getStack(); - $rightValue = $this->getStack(); - - $this->pushStack(BinaryTools::sub($leftValue, $rightValue, 4)); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_iushr.php b/PHPJava/Statements/JavaStatement_iushr.php deleted file mode 100644 index 37c6d4dd..00000000 --- a/PHPJava/Statements/JavaStatement_iushr.php +++ /dev/null @@ -1,14 +0,0 @@ -getStack(); - $value2 = $this->getStack(); - - $this->pushStack(BinaryTools::unsignedShiftRight($value1, $value2, 4)); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_ixor.php b/PHPJava/Statements/JavaStatement_ixor.php deleted file mode 100644 index f9096c35..00000000 --- a/PHPJava/Statements/JavaStatement_ixor.php +++ /dev/null @@ -1,14 +0,0 @@ -getStack(); - $value2 = $this->getStack(); - - $this->pushStack(BinaryTools::xorBits($value1, $value2, 4)); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_jsr.php b/PHPJava/Statements/JavaStatement_jsr.php deleted file mode 100644 index cae0798a..00000000 --- a/PHPJava/Statements/JavaStatement_jsr.php +++ /dev/null @@ -1,11 +0,0 @@ -getStack(); - $value2 = $this->getStack(); - - $this->pushStack(BinaryTools::add($value1, $value2, 8)); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_laload.php b/PHPJava/Statements/JavaStatement_laload.php deleted file mode 100644 index dd8d6649..00000000 --- a/PHPJava/Statements/JavaStatement_laload.php +++ /dev/null @@ -1,17 +0,0 @@ -getStack(); - $arrayref = $this->getStack(); - - $this->pushStack($arrayref[$index]); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_land.php b/PHPJava/Statements/JavaStatement_land.php deleted file mode 100644 index 162d1806..00000000 --- a/PHPJava/Statements/JavaStatement_land.php +++ /dev/null @@ -1,14 +0,0 @@ -getStack(); - $value2 = $this->getStack(); - - $this->pushStack(BinaryTools::andBits($value1, $value2, 8)); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_lastore.php b/PHPJava/Statements/JavaStatement_lastore.php deleted file mode 100644 index c93bddcb..00000000 --- a/PHPJava/Statements/JavaStatement_lastore.php +++ /dev/null @@ -1,18 +0,0 @@ -getStack(); - $index = $this->getStack(); - $arrayref = $this->getStack(); - - $arrayref[$index] = $value; - - } - -} diff --git a/PHPJava/Statements/JavaStatement_lcmp.php b/PHPJava/Statements/JavaStatement_lcmp.php deleted file mode 100644 index b59989e3..00000000 --- a/PHPJava/Statements/JavaStatement_lcmp.php +++ /dev/null @@ -1,11 +0,0 @@ -pushStack(0); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_lconst_1.php b/PHPJava/Statements/JavaStatement_lconst_1.php deleted file mode 100644 index 705b04a4..00000000 --- a/PHPJava/Statements/JavaStatement_lconst_1.php +++ /dev/null @@ -1,11 +0,0 @@ -pushStack(1); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_ldc.php b/PHPJava/Statements/JavaStatement_ldc.php deleted file mode 100644 index 16e3db85..00000000 --- a/PHPJava/Statements/JavaStatement_ldc.php +++ /dev/null @@ -1,40 +0,0 @@ -getCpInfo(); - - $data = $cpInfo[$this->getByteCodeStream()->readUnsignedByte()]; - - $value = null; - - if ($data instanceof \JavaStructureString) { - - $value = $cpInfo[$data->getStringIndex()]; - - if ($value instanceof \JavaStructureUtf8) { - - // convert java string - $this->getInvoker()->loadPlatform('java.lang.String'); - - $value = new \java\lang\String($value); - - } - - } else if (($data instanceof \JavaStructureInteger) || ($data instanceof \JavaStructureFloat)) { - - $value = $data->getBytes(); - - } else { - - $value = $cpInfo[$cpInfo[$this->getByteCodeStream()->readUnsignedByte()]->getStringIndex()]; - - } - - $this->pushStack($value); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_ldc2_w.php b/PHPJava/Statements/JavaStatement_ldc2_w.php deleted file mode 100644 index b58e51bb..00000000 --- a/PHPJava/Statements/JavaStatement_ldc2_w.php +++ /dev/null @@ -1,15 +0,0 @@ -getCpInfo(); - - $data = $cpInfo[$this->getByteCodeStream()->readUnsignedShort()]; - - $this->pushStack($data->getBytes()); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_ldc_w.php b/PHPJava/Statements/JavaStatement_ldc_w.php deleted file mode 100644 index f5ba644d..00000000 --- a/PHPJava/Statements/JavaStatement_ldc_w.php +++ /dev/null @@ -1,11 +0,0 @@ -getByteCodeStream()->readUnsignedByte(); - - $this->pushStack($this->getLocalstorage($index)); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_lload_0.php b/PHPJava/Statements/JavaStatement_lload_0.php deleted file mode 100644 index c6bf30fb..00000000 --- a/PHPJava/Statements/JavaStatement_lload_0.php +++ /dev/null @@ -1,11 +0,0 @@ -getStack(); - $value2 = $this->getStack(); - - $this->pushStack(BinaryTools::multiply($value1, $value2, 8)); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_lneg.php b/PHPJava/Statements/JavaStatement_lneg.php deleted file mode 100644 index 70d482b4..00000000 --- a/PHPJava/Statements/JavaStatement_lneg.php +++ /dev/null @@ -1,11 +0,0 @@ -getStack(); - - $paddingData = $this->getByteCodeStream()->readByte() + $this->getByteCodeStream()->readByte() + $this->getByteCodeStream()->readByte(); - - $offsets = array(); - - $offsets['default'] = $this->getByteCodeStream()->readInt(); - $switchSize = $this->getByteCodeStream()->readUnsignedInt(); - - - for ($i = 0; $i < $switchSize; $i++) { - - $label = $this->getByteCodeStream()->readInt(); - - $offsets[(string) $label] = $this->getByteCodeStream()->readInt(); - - } - - if (isset($offsets[$key])) { - - // goto PC - $this->getByteCodeStream()->setOffset($this->getPointer() + $offsets[$key]); - return; - - } - - // goto default - $this->getByteCodeStream()->setOffset($this->getPointer() + $offsets['default']); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_lor.php b/PHPJava/Statements/JavaStatement_lor.php deleted file mode 100644 index 143cf270..00000000 --- a/PHPJava/Statements/JavaStatement_lor.php +++ /dev/null @@ -1,14 +0,0 @@ -getStack(); - $value2 = $this->getStack(); - - $this->pushStack(BinaryTools::orBits($value1, $value2, 8)); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_lrem.php b/PHPJava/Statements/JavaStatement_lrem.php deleted file mode 100644 index 3bbb6013..00000000 --- a/PHPJava/Statements/JavaStatement_lrem.php +++ /dev/null @@ -1,11 +0,0 @@ -getStack()); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_lshl.php b/PHPJava/Statements/JavaStatement_lshl.php deleted file mode 100644 index 90526f12..00000000 --- a/PHPJava/Statements/JavaStatement_lshl.php +++ /dev/null @@ -1,14 +0,0 @@ -getStack(); - $value2 = $this->getStack(); - - $this->pushStack(BinaryTools::shiftLeft($value1, $value2, 8)); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_lshr.php b/PHPJava/Statements/JavaStatement_lshr.php deleted file mode 100644 index fbb163d4..00000000 --- a/PHPJava/Statements/JavaStatement_lshr.php +++ /dev/null @@ -1,14 +0,0 @@ -getStack(); - $value2 = $this->getStack(); - - $this->pushStack(BinaryTools::shiftRight($value1, $value2, 8)); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_lstore.php b/PHPJava/Statements/JavaStatement_lstore.php deleted file mode 100644 index 351f41ce..00000000 --- a/PHPJava/Statements/JavaStatement_lstore.php +++ /dev/null @@ -1,12 +0,0 @@ -getByteCodeStream()->readUnsignedByte(); - $this->setLocalstorage($index, $this->getStack()); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_lstore_0.php b/PHPJava/Statements/JavaStatement_lstore_0.php deleted file mode 100644 index 51220b97..00000000 --- a/PHPJava/Statements/JavaStatement_lstore_0.php +++ /dev/null @@ -1,11 +0,0 @@ -getStack(); - $value2 = $this->getStack(); - - $this->pushStack(BinaryTools::sub($value1, $value2, 8)); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_lushr.php b/PHPJava/Statements/JavaStatement_lushr.php deleted file mode 100644 index 41e8e90a..00000000 --- a/PHPJava/Statements/JavaStatement_lushr.php +++ /dev/null @@ -1,14 +0,0 @@ -getStack(); - $value2 = $this->getStack(); - - $this->pushStack(BinaryTools::unsignedShiftRight($value1, $value2, 8)); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_lxor.php b/PHPJava/Statements/JavaStatement_lxor.php deleted file mode 100644 index 6fb819bf..00000000 --- a/PHPJava/Statements/JavaStatement_lxor.php +++ /dev/null @@ -1,11 +0,0 @@ -getCpInfo(); - - $class = $cpInfo[$this->getByteCodeStream()->readUnsignedShort()]; - - $className = $cpInfo[$class->getClassIndex()]->getString(); - - if (isset($this->getInvoker()->getClass()->getManipulator()->$className) && - $this->getInvoker()->getClass()->getManipulator()->$className !== null) { - - // call constructor - call_user_func_array( - array( - $this->getInvoker()->getClass()->getManipulator()->$className->getMethodInvoker(), - '' - ), - array() - ); - - $this->pushStack($this->getInvoker()->getClass()->getManipulator()->$className); - - } else { - - if (($this->getInvoker()->getClass()->getManipulator() !== null && - $this->getInvoker()->getClass()->getManipulator()->$className->getArchive() !== null && - $this->getInvoker()->getClass()->getManipulator()->$className->getArchive()->hasClass($className)) || - is_file(dirname($this->getInvoker()->getClass()->getClassFile()) . '/' . $className . '.class')) { - - $javaClass = null; - - if ($this->getInvoker()->getClass()->getManipulator() !== null && - $this->getInvoker()->getClass()->getManipulator()->$className->getArchive() !== null && - $this->getInvoker()->getClass()->getManipulator()->$className->getArchive()->hasClass($className)) { - - $javaClass = new JavaClass($className . '.class', $this->getInvoker()->getClass()->getManipulator()->$className->getArchive()->getClassBytecode($className)); - - } else { - - $javaClass = new JavaClass(dirname($this->getInvoker()->getClass()->getClassFile()) . '/' . $className . '.class'); - - } - - $outerClasses = explode('$', $className); - - $javaClass->setInstance('this', $javaClass); - - for ($i = 1, $size = sizeof($outerClasses); $i < $size; $i++) { - - $javaClass->setInstance('this$' . ($i - 1), $this->getInvoker()->getClass()->getManipulator()->{implode('$', array_slice($outerClasses, 0, $i))}); - - - } - - if (method_exists($javaClass->getMethodInvoker(), '')) { - - // call constructor - call_user_func_array( - array( - $javaClass->getMethodInvoker(), - '' - ), - array( - $this->getInvoker()->getClass() - ) - ); - - } - - if ($this->getInvoker()->getClass()->getManipulator() !== null) { - - // regist to manipulator - $this->getInvoker()->getClass()->getManipulator()->registerClass($javaClass); - - } - - // push to stack - $this->pushStack($javaClass); - - } else { - - // load platform - $this->getInvoker()->loadPlatform($className); - - $invokeClassName = '\\' . str_replace('/', '\\', $className); - - $this->pushStack(new $invokeClassName()); - - } - - } - - } - -} diff --git a/PHPJava/Statements/JavaStatement_newarray.php b/PHPJava/Statements/JavaStatement_newarray.php deleted file mode 100644 index 3b1b453c..00000000 --- a/PHPJava/Statements/JavaStatement_newarray.php +++ /dev/null @@ -1,17 +0,0 @@ -getByteCodeStream()->readUnsignedByte(); - $count = $this->getStack(); - - // need reference - $ref = new ArrayIterator(array_fill(0, $count, null)); - $this->pushStackByReference($ref); - - - } - -} diff --git a/PHPJava/Statements/JavaStatement_nop.php b/PHPJava/Statements/JavaStatement_nop.php deleted file mode 100644 index dacee615..00000000 --- a/PHPJava/Statements/JavaStatement_nop.php +++ /dev/null @@ -1,10 +0,0 @@ -popStack(); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_pop2.php b/PHPJava/Statements/JavaStatement_pop2.php deleted file mode 100644 index 4199b57b..00000000 --- a/PHPJava/Statements/JavaStatement_pop2.php +++ /dev/null @@ -1,11 +0,0 @@ -getCpInfo(); - - $cp = $cpInfo[$this->getByteCodeStream()->readUnsignedShort()]; - $class = $cpInfo[$cp->getNameAndTypeIndex()]; - - $value = $this->getStack(); - $name = $cpInfo[$class->getNameIndex()]->getString(); - - $objectref = $this->getStack(); - - $objectref->setInstance($name, $value); - } - -} diff --git a/PHPJava/Statements/JavaStatement_putstatic.php b/PHPJava/Statements/JavaStatement_putstatic.php deleted file mode 100644 index 6391ab43..00000000 --- a/PHPJava/Statements/JavaStatement_putstatic.php +++ /dev/null @@ -1,21 +0,0 @@ -getCpInfo(); - - $cp = $cpInfo[$this->getByteCodeStream()->readUnsignedShort()]; - - $class = $cpInfo[$cp->getNameAndTypeIndex()]; - $name = $cpInfo[$class->getNameIndex()]->getString(); - - $value = $this->getStack(); - - // set field - $this->getInvoker()->getClass()->setStatic($name, $value); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_ret.php b/PHPJava/Statements/JavaStatement_ret.php deleted file mode 100644 index d0fc4299..00000000 --- a/PHPJava/Statements/JavaStatement_ret.php +++ /dev/null @@ -1,11 +0,0 @@ -pushStack($this->getByteCodeStream()->readShort()); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_swap.php b/PHPJava/Statements/JavaStatement_swap.php deleted file mode 100644 index 78cf3752..00000000 --- a/PHPJava/Statements/JavaStatement_swap.php +++ /dev/null @@ -1,11 +0,0 @@ -getStack(); - - $paddingData = $this->getByteCodeStream()->readByte() + $this->getByteCodeStream()->readByte() + $this->getByteCodeStream()->readByte(); - - $offsets = array(); - - $offsets['default'] = $this->getByteCodeStream()->readInt(); - - $lowByte = $this->getByteCodeStream()->readInt(); - $highByte = $this->getByteCodeStream()->readInt(); - - for ($i = $lowByte; $i <= $highByte; $i++) { - - $offsets[$i] = $this->getByteCodeStream()->readInt(); - - } - - if (isset($offsets[$key])) { - - // goto PC - $this->getByteCodeStream()->setOffset($this->getPointer() + $offsets[$key]); - return; - - } - - // goto default - $this->getByteCodeStream()->setOffset($this->getPointer() + $offsets['default']); - - } - -} diff --git a/PHPJava/Statements/JavaStatement_wide.php b/PHPJava/Statements/JavaStatement_wide.php deleted file mode 100644 index a2f015e5..00000000 --- a/PHPJava/Statements/JavaStatement_wide.php +++ /dev/null @@ -1,11 +0,0 @@ -OperandStacks[] = $result; - - return $result; - - } - - public function readUnsignedByte () { - - $result = parent::readUnsignedByte(); - $this->OperandStacks[] = $result; - - return $result; - - } - - public function readInt () { - - $result = parent::readInt(); - $this->OperandStacks[] = $result; - - return $result; - - } - - public function readUnsignedInt () { - - $result = parent::readUnsignedInt(); - $this->OperandStacks[] = $result; - - return $result; - - } - - public function readShort () { - - $result = parent::readShort(); - $this->OperandStacks[] = $result; - - return $result; - - } - - public function readUnsignedShort () { - - $result = parent::readUnsignedShort(); - $this->OperandStacks[] = $result; - - return $result; - - } - - public function getOperands () { - - $operands = $this->OperandStacks; - - $this->OperandStacks = array(); - - return $operands; - - } - -} \ No newline at end of file diff --git a/PHPJava/Structures/JavaStructureAppendFrame.php b/PHPJava/Structures/JavaStructureAppendFrame.php deleted file mode 100644 index e8014220..00000000 --- a/PHPJava/Structures/JavaStructureAppendFrame.php +++ /dev/null @@ -1,25 +0,0 @@ -FrameType = $Class->getJavaBinaryStream()->readUnsignedByte(); - $this->OffsetDelta = $Class->getJavaBinaryStream()->readUnsignedShort(); - - for ($i = 0, $s = $this->FrameType - 251; $i < $s; $i++) { - - $this->Locals[] = new JavaStructureVarificationTypeInfo($Class); - - } - - } - -} - diff --git a/PHPJava/Structures/JavaStructureChopFrame.php b/PHPJava/Structures/JavaStructureChopFrame.php deleted file mode 100644 index 5db9cb49..00000000 --- a/PHPJava/Structures/JavaStructureChopFrame.php +++ /dev/null @@ -1,18 +0,0 @@ -FrameType = $Class->getJavaBinaryStream()->readUnsignedByte(); - $this->OffsetDelta = $Class->getJavaBinaryStream()->readUnsignedShort(); - - } - -} - diff --git a/PHPJava/Structures/JavaStructureClass.php b/PHPJava/Structures/JavaStructureClass.php deleted file mode 100644 index 35c0f99d..00000000 --- a/PHPJava/Structures/JavaStructureClass.php +++ /dev/null @@ -1,22 +0,0 @@ -ClassIndex = $this->Class->getJavaBinaryStream()->readUnsignedShort(); - - } - - public function getClassIndex () { - - return $this->ClassIndex; - - } - - -} \ No newline at end of file diff --git a/PHPJava/Structures/JavaStructureClasses.php b/PHPJava/Structures/JavaStructureClasses.php deleted file mode 100644 index 665e1994..00000000 --- a/PHPJava/Structures/JavaStructureClasses.php +++ /dev/null @@ -1,64 +0,0 @@ -InnerClassInfoIndex = $InnerClassInfoIndex; - - } - - public function setOuterClassInfoIndex ($OuterClassInfoIndex) { - - $this->OuterClassInfoIndex = $OuterClassInfoIndex; - - } - - public function setInnerNameIndex ($InnerNameIndex) { - - $this->InnerNameIndex = $InnerNameIndex; - - } - - public function setInnerClassAccessFlag ($InnerClassAccessFlag) { - - $this->InnerClassAccessFlag = $InnerClassAccessFlag; - - } - - public function getInnerClassInfoIndex () { - - return $this->InnerClassInfoIndex; - - } - - public function getOuterClassInfoIndex () { - - return $this->OuterClassInfoIndex; - - } - - public function getInnerNameIndex () { - - return $this->InnerNameIndex; - - } - - public function getInnerClassAccessFlag () { - - return $this->InnerClassAccessFlag; - - } - -} \ No newline at end of file diff --git a/PHPJava/Structures/JavaStructureDouble.php b/PHPJava/Structures/JavaStructureDouble.php deleted file mode 100644 index 4746e349..00000000 --- a/PHPJava/Structures/JavaStructureDouble.php +++ /dev/null @@ -1,23 +0,0 @@ -HighBytes = $Class->getJavaBinaryStream()->readUnsignedInt(); - $this->LowBytes = $Class->getJavaBinaryStream()->readUnsignedInt(); - - } - - public function getBytes () { - - return ($this->HighBytes << 32) + $this->LowBytes; - - } - -} diff --git a/PHPJava/Structures/JavaStructureDoubleVariableInfo.php b/PHPJava/Structures/JavaStructureDoubleVariableInfo.php deleted file mode 100644 index dd553d8a..00000000 --- a/PHPJava/Structures/JavaStructureDoubleVariableInfo.php +++ /dev/null @@ -1,15 +0,0 @@ -Tag = $Class->getJavaBinaryStream()->readUnsignedByte(); - - } - -} \ No newline at end of file diff --git a/PHPJava/Structures/JavaStructureExceptionTable.php b/PHPJava/Structures/JavaStructureExceptionTable.php deleted file mode 100644 index a69eed65..00000000 --- a/PHPJava/Structures/JavaStructureExceptionTable.php +++ /dev/null @@ -1,57 +0,0 @@ -StartPc = $StartPc; - - } - - public function setEndPc ($EndPc) { - - $this->EndPc = $EndPc; - - } - - public function setHandlerPc ($HandlerPc) { - - $this->HandlerPc = $HandlerPc; - - } - - public function setCatchType ($CatchType) { - - $this->CatchType = $CatchType; - - } - public function getStartPc () { - - return $this->StartPc; - - } - - public function getEndPc () { - - return $this->EndPc; - - } - - public function getHandlerPc () { - - return $this->HandlerPc; - - } - - public function getCatchType () { - - return $this->CatchType; - - } - -} \ No newline at end of file diff --git a/PHPJava/Structures/JavaStructureFieldInfo.php b/PHPJava/Structures/JavaStructureFieldInfo.php deleted file mode 100644 index 4a7d58c9..00000000 --- a/PHPJava/Structures/JavaStructureFieldInfo.php +++ /dev/null @@ -1,53 +0,0 @@ -AccessFlag = $this->Class->getJavaBinaryStream()->readUnsignedShort(); - $this->NameIndex = $this->Class->getJavaBinaryStream()->readUnsignedShort(); - $this->DescriptorIndex = $this->Class->getJavaBinaryStream()->readUnsignedShort(); - $this->AttributeCount = $this->Class->getJavaBinaryStream()->readUnsignedShort(); - - for ($i = 0; $i < $this->AttributeCount; $i++) { - - $this->Attributes[$i] = new JavaAttributeInfo($Class); - - } - - } - - public function getAccessFlag () { - - return $this->AccessFlag; - - } - - public function getNameIndex () { - - return $this->NameIndex; - - } - - public function getDescriptorIndex () { - - return $this->DescriptorIndex; - - } - - public function getAttributes () { - - return $this->Attributes; - - } - - -} \ No newline at end of file diff --git a/PHPJava/Structures/JavaStructureFieldRef.php b/PHPJava/Structures/JavaStructureFieldRef.php deleted file mode 100644 index 551896e2..00000000 --- a/PHPJava/Structures/JavaStructureFieldRef.php +++ /dev/null @@ -1,29 +0,0 @@ -ClassIndex = $this->Class->getJavaBinaryStream()->readUnsignedShort(); - $this->NameAndTypeIndex = $this->Class->getJavaBinaryStream()->readUnsignedShort(); - - } - - public function getClassIndex () { - - return $this->ClassIndex; - - } - - public function getNameAndTypeIndex () { - - return $this->NameAndTypeIndex; - - } - -} \ No newline at end of file diff --git a/PHPJava/Structures/JavaStructureFloat.php b/PHPJava/Structures/JavaStructureFloat.php deleted file mode 100644 index 553d1044..00000000 --- a/PHPJava/Structures/JavaStructureFloat.php +++ /dev/null @@ -1,21 +0,0 @@ -Bytes = $Class->getJavaBinaryStream()->readUnsignedInt(); - - } - - public function getBytes () { - - return $this->Bytes; - - } - -} diff --git a/PHPJava/Structures/JavaStructureFloatVariableInfo.php b/PHPJava/Structures/JavaStructureFloatVariableInfo.php deleted file mode 100644 index 27633640..00000000 --- a/PHPJava/Structures/JavaStructureFloatVariableInfo.php +++ /dev/null @@ -1,15 +0,0 @@ -Tag = $Class->getJavaBinaryStream()->readUnsignedByte(); - - } - -} \ No newline at end of file diff --git a/PHPJava/Structures/JavaStructureFullFrame.php b/PHPJava/Structures/JavaStructureFullFrame.php deleted file mode 100644 index 08b5c71b..00000000 --- a/PHPJava/Structures/JavaStructureFullFrame.php +++ /dev/null @@ -1,38 +0,0 @@ -FrameType = $Class->getJavaBinaryStream()->readUnsignedByte(); - $this->OffsetDelta = $Class->getJavaBinaryStream()->readUnsignedShort(); - $this->NumberOfLocals = $Class->getJavaBinaryStream()->readUnsignedShort(); - - for ($i = 0; $i < $this->NumberOfLocals; $i++) { - - $this->Locals[] = new JavaStructureVarificationTypeInfo($Class); - - } - - $this->NumberOfStackItems = $Class->getJavaBinaryStream()->readUnsignedShort(); - - for ($i = 0; $i < $this->NumberOfStackItems; $i++) { - - $this->Stack[] = new JavaStructureVarificationTypeInfo($Class); - - } - - } - -} - diff --git a/PHPJava/Structures/JavaStructureInteger.php b/PHPJava/Structures/JavaStructureInteger.php deleted file mode 100644 index 7266e3f0..00000000 --- a/PHPJava/Structures/JavaStructureInteger.php +++ /dev/null @@ -1,22 +0,0 @@ -Bytes = $Class->getJavaBinaryStream()->readInt(); - - - } - - public function getBytes () { - - return $this->Bytes; - - } - -} diff --git a/PHPJava/Structures/JavaStructureIntegerVariableInfo.php b/PHPJava/Structures/JavaStructureIntegerVariableInfo.php deleted file mode 100644 index ad6ed2e5..00000000 --- a/PHPJava/Structures/JavaStructureIntegerVariableInfo.php +++ /dev/null @@ -1,15 +0,0 @@ -Tag = $Class->getJavaBinaryStream()->readUnsignedByte(); - - } - -} \ No newline at end of file diff --git a/PHPJava/Structures/JavaStructureLineNumberTable.php b/PHPJava/Structures/JavaStructureLineNumberTable.php deleted file mode 100644 index 1906800a..00000000 --- a/PHPJava/Structures/JavaStructureLineNumberTable.php +++ /dev/null @@ -1,32 +0,0 @@ -StartPc = $StartPc; - - } - - public function setLineNumber ($LineNumber) { - - $this->LineNumber = $LineNumber; - - } - - public function getStartPc () { - - return $this->StartPc; - - } - - public function getLineNumber () { - - return $this->LineNumber; - - } - -} \ No newline at end of file diff --git a/PHPJava/Structures/JavaStructureLocalVariableTable.php b/PHPJava/Structures/JavaStructureLocalVariableTable.php deleted file mode 100644 index 69928b84..00000000 --- a/PHPJava/Structures/JavaStructureLocalVariableTable.php +++ /dev/null @@ -1,24 +0,0 @@ -StartPc = $Class->getJavaBinaryStream()->readUnsignedShort(); - $this->Length = $Class->getJavaBinaryStream()->readUnsignedShort(); - $this->NameIndex = $Class->getJavaBinaryStream()->readUnsignedShort(); - $this->DescriptorIndex = $Class->getJavaBinaryStream()->readUnsignedShort(); - $this->Index = $Class->getJavaBinaryStream()->readUnsignedShort(); - - } - -} \ No newline at end of file diff --git a/PHPJava/Structures/JavaStructureLong.php b/PHPJava/Structures/JavaStructureLong.php deleted file mode 100644 index b55a227a..00000000 --- a/PHPJava/Structures/JavaStructureLong.php +++ /dev/null @@ -1,28 +0,0 @@ -HighBytes = $Class->getJavaBinaryStream()->readUnsignedInt(); - //$this->LowBytes = $Class->getJavaBinaryStream()->readUnsignedInt(); - - $this->Bytes = $Class->getJavaBinaryStream()->readLong(); - - } - - public function getBytes () { - - //return ($this->HighBytes << 32) + $this->LowBytes; - return $this->Bytes; - - } - -} diff --git a/PHPJava/Structures/JavaStructureLongVariableInfo.php b/PHPJava/Structures/JavaStructureLongVariableInfo.php deleted file mode 100644 index ee752eee..00000000 --- a/PHPJava/Structures/JavaStructureLongVariableInfo.php +++ /dev/null @@ -1,15 +0,0 @@ -Tag = $Class->getJavaBinaryStream()->readUnsignedByte(); - - } - -} \ No newline at end of file diff --git a/PHPJava/Structures/JavaStructureMethodInfo.php b/PHPJava/Structures/JavaStructureMethodInfo.php deleted file mode 100644 index 1e694115..00000000 --- a/PHPJava/Structures/JavaStructureMethodInfo.php +++ /dev/null @@ -1,53 +0,0 @@ -AccessFlag = $this->Class->getJavaBinaryStream()->readUnsignedShort(); - $this->NameIndex = $this->Class->getJavaBinaryStream()->readUnsignedShort(); - $this->DescriptorIndex = $this->Class->getJavaBinaryStream()->readUnsignedShort(); - $this->AttributeCount = $this->Class->getJavaBinaryStream()->readUnsignedShort(); - - for ($i = 0; $i < $this->AttributeCount; $i++) { - - $this->Attributes[$i] = new JavaAttributeInfo($Class); - - } - - } - - public function getAccessFlag () { - - return $this->AccessFlag; - - } - - public function getNameIndex () { - - return $this->NameIndex; - - } - - public function getDescriptorIndex () { - - return $this->DescriptorIndex; - - } - - public function getAttributes () { - - return $this->Attributes; - - } - - -} \ No newline at end of file diff --git a/PHPJava/Structures/JavaStructureMethodref.php b/PHPJava/Structures/JavaStructureMethodref.php deleted file mode 100644 index 9eae3e33..00000000 --- a/PHPJava/Structures/JavaStructureMethodref.php +++ /dev/null @@ -1,29 +0,0 @@ -ClassIndex = $this->Class->getJavaBinaryStream()->readUnsignedShort(); - $this->NameAndTypeIndex = $this->Class->getJavaBinaryStream()->readUnsignedShort(); - - } - - public function getClassIndex () { - - return $this->ClassIndex; - - } - - public function getNameAndTypeIndex () { - - return $this->NameAndTypeIndex; - - } - -} \ No newline at end of file diff --git a/PHPJava/Structures/JavaStructureNameAndType.php b/PHPJava/Structures/JavaStructureNameAndType.php deleted file mode 100644 index d0a08cb4..00000000 --- a/PHPJava/Structures/JavaStructureNameAndType.php +++ /dev/null @@ -1,29 +0,0 @@ -NameIndex = $this->Class->getJavaBinaryStream()->readUnsignedShort(); - $this->DescriptorIndex = $this->Class->getJavaBinaryStream()->readUnsignedShort(); - - } - - public function getNameIndex () { - - return $this->NameIndex; - - } - - public function getDescriptorIndex () { - - return $this->DescriptorIndex; - - } - -} \ No newline at end of file diff --git a/PHPJava/Structures/JavaStructureNullVariableInfo.php b/PHPJava/Structures/JavaStructureNullVariableInfo.php deleted file mode 100644 index 7337c78f..00000000 --- a/PHPJava/Structures/JavaStructureNullVariableInfo.php +++ /dev/null @@ -1,15 +0,0 @@ -Tag = $Class->getJavaBinaryStream()->readUnsignedByte(); - - } - -} \ No newline at end of file diff --git a/PHPJava/Structures/JavaStructureObjectVariableInfo.php b/PHPJava/Structures/JavaStructureObjectVariableInfo.php deleted file mode 100644 index b987ab7c..00000000 --- a/PHPJava/Structures/JavaStructureObjectVariableInfo.php +++ /dev/null @@ -1,17 +0,0 @@ -Tag = $Class->getJavaBinaryStream()->readUnsignedByte(); - $this->cpoolIndex = $Class->getJavaBinaryStream()->readUnsignedShort(); - - } - -} \ No newline at end of file diff --git a/PHPJava/Structures/JavaStructureSameFrame.php b/PHPJava/Structures/JavaStructureSameFrame.php deleted file mode 100644 index 04a720c6..00000000 --- a/PHPJava/Structures/JavaStructureSameFrame.php +++ /dev/null @@ -1,16 +0,0 @@ -FrameType = $Class->getJavaBinaryStream()->readUnsignedByte(); - - } - -} - diff --git a/PHPJava/Structures/JavaStructureSameFrameExtended.php b/PHPJava/Structures/JavaStructureSameFrameExtended.php deleted file mode 100644 index d6398f4a..00000000 --- a/PHPJava/Structures/JavaStructureSameFrameExtended.php +++ /dev/null @@ -1,19 +0,0 @@ -FrameType = $Class->getJavaBinaryStream()->readUnsignedByte(); - $this->OffsetDelta = $Class->getJavaBinaryStream()->readUnsignedShort(); - - - } - -} - diff --git a/PHPJava/Structures/JavaStructureSameLocals1StackItemFrame.php b/PHPJava/Structures/JavaStructureSameLocals1StackItemFrame.php deleted file mode 100644 index f96e5a1c..00000000 --- a/PHPJava/Structures/JavaStructureSameLocals1StackItemFrame.php +++ /dev/null @@ -1,19 +0,0 @@ -FrameType = $Class->getJavaBinaryStream()->readUnsignedByte(); - - $this->Stack[] = new JavaStructureVarificationTypeInfo($Class); - - } - -} - diff --git a/PHPJava/Structures/JavaStructureSameLocals1StackItemFrameExtended.php b/PHPJava/Structures/JavaStructureSameLocals1StackItemFrameExtended.php deleted file mode 100644 index 7d258ad3..00000000 --- a/PHPJava/Structures/JavaStructureSameLocals1StackItemFrameExtended.php +++ /dev/null @@ -1,21 +0,0 @@ -FrameType = $Class->getJavaBinaryStream()->readUnsignedByte(); - $this->OffsetDelta = $Class->getJavaBinaryStream()->readUnsignedShort(); - - $this->Locals[] = new JavaStructureVarificationTypeInfo($Class); - - } - -} - diff --git a/PHPJava/Structures/JavaStructureStackMapFrame.php b/PHPJava/Structures/JavaStructureStackMapFrame.php deleted file mode 100644 index 42088610..00000000 --- a/PHPJava/Structures/JavaStructureStackMapFrame.php +++ /dev/null @@ -1,56 +0,0 @@ -FrameType = $Class->getJavaBinaryStream()->readUnsignedByte(); - - // back by frametype - $Class->getJavaBinaryStream()->seek(-1); - - if ($this->FrameType >= 0 && $this->FrameType <= 63) { - - $this->SameFrame = new JavaStructureSameFrame($Class); - - } else if ($this->FrameType >= 64 && $this->FrameType <= 127) { - - $this->SameLocals1StackItemFrame = new JavaStructureSameLocals1StackItemFrame($Class); - - } else if ($this->FrameType == 247) { - - $this->SameLocals1StackItemFrameExtended = new JavaStructureSameLocals1StackItemFrameExtended($Class); - - } else if ($this->FrameType >= 248 && $this->FrameType <= 250) { - - $this->ChopFrame = new JavaStructureChopFrame($Class); - - } else if ($this->FrameType == 251) { - - $this->SameFrameExtended = new JavaStructureSameFrameExtended($Class); - - } else if ($this->FrameType >= 252 && $this->FrameType <= 254) { - - $this->AppendFrame = new JavaStructureAppendFrame($Class); - - } else if ($this->FrameType == 255) { - - $this->FullFrame = new JavaStructureFullFrame($Class); - - } - - } - -} \ No newline at end of file diff --git a/PHPJava/Structures/JavaStructureString.php b/PHPJava/Structures/JavaStructureString.php deleted file mode 100644 index b047c8a2..00000000 --- a/PHPJava/Structures/JavaStructureString.php +++ /dev/null @@ -1,21 +0,0 @@ -StringIndex = $this->Class->getJavaBinaryStream()->readUnsignedShort(); - - } - - public function getStringIndex () { - - return $this->StringIndex; - - } - -} \ No newline at end of file diff --git a/PHPJava/Structures/JavaStructureTopVariableInfo.php b/PHPJava/Structures/JavaStructureTopVariableInfo.php deleted file mode 100644 index ea6d8ace..00000000 --- a/PHPJava/Structures/JavaStructureTopVariableInfo.php +++ /dev/null @@ -1,15 +0,0 @@ -Tag = $Class->getJavaBinaryStream()->readUnsignedByte(); - - } - -} \ No newline at end of file diff --git a/PHPJava/Structures/JavaStructureUninitializedThisVariableInfo.php b/PHPJava/Structures/JavaStructureUninitializedThisVariableInfo.php deleted file mode 100644 index ac9b308e..00000000 --- a/PHPJava/Structures/JavaStructureUninitializedThisVariableInfo.php +++ /dev/null @@ -1,15 +0,0 @@ -Tag = $Class->getJavaBinaryStream()->readUnsignedByte(); - - } - -} \ No newline at end of file diff --git a/PHPJava/Structures/JavaStructureUninitializedVariableInfo.php b/PHPJava/Structures/JavaStructureUninitializedVariableInfo.php deleted file mode 100644 index 8b160406..00000000 --- a/PHPJava/Structures/JavaStructureUninitializedVariableInfo.php +++ /dev/null @@ -1,17 +0,0 @@ -Tag = $Class->getJavaBinaryStream()->readUnsignedByte(); - $this->Offset = $Class->getJavaBinaryStream()->readUnsignedShort(); - - } - -} \ No newline at end of file diff --git a/PHPJava/Structures/JavaStructureUtf8.php b/PHPJava/Structures/JavaStructureUtf8.php deleted file mode 100644 index b115b52c..00000000 --- a/PHPJava/Structures/JavaStructureUtf8.php +++ /dev/null @@ -1,34 +0,0 @@ -Length = $this->Class->getJavaBinaryStream()->readUnsignedShort(); - - for ($i = 0; $i < $this->Length; $i++) { - - $this->String .= chr($this->Class->getJavaBinaryStream()->readUnsignedByte()); - - } - - } - - public function getLength () { - - return $this->Length; - - } - - public function getString () { - - return $this->String; - - } - -} \ No newline at end of file diff --git a/PHPJava/Structures/JavaStructureVerificationTypeInfo.php b/PHPJava/Structures/JavaStructureVerificationTypeInfo.php deleted file mode 100644 index 45b7f403..00000000 --- a/PHPJava/Structures/JavaStructureVerificationTypeInfo.php +++ /dev/null @@ -1,36 +0,0 @@ -Tag = $Class->getJavaBinaryStream()->readUnsignedByte(); - // back by tag - $Class->getJavaBinaryStream()->seek(-1); - - if ($this->Tag == 0) { - $this->TopVariableInfo = new JavaStructureTopVariableInfo($Class); - } else if ($this->Tag == 1) { - $this->IntegerVariableInfo = new JavaStructureIntegerVariableInfo($Class); - } else if ($this->Tag == 2) { - $this->FloatVariableInfo = new JavaStructureFloatVariableInfo($Class); - } else if ($this->Tag == 4) { - $this->LongVariableInfo = new JavaStructureLongVariableInfo($Class); - } else if ($this->Tag == 3) { - $this->DoubleVariableInfo = new JavaStructureDoubleVariableInfo($Class); - } else if ($this->Tag == 5) { - $this->NullVariableInfo = new JavaStructureNullVariableInfo($Class); - } else if ($this->Tag == 6) { - $this->UninitializedThisVariableInfo = new JavaStructureUninitializedThisVariableInfo($Class); - } else if ($this->Tag == 7) { - $this->ObjectVariableInfo = new JavaStructureObjectVariableInfo($Class); - } else if ($this->Tag == 8) { - $this->UninitializedVariableInfo = new JavaStructureUninitializedVariableInfo($Class); - } - -} - -} - diff --git a/PHPJava/Types/JavaTypeBoolean.php b/PHPJava/Types/JavaTypeBoolean.php deleted file mode 100644 index 0d3c82f1..00000000 --- a/PHPJava/Types/JavaTypeBoolean.php +++ /dev/null @@ -1,5 +0,0 @@ -= 0 && $bits[$index] === '1'; $index--) { - - // nop - - } - - if ($index === -1) { - - throw new BinaryToolsException('Passed parameter was overflow'); - - } - - $bits[$index] = '1'; - - for ($i = $index + 1; $i < $bitSize; $i++) { - - $bits[$i] = '0'; - - } - - - return $bits; - - } - - public final static function toSigned ($value, $bytes) { - - $convert = base_convert((string) $value, 10, 2); - $bitSize = strlen($convert); - - if ($bitSize < ($bytes * 8) || $convert[0] !== '1') { - - return $value; - - } - - return '-' . base_convert(self::addOneBit(self::reverseBits($convert)), 2, 10); - - } - - public final static function negate ($value, $bytes) { - - $value = base_convert((string) $value, 10, 2); - - if (sprintf('%0' . $bytes . 's', $value) === str_repeat('0', $bytes)) { - - // zero number was overflow - return '0'; - - } - - $convert = self::addOneBit(self::reverseBits($value)); - - if ($convert[0] === '1') { - - $convert = '-' . base_convert($convert, 2, 10); - - } else { - - $convert = base_convert($convert, 2, 10); - - } - - return $convert; - - } - - public final static function multiply ($value1, $value2, $bytes) { - - if (function_exists('gmp_mul')) { - - $a = gmp_init($value1); - $b = gmp_init($value2); - - return gmp_strval(gmp_mul($a, $b)); - - } else if (function_exists('bcmul')) { - - return bcmul($value1, $value2); - - } else { - - throw new BinaryToolsException('Cannot multiply values.'); - - } - - } - - public final static function add ($value1, $value2, $bytes) { - - if (function_exists('gmp_add')) { - - $a = gmp_init($value1); - $b = gmp_init($value2); - - return gmp_strval(gmp_add($a, $b)); - - } else if (function_exists('bcadd')) { - - return bcadd($value1, $value2); - - } else { - - throw new BinaryToolsException('Cannot add values.'); - - } - - } - - public final static function sub ($value1, $value2, $bytes) { - - if (function_exists('gmp_sub')) { - - $a = gmp_init($value1); - $b = gmp_init($value2); - - return gmp_strval(gmp_sub($a, $b)); - - } else if (function_exists('bcsub')) { - - return bcsub($value1, $value2); - - } else { - - throw new BinaryToolsException('Cannot sub values.'); - - } - - } - - public final static function div ($value1, $value2, $bytes) { - - if (function_exists('gmp_div')) { - - $a = gmp_init($value1); - $b = gmp_init($value2); - - return gmp_strval(gmp_div($a, $b)); - - } else if (function_exists('bcdiv')) { - - return bcdiv($value1, $value2); - - } else { - - throw new BinaryToolsException('Cannot div values.'); - - } - - } - - public final static function shiftLeft ($value1, $value2, $bytes) { - - $bits = base_convert($value1, 10, 2); - - $bits = sprintf('%0' . ($bytes * 8) . 's', $bits . str_repeat('0', $value2)); - - return base_convert($bits, 2, 10); - - } - - public final static function unsignedShiftRight ($value1, $value2, $bytes) { - - $bits = sprintf('%0' . ($bytes * 8) . 's', base_convert($value2, 10, 2)); - - $bits = sprintf('%0' . ($bytes * 8) . 's', substr($bits, 0, strlen($bits) - $value1)); - - if ($bits === '') { - - $bits = '0'; - - } - - return base_convert($bits, 2, 10); - - } - - public final static function shiftRight ($value1, $value2, $bytes) { - - return self::toSigned(self::unsignedShiftRight($value1, $value2, $bytes), $bytes); - - } - - public final static function orBits ($value1, $value2, $bytes) { - - $value1 = sprintf('%0' . ($bytes * 8) . 's', base_convert($value1, 10, 2)); - $value2 = sprintf('%0' . ($bytes * 8) . 's', base_convert($value2, 10, 2)); - - $build = ''; - for ($i = 0; $i < $bytes * 8; $i++) { - - if ($value1[$i] === '1' || $value2[$i] == '1') { - - $build .= '1'; - - } else { - - $build .= '0'; - - } - - } - - return base_convert($build, 2, 10); - - } - - public final static function xorBits ($value1, $value2, $bytes) { - - $value1 = sprintf('%0' . ($bytes * 8) . 's', base_convert($value1, 10, 2)); - $value2 = sprintf('%0' . ($bytes * 8) . 's', base_convert($value2, 10, 2)); - - $build = ''; - for ($i = 0; $i < $bytes * 8; $i++) { - - if (($value1[$i] === '1' && $value2[$i] === '0') || - ($value1[$i] === '0' && $value2[$i] === '1')) { - - $build .= '1'; - - } else { - - $build .= '0'; - - } - - } - - return base_convert($build, 2, 10); - - } - - public final static function andBits ($value1, $value2, $bytes) { - - $value1 = sprintf('%0' . ($bytes * 8) . 's', base_convert($value1, 10, 2)); - $value2 = sprintf('%0' . ($bytes * 8) . 's', base_convert($value2, 10, 2)); - - $build = ''; - for ($i = 0; $i < $bytes * 8; $i++) { - - if ($value1[$i] === '1' && $value2[$i] === '1') { - - $build .= '1'; - - } else { - - $build .= '0'; - - } - - } - - return base_convert($build, 2, 10); - - } - - public final static function convertDoubleToIEEE754 ($doubleValue, $rounded = 8) { - - $doubleValue = sprintf('%063s', base_convert($doubleValue, 10, 2)); - - $sign = $doubleValue[0]; - $exponent = substr($doubleValue, 1, 10); - $fraction = substr($doubleValue, 11); - - // double scale - $scale = 52; - - $fractionData = 0; - for ($i = 0; $i < 52; $i++) { - $fractionData = bcadd($fractionData, bcmul($fraction[$i], bcpow(2, -1 * ($i + 1), $scale), $scale), $scale); - } - - // calc sign - $operand1 = -1 * $sign; - - // calc fraction - $operand2 = bcadd(1, $fractionData, $scale); - - // calc exponent and bias(?) - $operand3 = bcpow(2, bindec($exponent), $scale); - - return bcmul(-2, bcmul(bcmul($operand1, $operand2, $scale), $operand3, $scale), $rounded); - - } - -} diff --git a/PHPJava/Utils/BinaryToolsException.php b/PHPJava/Utils/BinaryToolsException.php deleted file mode 100644 index a14c0157..00000000 --- a/PHPJava/Utils/BinaryToolsException.php +++ /dev/null @@ -1,3 +0,0 @@ -

+ +# What is PHPJava? +PHPJava は PHP で JVM (Java Virtual Machine) をエミュレーションさせたり、JVM 上で実行できる中間コードのコンパイラを提供している実験的なライブラリです 🐘 +PHPJava は予めコンパイルされた Java ファイル(一般的には class ファイル)を読み込み逐次処理をしていきます ☕ +そして、 PHPJava は Java を **ブリッジや通信をするためのライブラリではありません**。 +PHPJava は **100% PHP のみ** で動きます +このプロジェクトは [Java Virtual Machine Specification](https://docs.oracle.com/javase/specs/jvms/se11/html/index.html) を参考に創られています。 + +私達は心よりあなたのコントリビューションをお待ちしています 💪 + +コントリビューションガイド: +- [コントリビューションガイド](https://github.com/php-java/php-java/wiki/The-Contribution-Guide) + +## ドキュメント + +## Java Virtual Machine +- [English](./README.md) +- [日本語](./README-ja.md) + +## 中間コードコンパイラ +- [English](./docs/compiler/README.md) +- [日本語](./docs/compiler/README-ja.md) + +## PHP のシンタックスによる JVM 言語 +- [English](./docs/jvm-lang/README.md) +- [日本語](./docs/jvm-lang/README-ja.md) + +## デモ +![DEMO](https://user-images.githubusercontent.com/1282995/58679222-87070880-839d-11e9-8c98-978fdd0bb015.gif) + +## 必須環境 +- PHP >= 7.2 +- Composer +- ext-zip + +## PHPJava を実行ファイルとして実行する +PHPJava は実行ファイルとして処理させることが可能です。 + +### クラスファイルを動かす場合 +```shell +./vendor/bin/PHPJava HelloWorld +``` + +または、 + +```shell +./vendor/bin/PHPJava HelloWorld.class +``` + +### Jar ファイルを動かす場合 +```shell +./vendor/bin/PHPJava -m jar HelloWorld.jar +``` + +### ヘルプを表示したい場合 + +```shell +./vendor/bin/PHPJava -h +``` + +## クイックスタート +1) PHPJava をインストールします。 +``` +$ composer require php-java/php-java:dev-master +``` + +2) Java を書きます。 +```java +class HelloWorld +{ + public static void main(String[] args) + { + System.out.println(args[0]); + } +} +``` + +3) Java をコンパイルします。 +``` +$ javac -encoding UTF8 /path/to/HelloWorld.java +``` + +4) main メソッドを呼びます。 + +```php +getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'main', + ["Hello World!"] + ); + +// または、以下のようにファイルパスを指定することも可能です。 +(new JavaClass(new JavaCompiledClass(new FileReader('/path/to/HelloWorld.class')))) + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'main', + ["Hello World!"] + ); +``` + +5) 結果を取得します。 +``` +$ php /path/to/HelloWorld.php +Hello World! +``` + +## Java Archive (*.jar ファイルの実行) + +1) Jar ファイルを作成します。 +``` +$ javac -encoding UTF8 -d build src/* +$ cd build && jar -cvfe ../Test.jar Test * +``` + +2) エントリーポイントを指定するか、またはすでに指定されたエントリーポイントをもとに Jar ファイルを動かします. +2) Execute the jar on PHPJava with either an enrtypoint or your target method. +```php +execute([]); + +// または、 +(new JavaArchive('Test.jar')) + ->getClassByName('Test') + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'main', + [] + ); +``` + +### 静的フィールドの取得または代入 + +- 下記のように取得または代入を行うことが可能です + +```php +getInvoker() + ->getStatic() + ->getFields(); + +// 代入 +$staticFieldAccessor->set('fieldName', 'value'); + +// 取得 +echo $staticFieldAccessor->get('fieldName'); +``` + +### 静的メソッドの呼び出し + +- 下記のように静的メソッドを呼び出します。 + +```php +getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'methodName', + $firstArgument, + $secondArgument, + $thirdArgument, + ... + ); + +// または、メソッドが返り値をもつ場合は、下記のようにして、返り値を変数に代入することが可能です。 +$result = JavaClass::load('HelloWorld') + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'methodWithSomethingReturn', + $firstArgument, + $secondArgument, + $thirdArgument, + ... + ); + +// 返り値を出力します。 +echo $result; +``` + + +### 動的フィールドの取得または代入 +- 動的フィールドを取得または代入したい場合は、`construct` メソッド呼ぶ必要があります。 + +```php +getInvoker()->construct(); + +$dynamicFieldAccessor = $javaClass + ->getInvoker() + ->getDynamic() + ->getFields(); + +// 代入 +$dynamicFieldAccessor->set('fieldName', 'value'); + +// 取得 +echo $dynamicFieldAccessor->get('fieldName'); +``` + +### 動的メソッドの呼び出し + +- 動的メソッドを呼びたい場合動的フィールドのように、`construct` メソッド呼ぶ必要があります。 + +```php +getInvoker() + ->construct() + ->getDynamic() + ->getMethods(); + +$dynamicMethodAccessor + ->call( + 'methodName', + $firstArgument, + $secondArgument, + $thirdArgument, + ... + ); + +// または、メソッドが返り値をもつ場合は、下記のようにして、返り値を変数に代入することが可能です。 +$dynamicMethodAccessor + ->call( + 'methodWithSomethingReturn', + $firstArgument, + $secondArgument, + $thirdArgument, + ... + ); + +// 返り値を出力します。 +echo $result; +``` + +### Java のビルトインパッケージ内のメソッドの呼び出し +Ver. 0.0.8.5 より通常の `JavaClass::load` と同様の呼び出し方法でビルトインパッケージの呼び出しが可能になりました。 +なお、これは `PHP` の `ReflectionClass` を用いてエミュレートされており、静的なメソッドやフィールドも実際には動的に生成されます。 + +下記は `java.lang.Math` の呼び出し例です。 +```php +getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'pow', + 2, + 4 + ); +```` + +上記の結果は `16` となります。 + + +### あいまいなメソッドを PHPJava から呼び出す場合 +- PHP は Java と比べると型がだいぶ曖昧です。そのため、 PHPJava では正確にメソッドを呼び出すための手段をいくつか用意しています。 +- 以下は、 `long` パラメータを受け取るメソッドを呼び出す場合の例です。 + +#### [推奨] パラメータを `\PHPJava\Kernel\Types\Long_ ` にする。 +##### Java +```java +class Test +{ + public static void includingLongTypeParameter(long n) + { + System.out.println(n); + } +} +``` + +##### PHP +```php +getInvoker()->getStatic()->getMethods()->call( + 'includingLongTypeParameter', + new \PHPJava\Kernel\Types\Long_ (1234) +); +``` + +この例は `1234` を返却します。 + +#### strict オプションを `無効` にする +##### PHP +```php + false, + ] +); +``` + +### ラインタイムオプション +- `JavaClass` または、 `JavaArchive` で使用可能なランタイムオプションは下記のとおりです。 + +| オプション名 | 型 | デフォルト値 | 概要 | 対象 | +|:--------|:------|:--------|:------------|:---------| +| entrypoint | string または、 null | null | Jar のエントリーポイントを指定します | JavaArchive | +| max_stack_exceeded | integer | 9999 | オペレーションを最大何回実行できるかを指定します。 | JavaClass | +| max_execution_time | integer | 30 | 最大実行時間を指定します。 | JavaClass | +| strict | boolean | true | このオプションが `true` の場合、 PHPJava はメソッド、変数などを厳格に評価し実行します。 `false` の場合は、曖昧に評価して実行します。. | Both | +| validation.method.arguments_count_only | boolean | false | このオプションが `true` の場合、 クラス解決をして、メソッドを呼び出す際に、引数の数のみを比較します。 | JavaClass | +| operations.enable_trace | boolean | false | このオプションが `true` の場合、 PHPJava はオペレーションの実行ログを記録します。 | JavaClass | +| operations.temporary_code_stream | string | php://memory | 実行用のバイトコードの一時保存先を指定します。 | JavaClass | +| operations.injector.before | callable | null | オペレーション実行前に処理をするトリガーを設定します。 | JavaClass | +| operations.injector.after | callable | null | オペレーション実行後に処理をするトリガーを設定します。 | JavaClass | +| log.level | int | Logger::EMERGENCY | `Monolog` によるログの出力レベルを設定します | Both | +| log.path | string または resource | php://stdout | `Monolog` の出力先を指定します。. | Both | +| dry_run (未実装) | boolean | false | このオプションが `true` の場合、 JavaClass または JavaArchive の構造のチェックのみを行います。 | Both | +| env (未実装) | enum | Environment::EXPERIMENTAL | あなたの実行時環境を設定します。 | Both | + +- JavaClass でオプションを指定する場合は下記のとおりです。 +```php + 12345, + 'validation' => [ + 'method' => [ + 'arguments_count_only' => true, + ], + ], + ] +); +``` + +- `GlobalOptions` を使用して設定する場合は下記のとおりです。 +```php + [ + 'level' => Logger::DEBUG, + ], + 'validation' => [ + 'method' => [ + 'arguments_count_only' => true, + ], + ], +]); + +``` + +### PHPJava の実行結果を出力する + +- 実行中のオペレーションの処理を確認したい場合は下記のとおりにします。 + +```php +getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'main', + ["Hello", 'World'] + ); + +// デバッグトレースを表示します。 +$javaClass->debug(); +``` + +- デバッグトレースを出力します。 + +``` +[method] +public static void main(java.lang.String[]) + +[code] +<0xb2> <0x00> <0x02> <0x2a> <0x03> <0x32> <0xb6> <0x00> <0x03> <0xb2> <0x00> <0x02> <0x2a> <0x04> <0x32> <0xb6> <0x00> <0x03> <0xb2> <0x00> +<0x02> <0x2a> <0x05> <0x32> <0xb6> <0x00> <0x03> <0xb1> + +[executed] + PC | OPCODE | MNEMONIC | OPERANDS | LOCAL STORAGE +---------+--------+----------------------+------------+----------------- + 0 | 0xB2 | getstatic | 0 | 1 + 3 | 0x2A | aload_0 | 1 | 1 + 4 | 0x03 | iconst_0 | 2 | 1 + 5 | 0x32 | aaload | 3 | 1 + 6 | 0xB6 | invokevirtual | 2 | 1 + 9 | 0xB2 | getstatic | 0 | 1 + 12 | 0x2A | aload_0 | 1 | 1 + 13 | 0x04 | iconst_1 | 2 | 1 + 14 | 0x32 | aaload | 3 | 1 + 15 | 0xB6 | invokevirtual | 2 | 1 + 18 | 0xB2 | getstatic | 0 | 1 + 21 | 0x2A | aload_0 | 1 | 1 + 22 | 0x05 | iconst_2 | 2 | 1 + 23 | 0x32 | aaload | 3 | 1 + 24 | 0xB6 | invokevirtual | 2 | 1 + 27 | 0xB1 | return | 0 | 1 +---------+--------+----------------------+------------+----------------- +``` + +- **[method]** は呼ばれたメソッドを表示します。 +- **[code]** は JVM 上の実際のコードを表示します。 +- **[executed]** は実行されたオペレーションコードの一覧を表示します。 + - **PC** はプログラムカウンタを表示します。 + - **OPCODE** はオペレーションコードを表示します。 + - **MNEMONIC** はニーモニックを表示します。. + - **OPERANDS** はオペランドスタック上のアイテムを表示します。 + - **LOCAL STORAGE** はローカルストレージに格納されているアイテムの数を表示します。 + + +## 大きな数字の計算について +- PHP は通常、 Java における long 型や double 型といった大きな値の計算を行うことができません。 + PHPJava ではそれらをカバーするために数値計算用ライブラリを使用します。 + それらは、 下記の Java の型でラップされて使用されます。 + そのため、数値をPHP側で取り扱う場合は、 string 型にキャストすることを推奨します。 + また、通常の 64bit 版 PHP で計算できる範囲については、PHP の四則演算を使用して計算を行います。 + +## Java の型 +- 下記はJava と PHPJava の型の比較表です。 + +| Java | PHPJava | +|:-----|:--------| +| null | null | +| boolean | \PHPJava\Kernel\Types\\Boolean_ (`__toString` を含む) | +| char | \PHPJava\Kernel\Types\\Char_ (`__toString` を含む) | +| byte | \PHPJava\Kernel\Types\\Byte_ (`__toString` を含む) | +| short | \PHPJava\Kernel\Types\\Short_ (`__toString` を含む) | +| int | \PHPJava\Kernel\Types\\Int_ (`__toString` を含む) | +| long | \PHPJava\Kernel\Types\\Long_ (`__toString` を含む) | +| float | \PHPJava\Kernel\Types\\Float_ (`__toString` を含む) | +| double | \PHPJava\Kernel\Types\\Double_ (`__toString` を含む) | + +## Run Kotlin on the PHPJava +## Kotlin を PHPJava で動かす。 +Kotlin を PHPJava で動かしたいですか? +可能ではありますが、現状は試験的な実装となっています。 + +### クイックスタート + +1) Kotlin を書きます。 + +```kotlin +fun main(args: Array) { + println("Hello World!") +} +``` + +2) ランタイム付きでコンパイルします。 +``` +$ kotlinc HelloWorld.kt -include-runtime -d HelloWorld.jar +``` + +3) Jar として実行します。 + +```php +execute([]); +``` + +`Hello World!` と出力されます。 + +## ユニットテスト + +- PHPUnit でテストを動かします。 +``` +$ ./vendor/bin/phpunit tests/Cases +``` + +- コーディングルールをチェックします。 + +``` +$ ./vendor/bin/phpcs --standard=phpcs.xml src +``` + +- すべてのテストを実行します。 + +``` +$ composer run tests +``` + +## 参照 +- [Java Virtual Machine Specification](https://docs.oracle.com/javase/specs/jvms/se11/html/index.html) + +## ライセンス +MIT diff --git a/README.md b/README.md index 5993d264..f60d823d 100644 --- a/README.md +++ b/README.md @@ -1,125 +1,540 @@ -# PHPJava -PHPJavaは、コンパイルされたJavaクラスをPHP上でエミュレートするためのプロジェクトです。 -JavaクラスはもちろんのことJARにも対応しています。(現在実装中) -PHPJavaを使うことにより、Javaのモジュールを活用した拡張性の高いシステムを構築することを可能とします。 +# PHPJava - The JVM on PHP +[![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/dwyl/esta/issues) +![Compatibility](https://img.shields.io/badge/Compatibility-7.2%20and%20greater-green.svg) +[![Build Status](https://travis-ci.org/php-java/php-java.svg?branch=master)](https://travis-ci.org/php-java/php-java) +[![Total Downloads](https://poser.pugx.org/php-java/php-java/downloads)](https://packagist.org/packages/php-java/php-java) +[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT) +

-# どういう仕組みなの? +# What is PHPJava? +PHPJava is an experimental library which emulates JVM (a.k.a. Java Virtual Machine) and compiling intermediate code by PHP 🐘 +And PHPJava reads binary from pre-compiled Java file(s) ☕ +So, PHPJava is **NOT** bridge to Java. This library can be run 100% in PHP. +This project referred to [Java Virtual Machine Specification](https://docs.oracle.com/javase/specs/jvms/se11/html/index.html) documentation at the time we made it. -## 仕組みについて -コンパイルされたJavaクラスのバイトコードをPHPJavaが理解し、 -オペコードに従い逐次処理していきます。 +We are welcoming any contributions to this project 💪 -下記を参考に作りました。 +Contribution guide is here: +- [The Contribution Guide](https://github.com/php-java/php-java/wiki/The-Contribution-Guide) -The Java® Virtual Machine Specification : https://docs.oracle.com/javase/specs/jvms/se8/html/ +## Documents -Java Bytecode Instruction Listings : https://en.wikipedia.org/wiki/Java_bytecode_instruction_listings +## Java Virtual Machine +- [English](./README.md) +- [日本語](./README-ja.md) +## The Intermediate Code Compiler +- [English](./docs/compiler/README.md) +- [日本語](./docs/compiler/README-ja.md) -## 動作について +## The JVM language of PHP syntax +- [English](./docs/jvm-lang/README.md) +- [日本語](./docs/jvm-lang/README-ja.md) + +## DEMO +![DEMO](https://user-images.githubusercontent.com/1282995/58679222-87070880-839d-11e9-8c98-978fdd0bb015.gif) + +## Requirements +- PHP >= 7.2 +- Composer +- ext-zip + +## Run with PHPJava's binary +You can run PHPJava as same as an executable binary. + +### Run Class +```shell +./vendor/bin/PHPJava HelloWorld +``` + +or + +```shell +./vendor/bin/PHPJava HelloWorld.class +``` + +### Run Jar +```shell +./vendor/bin/PHPJava -m jar HelloWorld.jar +``` + +### help + +```shell +./vendor/bin/PHPJava -h +``` + +## Quick start +1) Install PHPJava in your project: +``` +$ composer require php-java/php-java:dev-master +``` + +2) Write Java: +```java +class HelloWorld +{ + public static void main(String[] args) + { + System.out.println(args[0]); + } +} +``` + +3) Compile Java: +``` +$ javac -encoding UTF8 /path/to/HelloWorld.java +``` + +4) Call the main method as follows: -例: ```php getMethodInvoker()->main(array(999, 888)); +use PHPJava\Core\JavaClass; +use PHPJava\Core\Stream\Reader\FileReader; + +JavaClass::load('HelloWorld') + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'main', + ["Hello World!"] + ); + +// Or, you can specify file path as follows. +(new JavaClass(new JavaCompiledClass(new FileReader('/path/to/HelloWorld.class')))) + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'main', + ["Hello World!"] + ); ``` -上記のようにして、 `test.class` のmainメソッドを呼び出すことが可能となります。 -(mainメソッドの引数はString[]を取るため、配列型を渡しています。) +5) Get the result +``` +$ php /path/to/HelloWorld.php +Hello World! +``` + +## Java Archive (Execute *.jar file) -また、Javaのバイトコードではコンストラクタの定義は``とされており、PHPで呼び出すのは -`call_user_func`や`$javaClass->{''}()`等としないと表現が難しいですが、 -PHPJavaでは、下記の方法でJavaのコンストラクタを簡易的に呼び出す方法を提供しています。 +1) Build your Java files into a class: +``` +$ javac -encoding UTF8 -d build src/* +$ cd build && jar -cvfe ../Test.jar Test * +``` +2) Execute the jar on PHPJava with either an enrtypoint or your target method. ```php construct(); +use PHPJava\Core\JavaArchive; + +// You must pass parameters to entrypoint within the `execute` method. +// The `execute` method does not have any default parameters. +(new JavaArchive('Test.jar'))->execute([]); + +// or +(new JavaArchive('Test.jar')) + ->getClassByName('Test') + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'main', + [] + ); +``` + +### Get/Set static fields + +e.g., Set or Get static fields: + +```php +getInvoker() + ->getStatic() + ->getFields(); -// 静的なメソッド -$invoker->main(array(999, 888)); -// または -$javaClass->getMethodInvoker()->main(array(999, 888)); +// Set +$staticFieldAccessor->set('fieldName', 'value'); +// Get +echo $staticFieldAccessor->get('fieldName'); +``` + +### Call a static method + +e.g., Call a static method: -// 動的なメソッド -$invoker->abc(1234); +```php +getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'methodName', + $firstArgument, + $secondArgument, + $thirdArgument, + ... + ); + +// Or, if the called method has a return value, you can store it to a variable. +$result = JavaClass::load('HelloWorld') + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'methodWithSomethingReturn', + $firstArgument, + $secondArgument, + $thirdArgument, + ... + ); + +// Output the $result you want +echo $result; ``` +### Get/Set dynamic fields +If you want to get/set dynamic fields, you need to call the `construct` method on Java by PHPJava. -## メンバの呼び出しについて -PHPJavaではstaticであるかどうかを区別しません。 -メンバの呼び出しは非常に単純で、下記のように実行すると、Javaのクラスで定義されているメンバを取得することが可能です。 +e.g., Call dynamic field: ```php construct(); +use PHPJava\Core\JavaClass; +use PHPJava\Core\Stream\Reader\FileReader; -// Java側で `String stringValue="Hello World"` と定義されていた場合、 -// Hello Worldと出力をします。 -var_dump((string) $invoker->stringValue); +$javaClass = JavaClass::load('HelloWorld'); -// クラスを調べるとjava\lang\Stringという扱いになります。 -var_dump(get_class($invoker->stringValue)); +$javaClass->getInvoker()->construct(); -// プリミティブな型の場合JavaType*が出力されます。 -// Java側で`int intvalue=1111`と定義されていた場合下記の例ではJavaTypeIntが出力されます。 -var_dump(get_class($invoker->intValue)); +$dynamicFieldAccessor = $javaClass + ->getInvoker() + ->getDynamic() + ->getFields(); -// なお、値を取得すると1111となります。 -var_dump((string) $invoker>intValue); -var_dump($invoker->intValue->getValue()); +// Set +$dynamicFieldAccessor->set('fieldName', 'value'); +// Get +echo $dynamicFieldAccessor->get('fieldName'); ``` +### Call a dynamic method +If you want to call a dynamic method (same as a field), you need to call the `construct` method on Java by PHPJava. -## 型の定義について -PHPJavaでは、呼び出すメソッドが静的、あるいは動的かを区別しません。 -これは、PHPで簡易的にJavaのメソッドを呼び出すためです。 +e.g., Call dynamic method: -また、PHPは動的型付けのため、PHPJavaではJavaへの引数においても厳密に比較しません。 -したがって、Javaへ引数を渡す際には暗黙の型変換が行われます。 +```php +getInvoker() + ->construct() + ->getDynamic() + ->getMethods(); + +$dynamicMethodAccessor + ->call( + 'methodName', + $firstArgument, + $secondArgument, + $thirdArgument, + ... + ); + +// Or, if the called method has a return value, you can store it to a variable. +$dynamicMethodAccessor + ->call( + 'methodWithSomethingReturn', + $firstArgument, + $secondArgument, + $thirdArgument, + ... + ); + +// Output the $result you want +echo $result; +``` -また、PHPのint型の最大値が32bit及び64bit環境により異なることから、 -巨大な数字の計算時には`bcmath関数`または `gmp関数` を用いて計算を行います。 -(優先的に`bcmath関数`を使用するようにしており利用できなければ、`gmp関数`を使用するようにしています。) +### Call a method in a built-in package on Java +PHPJava can call a built-in package in the same way as `JavaClass::load` after the version 0.0.8.5. +This feature is emulated by `ReflectionClass` on `PHP` and any static methods/fields will dynamically be generated in fact. -bcmath関数: http://php.net/manual/ja/book.bc.php +e.g.) To Call `java.lang.Math` is below. +```php +getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'pow', + 2, + 4 + ); +```` + +The example will return `16`. + +### Call ambiguous methods in Java from PHP +- In PHP, types are more ambiguous than Java. +- For example, you may want to call a method that accepts a `long` parameter in Java from PHP. +In this case, you can call that method as follows: + +#### e.g., [Recommended] Wrap with `\PHPJava\Kernel\Types\Long_ `. +##### In Java +```java +class Test +{ + public static void includingLongTypeParameter(long n) + { + System.out.println(n); + } +} +``` -gmp関数: http://php.net/manual/ja/ref.gmp.php +##### In PHP +```php +getInvoker()->getStatic()->getMethods()->call( + 'includingLongTypeParameter', + new \PHPJava\Kernel\Types\Long_ (1234) +); +``` + +The example will return `1234`. + +#### e.g., Set `false` to strict mode option. +##### In PHP +```php + false, + ] +); +``` + +### Runtime options +- Available options on `JavaClass` or `JavaArchive`: + +| Options | Value | Default | Description | Targeted | +|:--------|:------|:--------|:------------|:---------| +| entrypoint | string or null | null | The entrypoint in JAR. | JavaArchive | +| max_stack_exceeded | integer | 9999 | Execute more than the specified number of times be stopped the operation. | JavaClass | +| max_execution_time | integer | 30 | Maximum execution time. | JavaClass | +| strict | boolean | true | When `true`, PHPJava calls a method, variables, and so on strictly; otherwise, it calls them ambiguously. | Both | +| validation.method.arguments_count_only | boolean | false | When `true`, ClassResolver validates arguments by their number only. | JavaClass | +| operations.enable_trace | boolean | false | When `true`, PHPJava stores the operation history. | JavaClass | +| operations.temporary_code_stream | string | php://memory | Operation code will be output to temporary stream. Change this if your code is heavy so you'll be happy. | JavaClass | +| operations.injector.before | callable | null | Inject an executor before executing an operation. | JavaClass | +| operations.injector.after | callable | null | Inject an executor after executing an operation. | JavaClass | +| log.level | int | Logger::EMERGENCY | The output level of `Monolog`. | Both | +| log.path | string or resource | php://stdout | The output destination of `Monolog`. | Both | +| dry_run (Not Implemented) | boolean | false | Checking JavaClass/JavaArchive structure only. When `true`, PHPJava runs in dry-run mode. | Both | +| env (Not Implemented) | enum | Environment::EXPERIMENTAL | Your environment. | Both | + +- Example of JavaClass: +```php + 12345, + 'validation' => [ + 'method' => [ + 'arguments_count_only' => true, + ], + ], + ] +); +``` + +- Example of GlobalOptions +```php + [ + 'level' => Logger::DEBUG, + ], + 'validation' => [ + 'method' => [ + 'arguments_count_only' => true, + ], + ], +]); + +``` + +### Output PHPJava operations + +- Output debug trace if you want to see operation log: + +```php +getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'main', + ["Hello", 'World'] + ); + +// Show debug traces. +$javaClass->debug(); +``` + +- Output debug trace: + +``` +[method] +public static void main(java.lang.String[]) + +[code] +<0xb2> <0x00> <0x02> <0x2a> <0x03> <0x32> <0xb6> <0x00> <0x03> <0xb2> <0x00> <0x02> <0x2a> <0x04> <0x32> <0xb6> <0x00> <0x03> <0xb2> <0x00> +<0x02> <0x2a> <0x05> <0x32> <0xb6> <0x00> <0x03> <0xb1> + +[executed] + PC | OPCODE | MNEMONIC | OPERANDS | LOCAL STORAGE +---------+--------+----------------------+------------+----------------- + 0 | 0xB2 | getstatic | 0 | 1 + 3 | 0x2A | aload_0 | 1 | 1 + 4 | 0x03 | iconst_0 | 2 | 1 + 5 | 0x32 | aaload | 3 | 1 + 6 | 0xB6 | invokevirtual | 2 | 1 + 9 | 0xB2 | getstatic | 0 | 1 + 12 | 0x2A | aload_0 | 1 | 1 + 13 | 0x04 | iconst_1 | 2 | 1 + 14 | 0x32 | aaload | 3 | 1 + 15 | 0xB6 | invokevirtual | 2 | 1 + 18 | 0xB2 | getstatic | 0 | 1 + 21 | 0x2A | aload_0 | 1 | 1 + 22 | 0x05 | iconst_2 | 2 | 1 + 23 | 0x32 | aaload | 3 | 1 + 24 | 0xB6 | invokevirtual | 2 | 1 + 27 | 0xB1 | return | 0 | 1 +---------+--------+----------------------+------------+----------------- +``` + +- **[method]** shows the called method. +- **[code]** shows the JVM's real programs. +- **[executed]** shows the executed programs. + - **PC** shows the Program Counter. + - **OPCODE** shows the Operation Codes. + - **MNEMONIC** shows the names of the Operation Codes. + - **OPERANDS** shows the stacked items on memory. + - **LOCAL STORAGE** shows the stacked items on a method. + +## About big number calculation +- In normally, PHP cannot calculate big numbers as such as `long` and `double` types. + But, PHPJava uses external `Math` library for covering above problems. + And, PHPJava to use Java's type as below comparison table. + Therefore, we recommend to cast them to `string` on PHPJava. + And, if it can be calculated with 64-bitPHP, PHPJava uses PHP's arithmetic operations. + +## Types of Java +- The comparison table of Java and PHPJava is shown below: + +| Java | PHPJava | +|:-----|:--------| +| null | null | +| boolean | \PHPJava\Kernel\Types\\Boolean_ (including `__toString`) | +| char | \PHPJava\Kernel\Types\\Char_ (including `__toString`) | +| byte | \PHPJava\Kernel\Types\\Byte_ (including `__toString`) | +| short | \PHPJava\Kernel\Types\\Short_ (including `__toString`) | +| int | \PHPJava\Kernel\Types\\Int_ (including `__toString`) | +| long | \PHPJava\Kernel\Types\\Long_ (including `__toString`) | +| float | \PHPJava\Kernel\Types\\Float_ (including `__toString`) | +| double | \PHPJava\Kernel\Types\\Double_ (including `__toString`) | + +## Run Kotlin on the PHPJava +Do you wanna run Kotlin on the PHPJava? Are you serious? +Haha, yes, you can, but this feature is currently experimental. + +### Quick Start + +1) Write Kotlin: + +```kotlin +fun main(args: Array) { + println("Hello World!") +} +``` + +2) Compile Kotlin: +``` +$ kotlinc HelloWorld.kt -include-runtime -d HelloWorld.jar +``` + +3) Execute JAR: -なお、下記はJavaのメソッドを実行した際に返却される型の情報です。 -null以外の全ての値は`JavaType*`オブジェクトとして適切に扱われます。 +```php +execute([]); +``` -## エミュレーションについて +You'll get the result: `Hello World!`. -PHPJavaではJavaにデフォルトでビルトインされている機能は一切使用していません。 -つまり、PHPJava単体で、Javaをエミュレーションすることが可能となります。 +## Run unit tests +- To run a PHPUnit test: +``` +$ ./vendor/bin/phpunit tests/Cases +``` +- To run PHP Coding standards test: -# Javaにおけるスレッドの扱いについて -Javaにはマルチスレッドを扱うための機能が備わっていますが、PHP 5.6現在ではそのような機能はありません。 -Javaにおけるスレッドの扱いをPHPJavaで表現するには、pthreads(http://php.net/manual/ja/book.pthreads.php) の導入を必要とします。 +``` +$ ./vendor/bin/phpcs --standard=phpcs.xml src +``` + +- To run all tests: + +``` +$ composer run tests +``` -# TODO +## Reference +- [Java Virtual Machine Specification](https://docs.oracle.com/javase/specs/jvms/se11/html/index.html) -- 全ニーモニックへの対応 -- JARへの対応 -- アノテーションへの対応 -- PHPDocの追加 \ No newline at end of file +## License +MIT diff --git a/Test.class b/Test.class deleted file mode 100644 index 7e24ece7..00000000 Binary files a/Test.class and /dev/null differ diff --git a/composer.json b/composer.json new file mode 100644 index 00000000..2ad8dedc --- /dev/null +++ b/composer.json @@ -0,0 +1,61 @@ +{ + "name": "php-java/php-java", + "description": "Implement JVM by PHP", + "type": "library", + "license": "MIT", + "minimum-stability": "dev", + "authors": [ + { + "name": "memory" + } + ], + "bin": [ + "PHPJava" + ], + "require": { + "php": ">=7.3", + "ext-zip": "*", + "monolog/monolog": "^2.9.2", + "gabrielelana/byte-units": "^0.5.0", + "symfony/console": "^5.2", + "phpdocumentor/reflection-docblock": "^5.2", + "brick/math": "^0.9.1", + "nikic/php-parser": "^4.10" + }, + "autoload": { + "psr-4": { + "PHPJava\\": "src/", + "PHPJava\\Console\\": "console/" + } + }, + "autoload-dev": { + "psr-4": { + "PHPJava\\Tests\\": "tests/" + } + }, + "require-dev": { + "phpunit/phpunit": "^9.5", + "squizlabs/php_codesniffer": "^3.5", + "friendsofphp/php-cs-fixer": "^2.17", + "brainmaestro/composer-git-hooks": "^2.8" + }, + "scripts": { + "test": "phpunit", + "cs": "phpcs -n --standard=phpcs.xml src", + "fix": "php-cs-fixer fix", + "tests": [ + "@cs", + "@test" + ], + "post-install-cmd": "[ \"$COMPOSER_DEV_MODE\" = 1 ] && cghooks add --ignore-lock", + "post-update-cmd": "[ \"$COMPOSER_DEV_MODE\" = 1 ] && cghooks update" + }, + "extra": { + "hooks": { + "pre-commit": [ + "git diff-index --cached --name-only HEAD | vendor/bin/php-cs-fixer fix", + "git update-index --again" + ] + } + } +} diff --git a/console/Command.php b/console/Command.php new file mode 100755 index 00000000..cfc01285 --- /dev/null +++ b/console/Command.php @@ -0,0 +1,22 @@ +add($runCommand); + $application->setDefaultCommand( + $runCommand->getName(), + true + ); + $application->run(); + } +} diff --git a/console/Commands/JVM/RunCommand.php b/console/Commands/JVM/RunCommand.php new file mode 100644 index 00000000..46a4266d --- /dev/null +++ b/console/Commands/JVM/RunCommand.php @@ -0,0 +1,92 @@ +addArgument( + 'file', + InputArgument::REQUIRED, + 'Specify to run [jar|class] file.' + ) + ->addArgument( + 'parameters', + InputArgument::OPTIONAL | InputArgument::IS_ARRAY, + 'Specify parameters to pass for entrypoint.' + ) + ->addOption( + 'settings', + 's', + InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, + 'Set option settings.' + ) + ->addOption( + 'mode', + 'm', + InputOption::VALUE_OPTIONAL, + 'Set run mode [jar|class]. Default is class.' + ); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $settings = $input->getOption('settings') ?? []; + $mode = strtolower($input->getOption('mode') ?? 'class'); + $file = $input->getArgument('file'); + $parameters = $input->getArgument('parameters'); + + // Set global options + GlobalOptions::set($settings); + + if ($mode === 'jar') { + return $this->runJar($file, $parameters); + } elseif ($mode === 'class') { + return $this->runClass($file, $parameters); + } + + $output->writeln( + 'Unable to run `' . $mode . '` mode.' + ); + } + + private function runJar(string $file, array $parameters) + { + $jar = new JavaArchive($file); + $jar->execute( + $parameters + ); + } + + private function runClass(string $file, array $parameters) + { + $class = new JavaClass( + new JavaCompiledClass( + new FileReader($file) + ) + ); + $class + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'main', + $parameters + ); + } +} diff --git a/docs/compiler/README-ja.md b/docs/compiler/README-ja.md new file mode 100644 index 00000000..5b711b68 --- /dev/null +++ b/docs/compiler/README-ja.md @@ -0,0 +1,581 @@ +# PHPJava 中間コードコンパイラ +# What is PHPJava Intermediate Code Compiler? +PHPJava 中間コードコンパイラとは、いくつか提供している API を使用して JVM 上で動く中間コードを生成するための PHPJava のもう一つの機能です。 +これは、与えられた引数をもとに中間コードを生成し、それ自身をセルフホスティングすることも可能です。 +また、この機能はいくつか実験的な要素を含んでいます。 + +# DEMO +![DEMO](../img/php_compiler.gif) + +# Get started +`Hello World` を出力する一例です。 + +```php +addString('Hello PHPJava Compiler!') + ->addClass(Object_::class) + ->addClass('HelloWorld') + ->addClass(System::class) + ->addClass(PrintStream::class) + ->addFieldref( + System::class, + 'out', + (new Descriptor()) + ->addArgument(PrintStream::class) + ->make() + ) + ->addMethodref( + PrintStream::class, + 'println', + (new Descriptor()) + ->addArgument(String_::class) + ->setReturn(Void_::class) + ->make() + ) + ->addNameAndType( + 'main', + (new Descriptor()) + ->addArgument(String_::class, 1) + ->setReturn(Void_::class) + ->make() + ); + +$compiler = new Compiler( + (new ClassFileStructure()) + ->setMinorVersion($minorVersion) + ->setMajorVersion($majorVersion) + ->setAccessFlags( + (new ClassAccessFlag()) + ->enableSuper() + ->make() + ) + ->setThisClass($enhancedConstantPool->findClass('HelloWorld')) + ->setSuperClass($enhancedConstantPool->findClass(Object_::class)) + ->setMethods( + (new Methods()) + ->add( + (new Method( + (new MethodAccessFlag()) + ->enablePublic() + ->enableStatic() + ->make(), + $enhancedConstantPool->findUtf8('main'), + $enhancedConstantPool->findUtf8( + (new Descriptor()) + ->addArgument(String_::class, 1) + ->setReturn(Void_::class) + ->make() + ) + )) + ->setAttributes( + (new Attributes()) + ->add( + (new Code($enhancedConstantPool->findUtf8('Code'))) + ->setConstantPool($constantPool) + ->setConstantPoolFinder($finder) + ->setCode( + [ + \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_getstatic, + Operand::factory( + Uint16::class, + $enhancedConstantPool->findField( + System::class, + 'out', + (new Descriptor()) + ->addArgument(PrintStream::class) + ->make() + ) + ) + ), + \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_ldc, + Operand::factory( + Uint8::class, + $enhancedConstantPool->findString('Hello PHPJava Compiler!') + ) + ), + \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_invokevirtual, + Operand::factory( + Uint16::class, + $enhancedConstantPool->findMethod( + PrintStream::class, + 'println', + (new Descriptor()) + ->addArgument(String_::class) + ->setReturn(Void_::class) + ->make() + ) + ) + ), + \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_return + ) + ] + ) + ->beginPreparation() + ) + ->toArray() + ) + ) + ->toArray() + ) + ->setConstantPool($constantPool->toArray()) +); + +$compiler->compile(fopen('HelloWorld.class', 'w+')); +``` + +`HelloWorld.class` が生成されるので、 `java` コマンドで実行します。 +``` +$ java HelloWorld +``` + +結果は `Hello World!` が出力されます。 + +*注意: `ConstantPool:toArray` メソッドは ConstantPool のエントリを決定します。* +*そのため、メソッドの追加や属性の追加など、それぞれの処理は ConstantPool が決定する前に行わなければなりません。* +*これらの処理は必要になったクラスやメソッドなどを解決する機能が備わっており、ConstantPool のエントリが決定してしまうと新しいエントリが追加できなくなるためです。* +*簡単な解決方法としては `ConstantPool:toArray` を一番最後に呼び出すことにより解決できます。* + +# How to use? + +## ClassFileStructure Builder + +`ClassFileStructure` Builder とは、クラスファイルの構造を定義するためのビルダークラスです。 +`java` 実行時に必要な値を設定することによりクラスファイルの構造を生成する手助けをします。 +`ClassFileStructure` Builder は下記の API をコールし、設定する必要があります。 + +| 必須 | メソッド名 | 概要 | +| --- | ------- | ---- | +| Required | `setMinorVersion(int)` | クラスファイルのマイナーバージョンを設定します。この値は `SDKVersionResolver::resolveByVersion` を使用して JDK のバージョンから逆引きで取得することが可能です。 | +| Required | `setMajorVersion(int)` | クラスファイルのメジャーバージョンを設定します。この値は `SDKVersionResolver::resolveByVersion` を使用して JDK のバージョンから逆引きで取得することが可能です。 | +| Required | `setConstantPool(EntryInterface[])` | Constant Pool に登録するエントリーを設定します。 +| Required | `setAccessFlags(int)` | クラスに対するアクセス修飾子を設定します。この値は `AccessFlag Signature Builder` を用いて生成するか直接数字を指定します。 | +| Required | `setThisClass(FinderResultInterface)` | どのクラスにマッピングするかを設定します。これは Constant Pool からエントリーを探索する際に返される `ConstantPoolFinder` を用いた検索結果のオブジェクトを渡します。 | +| Required | `setSuperClass(FinderResultInterface)` | どの親クラスにマッピングするかを設定します。これは Constant Pool からエントリーを探索する際に返される `ConstantPoolFinder` を用いた検索結果のオブジェクトを渡します。 | +| | `setInterfaces(EntryInterface[])` | クラスに実装されているインタフェースのエントリーを定義します。これは現時点では **未実装** です。 | +| | `setFields(EntryInterface[])` | クラスに実装されているフィールドのエントリーを定義します。これは現時点では **未実装** です。| +| | `setMethods(EntryInterface[])` | クラスに定義されているメソッドのエントリーを定義します。定義するメソッドは `EntryCollection` 上で `Method` エントリーを追加していく必要があります。 | +| | `setAttributes(EntryInterface[])` | クラスに定義されている属性のエントリーを定義します。 | + +## ConstantPoolFinder +`ConstantPoolFinder` とは、Constant Pool からエントリーを探索するための機能です。これにより、登録されたエントリーの位置を考える必要がなくなり、 +好きなタイミングで Constant Pool のエントリーを参照することができます。 + +この機能は主に下記の様に使用します。 + +```php +add(new Utf8Info('Hello World!')); + +try { + // 検索を行います。 + var_dump( + $finder->find(Utf8Info::class, 'Hello World!') + ); + + // 即時実行の場合は下記のようにします。 + var_dump( + $finder->find(Utf8Info::class, 'Hello World!') + ->getResult() + ); +} catch (FinderException $e) { + // 実行した際に見つからなかった場合 + printf("残念!エントリーは見つかりませんでした。"); +} +``` + +また、 `ConstantPoolFinder` は遅延評価であるため `find` をコールした直後に**即時実行**しません。 +コンパイル時に初めて検索が行われます。 + +下記のエントリーの検索が可能です。 + + +| エントリー名 | 引数の数 | 例 | +| --------------- | :---: | --- | +| Utf8Info | 1 | `$finder->find(Utf8Info::class, 'Hello World!')` | +| ClassInfo | 1 | `$finder->find(ClassInfo::class, 'HelloWorld')` | +| StringInfo | 1 | `$finder->find(StringInfo::class, 'Hello World!')` | +| NameAndTypeInfo | 2 | `$finder->find(NameAndTypeInfo::class, '', (new Descriptor())->setReturn(Void_::class)->make())` | +| MethodrefInfo | 3 | `$finder->find(MethodrefInfo::class, 'java/io/PrintStream', 'println', (new Descriptor())->addArgument(String_::class)->setReturn(Void_::class)->make())` | +| FieldrefInfo | 3 | `$finder->find(FieldrefInfo::class, 'java/lang/System', 'out', (new Descriptor())->addArgument(PrintStream::class)->make())` | + + +これを即時実行したい場合は `find` に続けて `getResult()` メソッドをコールする必要があります。 +なお、検索結果は 2 回目以降はキャッシュが使用されるため、常に最新のエントリーの情報が知りたい場合は `enableCache(false)` をコールして +キャッシュを無効にするか `clearCaches()` をコールして、キャッシュを削除する必要があります。 + +## AccessFlag Signature Builder +`AccessFlag Signature Builder` はクラスやメソッドへのアクセス修飾子を視覚的にわかりやすく生成する手助けをするためのビルダークラスです。 +これを使うことにより、クラスファイルを生成する際に数字の組み合わせを気にせず定義することが可能になります。 + +AccessFlag Signature Builder は下記のように使用することが可能です。 + +### クラスの場合 + +```php +enablePublic() + // 親クラスであるという修飾子を付けます。 + ->enableSuper() + // 与えられた情報から値を返します。 + ->make(); + +// 上記は下記と同一です。 +var_dump( + \PHPJava\Kernel\Maps\ClassAccessFlag::ACC_PUBLIC + | \PHPJava\Kernel\Maps\ClassAccessFlag::ACC_SUPER +); +``` + +また、使用できる API は下記のとおりです。 + +| アクセス修飾子 | +| --------- | +| enablePublic | +| enableSuper | +| enableFinal | +| enableInterface | +| enableAbstract | +| enableSynthetic | +| enableEnum | +| enableModule | + +### メソッドの場合 + +```php +enablePublic() + // 静的メソッドであるという修飾子を付けます。 + ->enableStatic() + // 与えられた情報から値を返します。 + ->make(); + +// 上記は下記と同一です。 +var_dump( + \PHPJava\Kernel\Maps\MethodAccessFlag::ACC_PUBLIC + | \PHPJava\Kernel\Maps\MethodAccessFlag::ACC_STATIC +); +``` + +また、使用できる API は下記のとおりです。 + +| アクセス修飾子 | +| --------- | +| enablePublic | +| enableStatic | +| enablePrivate | +| enableProtected | +| enableFinal | +| enableSynchronized | +| enableBridge | +| enableVarArgs | +| enableNative | +| enableAbstract | +| enableStrict | +| enableSynthetic | + +## Descriptor Signature Builder +`Descriptor Signature Builder` とは、メソッドの引数及び返り値の中間コード向けの書式の生成を手助けするためのビルダークラスです。 +`Java` は例えば `public static void main(String[] args)` がコンパイルされると `String[] args` と `void` は `([Ljava/lang/String;)V` のように中間コードに解釈されます。 +しかし、これを人間が相互に読み直すのは非効率であるため、このビルダークラスを用いてその課題を解決します。 + +`Descriptor Signature Builder` は下記のように使用します。 + +```php +addArgument( + // 第一引数は java.lang.String + String_::class, + // 配列の深さを指定する。デフォルトは 0 + 1 + ) + // 返り値をセットする + ->setReturn( + // 返り値は void 型 + Void_::class + ) + ->make(); + +// 上記は下記と同じです。 +var_dump( + '([Ljava/lang/String;)V' +); +``` + +## Interface Entry Builder +_この機能はまだ実装されていません。_ + +## Fields Entry Builder +_この機能はまだ実装されていません。_ + +## Method Entry Builder +`Method Entry Builder` とは、 `EntryCollection` である `Methods` にエントリーを追加していく際に、エントリーの生成を手助けするためのビルダークラスです。 +これを使用することにより、クラスファイルへのメソッドの追加が容易になります。 + +下記のように使用します。 + +```php +setMethods( + (new Methods()) + // Methods コレクションにメソッドを追加する + ->add( + // JVM Spec の method_info attribute に則ってメソッドの構造を定義する + (new Method( + // メソッドのアクセス修飾子を定義する + (new \PHPJava\Compiler\Builder\Signatures\MethodAccessFlag()) + ->enablePublic() + ->enableStatic() + ->make(), + // メソッド名を Constant Pool から探す + $finder->find( + Utf8Info::class, + 'main' + ), + // 引数及び返り値の情報を Constant Pool から探す + $finder->find( + Utf8Info::class, + (new Descriptor()) + ->addArgument(String_::class, 1) + ->setReturn(Void_::class) + ->make() + ) + ) + ->setAttributes( + // ...省略 + ) + ) + ->toArray() + ) +); +``` + +## Code Attribution Builder +`Code Attribution Builder` は `Attribute` Builder を継承したビルダークラスです。 +通常 `Attribute` Builder は [Java Virtual Machine Specification](https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.7) の性質から、バイナリ文字列のみを渡すことを想定して設計されていますが、 +バイナリ文字列を書くのは人類には早すぎるため、よっぽどのバイナリ文字列が好きで人間を辞めたなにかではない限り、手動で生成するのは困難であるため、 `Code Attribution Builder` が `Attribute` に渡すための文字列を生成する手助けをします。 + +`Code Attribution Builder` は下記のように使用します。 + +```php +find(Utf8Info::class, 'Code'))) + ->setMaxStacks(0) + ->setMaxLocals(0) + // return のみを設定する + ->setCode( + pack('C', OpCode::_return) + ) + ->getValue(); + +// これは下記と同様です。 +var_dump( + "" + // attribute_name_index + . pack('n', $finder->find(Utf8Info::class, 'Code')->getResult()->getEntryIndex()) // + // attribute_length + . pack('N', 2 + 2 + 4 + 1 + 2 + 2) // (u2 + u2 + u4 + u1 + u2 + u2) + // max_stack + . "\x00\x00" + // max_locals + . "\x00\x00" + // code_length + . "\x00\x00\x00\x01" + // code + . "\xB1" + // exception_table_length + . "\x00\x00" + // attributes_count + . "\x00\x00" +); +``` + +`Code Attribution Builder` が提供している API は下記のとおりです。 + + +| メソッド名 | 概要 | +| ------- | --- | +| `setCode(string)` | オペレーションコード及びオペランドを定義します。 | +| `setExceptionTable(ExceptionTable[])` | 例外が発生するテーブル情報を登録します。これは現在**未実装**です。 | +| `setAttributes(Attributes[])` | Code Attribute に付属する属性情報を追加します。 | +| `getValue()` | 指定した値を用いて Code Attribute の形式のバイナリ文字列を生成します。 | + + +## Operation Code Builder +`Operation Code Builder` は、 `Code Attribution Builder` の `setCode` に対してのオペレーションコード及びオペランドを生成する手助けをするためのビルダークラスです。 +このクラスを使用することにより、実際のバイナリ文字列を考えずにオペレーションコードを生成することが可能になります。 + +下記のように使用します。 + +```php +add( + OpCode::_getstatic, + [ + // Fieldref の位置を渡す + [ + Uint16::class, + $finder->find( + FieldrefInfo::class, + 'java/lang/System', + 'out', + (new Descriptor()) + ->addArgument(PrintStream::class) + ->make() + ), + ], + ] + ) + // ldc コマンドを設定する + ->add( + OpCode::_ldc, + [ + // StringInfo の Constant Pool の位置を渡す + [ + Uint8::class, + $finder->find( + StringInfo::class, + 'Hello World!' + ), + ], + ] + ) + // invokevirtual コマンドを設定する + ->add( + OpCode::_invokevirtual, + [ + // 実行するメソッドの Constant Pool の位置を渡す + [ + Uint16::class, + $finder->find( + MethodrefInfo::class, + 'java/io/PrintStream', + 'println', + (new Descriptor()) + ->addArgument(String_::class) + ->setReturn(Void_::class) + ->make() + ), + ], + ] + ) + // return コマンドを設定する + ->add(OpCode::_return); +``` + +オペレーションコードには引数を指定可能であり、これは Java Virtual Machine における命令の仕様です。 +また、例のコードで登場する `Uint8` や `Uint16` は渡すパラメータ長を定義しており、下記の意味を持ちます。 + +| クラス | 意味 | +| ---- | --- | +| PHPJava\Compiler\Builder\Types\Uint8 | 1 バイトの意味であることを示しています。 | +| PHPJava\Compiler\Builder\Types\Uint16 | 2 バイトの意味であることを示しています。つまり short 型と同一です。 | +| PHPJava\Compiler\Builder\Types\Uint32 | 4 バイトの意味であることを示しています。つまり int 型と同一です。 | +| PHPJava\Compiler\Builder\Types\Uint64 | 8 バイトの意味であることを示しています。つまり long 型と同一です。 | + +[Java Virtual Machine](https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-6.html) を確認することでそれぞれのパラメータ長を知ることができます。 \ No newline at end of file diff --git a/docs/compiler/README.md b/docs/compiler/README.md new file mode 100644 index 00000000..e1279b58 --- /dev/null +++ b/docs/compiler/README.md @@ -0,0 +1,582 @@ +# PHPJava Intermediate Code Compiler +# What is PHPJava Intermediate Code Compiler? +The PHPJava Intermediate Code Compiler is another feature of PHPJava that generates intermediate code for JVM by using APIs. +This is capable of self-hosting generated intermediate codes on PHPJava. Note that this is an experimental implementation. + +# DEMO +![DEMO](../img/php_compiler.gif) + + +# Get started +The following is an example that outputs `Hello World`. + +```php +addString('Hello PHPJava Compiler!') + ->addClass(Object_::class) + ->addClass($className) + ->addClass(System::class) + ->addClass(PrintStream::class) + ->addFieldref( + System::class, + 'out', + (new Descriptor()) + ->addArgument(PrintStream::class) + ->make() + ) + ->addMethodref( + PrintStream::class, + 'println', + (new Descriptor()) + ->addArgument(String_::class) + ->setReturn(Void_::class) + ->make() + ) + ->addNameAndType( + 'main', + (new Descriptor()) + ->addArgument(String_::class, 1) + ->setReturn(Void_::class) + ->make() + ); + +$compiler = new Compiler( + (new ClassFileStructure()) + ->setMinorVersion($minorVersion) + ->setMajorVersion($majorVersion) + ->setAccessFlags( + (new ClassAccessFlag()) + ->enableSuper() + ->make() + ) + ->setThisClass($enhancedConstantPool->findClass($className)) + ->setSuperClass($enhancedConstantPool->findClass(Object_::class)) + ->setMethods( + (new Methods()) + ->add( + (new Method( + (new MethodAccessFlag()) + ->enablePublic() + ->enableStatic() + ->make(), + $className, + 'main', + (new Descriptor()) + ->addArgument(String_::class, 1) + ->setReturn(Void_::class) + ->make() + )) + ->setConstantPool($constantPool) + ->setConstantPoolFinder($finder) + ->setAttributes( + (new Attributes()) + ->add( + (new Code($enhancedConstantPool->findUtf8('Code'))) + ->setConstantPool($constantPool) + ->setConstantPoolFinder($finder) + ->setCode( + [ + \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_getstatic, + Operand::factory( + Uint16::class, + $enhancedConstantPool->findField( + System::class, + 'out', + (new Descriptor()) + ->addArgument(PrintStream::class) + ->make() + ) + ) + ), + \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_ldc, + Operand::factory( + Uint8::class, + $enhancedConstantPool->findString('Hello PHPJava Compiler!') + ) + ), + \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_invokevirtual, + Operand::factory( + Uint16::class, + $enhancedConstantPool->findMethod( + PrintStream::class, + 'println', + (new Descriptor()) + ->addArgument(String_::class) + ->setReturn(Void_::class) + ->make() + ) + ) + ), + \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_return + ) + ] + ) + ->beginPreparation() + ) + ->toArray() + ) + ) + ->toArray() + ) + ->setConstantPool($constantPool->toArray()) +); + +$compiler->compile(fopen($className . '.class', 'w+')); +``` + +Run `java` command after the `HelloWorld.class` file is generated. +``` +$ java HelloWorld +``` + +You'll get `Hello World!` + +*NOTICE: The `ConstantPool:toArray` decides Constant Pool entries.* +*For that reason, You should to add method, attribute and anymore before deciding the Constant Pool.* +*Because it has entry resolver as such as class name, method name and anymore, You cannot to add an entry if the Constant Pool entries are decided.* +*As a simple solution, you call `ConstantPool:toArray` at last.* + +# How to use? + +## ClassFileStructure Builder + +`ClassFileStructure` Builder is a builder class that defines the structure of a class file. +This class helps the definition of a class file structure by setting the required parameters and values when running `java` command. + +`ClassFileStructure` Builder requires call and set the APIs below. + +| Required? | Method name | Summary | +| --- | ------- | ---- | +| Required | `setMinorVersion(int)` | Set the minor version of the class file structure. This value can be obtained by using `SDKVersionResolver::resolveByVersion`. | +| Required | `setMajorVersion(int)` | Set the major version of the class file structure. This value can be obtained by using `SDKVersionResolver::resolveByVersion`. | +| Required | `setConstantPool(EntryInterface[])` | Set the entries of the Constant Pool. | +| Required | `setAccessFlags(int)` | Set an AccessFlag for the current class. You need to either specify the value generated by `AccessFlag Signature Builder` or directly set the integer number. | +| Required | `setThisClass(FinderResultInterface)` | Set to which class it maps from the Constant Pool. Pass the result object returned by `ConstantPoolFinder`, which finds an entry on the ConstantPool. | +| Required | `setSuperClass(FinderResultInterface)` | Set mapping super class from the Constant Pool. Pass the result object returned by `ConstantPoolFinder`, which finds an entry on the ConstantPool. | +| | `setInterfaces(EntryInterface[])` | Set the interface entries of the class file structure. This feature is **not yet implemented**. | +| | `setFields(EntryInterface[])` | Set the field entries of the class file structure. This feature is **not yet implemented**. | +| | `setMethods(EntryInterface[])` | Set the method entries of the class file structure. The defining method is required to add a `Method` entry on an `EntryCollection`. | +| | `setAttributes(EntryInterface[])` | Set the attribute entries of the class file structure. | + +## ConstantPoolFinder +`ConstantPoolFinder` is a finder that looks for an entry from the ConstantPool. +By this approach, you do not need to think of the entry index in the Constant Pool and you can refer to any entries from the Constant Pool at any time. + +An example below uses this feature: +```php +add(new Utf8Info('Hello World!')); + +try { + // Start to find + var_dump( + $finder->find(Utf8Info::class, 'Hello World!') + ); + + // In case you need to run now. + var_dump( + $finder->find(Utf8Info::class, 'Hello World!') + ->getResult() + ); +} catch (FinderException $e) { + // Not found + printf("Unfortunately, an entry not found"); +} +``` + +`ConstantPoolFinder` is **not executed immediately** after you call `find` method because it is a lazy evaluation. +It instead starts to find one when compiled. + +You can find by the entries shown below: + +| Entry name | Argument size | Example | +| --------------- | :---: | --- | +| Utf8Info | 1 | `$finder->find(Utf8Info::class, 'Hello World!')` | +| ClassInfo | 1 | `$finder->find(ClassInfo::class, 'HelloWorld')` | +| StringInfo | 1 | `$finder->find(StringInfo::class, 'Hello World!')` | +| NameAndTypeInfo | 2 | `$finder->find(NameAndTypeInfo::class, '', (new Descriptor())->setReturn(Void_::class)->make())` | +| MethodrefInfo | 3 | `$finder->find(MethodrefInfo::class, 'java/io/PrintStream', 'println', (new Descriptor())->addArgument(String_::class)->setReturn(Void_::class)->make())` | +| FieldrefInfo | 3 | `$finder->find(FieldrefInfo::class, 'java/lang/System', 'out', (new Descriptor())->addArgument(PrintStream::class)->make())` | + + +If you need to execute immediately, you are required to call the `getResult` method after calling the `find` method. +Furthermore, as the search results will be cached, you need to either disable cache by calling `enableCache(false)` or remove it by `clearCaches()` when the latest entries are required. + +## AccessFlag Signature Builder +`AccessFlag Signature Builder` is a builder class that helps you generate access flags by means of a visually understandable method. +By using this class, you don't need to understand the proper combinations of the numeral flags for building a class file structure. + +An example for using AccessFlag Signature Builder is shown below: + +### Class Access Flags: + +```php +enablePublic() + // Define a super class + ->enableSuper() + // Make a string by specified parameters. + ->make(); + +// Same as: +var_dump( + \PHPJava\Kernel\Maps\ClassAccessFlag::ACC_PUBLIC + | \PHPJava\Kernel\Maps\ClassAccessFlag::ACC_SUPER +); +``` + +You can use the APIs below. + +| Access Flags | +| --------- | +| enablePublic | +| enableSuper | +| enableFinal | +| enableInterface | +| enableAbstract | +| enableSynthetic | +| enableEnum | +| enableModule | + +### Method Access Flags: + +```php +enablePublic() + // Define a static method. + ->enableStatic() + // Make a string by specified parameters. + ->make(); + +// Same as: +var_dump( + \PHPJava\Kernel\Maps\MethodAccessFlag::ACC_PUBLIC + | \PHPJava\Kernel\Maps\MethodAccessFlag::ACC_STATIC +); +``` + +You can use the APIs below: + +| Access Flags | +| --------- | +| enablePublic | +| enableStatic | +| enablePrivate | +| enableProtected | +| enableFinal | +| enableSynchronized | +| enableBridge | +| enableVarArgs | +| enableNative | +| enableAbstract | +| enableStrict | +| enableSynthetic | + +## Descriptor Signature Builder +`Descriptor Signature Builder` is a builder class that helps you generate a string representation of an intermediate code with method parameters and its signatures. +For instance, `Java` interprets `String[] args` and `void` as `([Ljava/lang/String;)V` when `public static void main(String[] args)` compiles. +This builer class solves an issue where this interpretation is such an unefficient task for a human being. + +To use `Descriptor Signature Builder`: + +```php +addArgument( + // The first argument is a java.lang.String + String_::class, + // Set the depth of an array. Defaults to zero. + 1 + ) + // Set the return value + ->setReturn( + // The return value is void. + Void_::class + ) + ->make(); + +// Same as: +var_dump( + '([Ljava/lang/String;)V' +); +``` + +## Interface Entry Builder +_This feature is not yet implemented._ + +## Fields Entry Builder +_This feature is not yet implemented._ + +## Method Entry Builder +`Method Entry Builder` is a builder class which helps generation of an entry when appending it to `Methods` that is an `EntryCollection`. +This class makes it easy to add a method to the class file structure. + +An example is shown below: + +```php +setMethods( + (new Methods()) + // Add a method to a method collection. + ->add( + // Define the method structure according to the method_info attribute in the JVM Spec. + (new Method( + // Defined the access flags of the method. + (new \PHPJava\Compiler\Builder\Signatures\MethodAccessFlag()) + ->enablePublic() + ->enableStatic() + ->make(), + // Find a method name from the ConstantPool. + $finder->find( + Utf8Info::class, + 'main' + ), + // Find a descriptor and the return types from the Constant Pool. + $finder->find( + Utf8Info::class, + (new Descriptor()) + ->addArgument(String_::class, 1) + ->setReturn(Void_::class) + ->make() + ) + ) + ->setAttributes( + // ...Omitted + ) + ) + ->toArray() + ) +); +``` + +## Code Attribution Builder +`Code Attribution Builder` is a builder class which extends `Attribute` Builder. +Originally, the architecture of the `Attribute` Builder is meant to pass only binary string by [Java Virtual Machine Specification](https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.7). +`Code Attribution Builder` supports generation of a binary string for the Attribute classes because it is too early for a human being to write a binary string by hand and it is almost impossible to generate them manually; but we ignore crazy binary-string lovers and recluses. + +An example is shown below: + +```php +find(Utf8Info::class, 'Code'))) + ->setMaxStacks(0) + ->setMaxLocals(0) + // Set the return opcode only. + ->setCode( + pack('C', OpCode::_return) + ) + ->getValue(); + +// Same as: +var_dump( + "" + // attribute_name_index + . pack('n', $finder->find(Utf8Info::class, 'Code')->getResult()->getEntryIndex()) // + // attribute_length + . pack('N', 2 + 2 + 4 + 1 + 2 + 2) // (u2 + u2 + u4 + u1 + u2 + u2) + // max_stack + . "\x00\x00" + // max_locals + . "\x00\x00" + // code_length + . "\x00\x00\x00\x01" + // code + . "\xB1" + // exception_table_length + . "\x00\x00" + // attributes_count + . "\x00\x00" +); +``` + +`Code Attribution Builder` provids the following APIs: + + +| Method name | Summary | +| ------- | --- | +| `setCode(string)` | Define an opcode and operands. | +| `setExceptionTable(ExceptionTable[])` | Define an exception tables to be thrown. This is **not yet implemented**. | +| `setAttributes(Attributes[])` | Set the attributes for Code Attribute. | +| `getValue()` | Generate a binary string of formatted `Code Attribute` with the specified parameters. | + + +## Operation Code Builder +`Operation Code Builder` is a builder class which supports generation of operation codes and operands that are passed to the `setCode` on `Code Attribution Builder.` +If you use this class, you are able to generate operation codes so you no longer have to understand a binary string. + +An example is shown below. + +```php +add( + OpCode::_getstatic, + [ + // Pass position of Fieldref + [ + Uint16::class, + $finder->find( + FieldrefInfo::class, + 'java/lang/System', + 'out', + (new Descriptor()) + ->addArgument(PrintStream::class) + ->make() + ), + ], + ] + ) + // Set ldc command + ->add( + OpCode::_ldc, + [ + // Pass the position of StringInfo in the Constant Pool + [ + Uint8::class, + $finder->find( + StringInfo::class, + 'Hello World!' + ), + ], + ] + ) + // Set invokevirtual Command + ->add( + OpCode::_invokevirtual, + [ + // Pass the position of Methodref in the Constant Pool + [ + Uint16::class, + $finder->find( + MethodrefInfo::class, + 'java/io/PrintStream', + 'println', + (new Descriptor()) + ->addArgument(String_::class) + ->setReturn(Void_::class) + ->make() + ), + ], + ] + ) + // Set the return command. + ->add(OpCode::_return); +``` + +Operation Code can have arguments based on the specification of the Java Virtual Machine. +The `Uint8` and `Uint16` which appeared in the examples mean the size of the parameters to be passed that mean: + +| Class | Meaning | +| ---- | --- | +| PHPJava\Compiler\Builder\Types\Uint8 | Mean to be 1 byte. | +| PHPJava\Compiler\Builder\Types\Uint16 | Mean to be 2 bytes. In other words, it is as same as a short. | +| PHPJava\Compiler\Builder\Types\Uint32 | Mean to be 4 bytes. In other words, it is as same as an int. | +| PHPJava\Compiler\Builder\Types\Uint64 | Mean to be 8 bytes. In other words, it is as same as a long. | + +Also refer to [Java Virtual Machine](https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-6.html) for the parameter length. \ No newline at end of file diff --git a/docs/img/THANKS.md b/docs/img/THANKS.md new file mode 100644 index 00000000..70104c3d --- /dev/null +++ b/docs/img/THANKS.md @@ -0,0 +1,7 @@ +# Created logo from as follows: +Thanks! + +- http://icooon-mono.com/ + +# License +- http://icooon-mono.com/license/ diff --git a/docs/img/logo.png b/docs/img/logo.png new file mode 100644 index 00000000..f07f6699 Binary files /dev/null and b/docs/img/logo.png differ diff --git a/docs/img/logo.psd b/docs/img/logo.psd new file mode 100644 index 00000000..6c25c347 Binary files /dev/null and b/docs/img/logo.psd differ diff --git a/docs/img/php_compiler.gif b/docs/img/php_compiler.gif new file mode 100644 index 00000000..8c667f69 Binary files /dev/null and b/docs/img/php_compiler.gif differ diff --git a/docs/img/php_jvm_lang.gif b/docs/img/php_jvm_lang.gif new file mode 100644 index 00000000..5199546d Binary files /dev/null and b/docs/img/php_jvm_lang.gif differ diff --git a/docs/jvm-lang/README-ja.md b/docs/jvm-lang/README-ja.md new file mode 100644 index 00000000..112e852c --- /dev/null +++ b/docs/jvm-lang/README-ja.md @@ -0,0 +1,97 @@ +# What is the JVM language of PHP syntax? +PHPJava は PHP のシンタックスを用いて PHP を JVM 言語として扱うことが可能です。 +この機能は `nikic/php-parser` を用いて、 PHP を 抽象構文木 (a.k.a Abstract Syntax Tree) に分解し、 PHPJava のアセンブラを用いて +JVM の中間コードを構成し、 PHP が JVM 言語として `java` コマンドで動作するようになります。 +これは、他のどの機能よりも試験的な実装で、やらなければいけないことがたくさんあります。 + +# デモ +![DEMO](../img/php_jvm_lang.gif) + +# Get started +1. `HelloWorld.php` を用意します。 +```php +setDistributeDirectory(__DIR__ . '/dist') +))->assemble(); + +``` + +3. `dist` ディレクトリに `HelloWorld.class` が生成されるので実行してみます。 + +``` +$ cd dist +$ java HelloWorld +``` + +実行すると `Hello World!` が出力されます。 + +# コンパイルについて +## 互換性 +この機能は `JDK 8` としてコンパイルされます。 + +## 型について +PHP の文字列出力の際に、文字列ではないリテラルやオブジェクトが渡された場合は、文字列に変換しようと試みます。 +例えば、 `1` という数値リテラルは、 Java 上では概ね下記のとおりに変換されます。 + +```java +int number = 1; +(new java.lang.Integer(number)).toString() +``` + +PHPJava が構成するオペレーションコードは下記のとおりです。 + +``` + 0: iconst_1 + 1: istore_1 + 2: getstatic #21 // Field java/lang/System.out:Ljava/io/PrintStream; + 5: new #9 // class java/lang/Integer + 8: dup + 9: iload_1 +10: invokespecial #7 // Method java/lang/Integer."":(I)V +13: invokevirtual #13 // Method java/lang/Integer.toString:()Ljava/lang/String; +16: invokevirtual #25 // Method java/io/PrintStream.print:(Ljava/lang/String;)V +``` + +## PHPRuntime.PHPStandard +`PHPStandard` は PHP を実行するためのランタイムクラスを格納している名前空間です。 + +### PHPRuntime.PHPStandard.Constants (旧: PHPStandardClass) +TBD + +### PHPRuntime.PHPStandard.Classes +TBD + +### PHPRuntime.PHPStandard.Functions +TBD + +### PHPRuntime.GlobalStore +TBD + +## PHPRuntime.EntryPoint +TBD + +# TODO +- [ ] 条件分岐の実装 +- [ ] ループ分の実装 +- [ ] PHP の関数呼び出しの実装 +- [ ] クラス外に書かれた箇所を main として扱うようなエントリーポイントの実装 +- [ ] Java Archive への対応 \ No newline at end of file diff --git a/docs/jvm-lang/README.md b/docs/jvm-lang/README.md new file mode 100644 index 00000000..c07b5052 --- /dev/null +++ b/docs/jvm-lang/README.md @@ -0,0 +1,89 @@ +# What is the JVM language of PHP syntax? +PHPJava is able to become a JVM language with PHP syntax. +This feature analyze to AST using `nikic/php-parser`, And PHPJava assemble a intermediately code with assembler in PHPJava, and you can run it as a JVM language with `java` command. +This feature is a experimental implementation more than any other. + +# DEMO +![DEMO](../img/php_jvm_lang.gif) + +# Get started +1. Create `HelloWorld.php` +```php +setDistributeDirectory(__DIR__ . '/dist') +))->assemble(); + +``` + +3. To run generated `HelloWorld.class` in `dist` directory. + +``` +$ cd dist +$ java HelloWorld +``` + +You'll get `Hello World!`. + +# About compile +## Compatibility +This feature compile as the JDK 8. + +## About types +PHPJava will try to convert to a string when passed literal or object is not a string. +For example, The number literal `1` will convert as below roughly on Java. + +```java +int number = 1; +(new java.lang.Integer(number)).toString() +``` + +PHPJava will assemble operation code as follows: + +``` + 0: iconst_1 + 1: istore_1 + 2: getstatic #21 // Field java/lang/System.out:Ljava/io/PrintStream; + 5: new #9 // class java/lang/Integer + 8: dup + 9: iload_1 +10: invokespecial #7 // Method java/lang/Integer."":(I)V +13: invokevirtual #13 // Method java/lang/Integer.toString:()Ljava/lang/String; +16: invokevirtual #25 // Method java/io/PrintStream.print:(Ljava/lang/String;)V +``` + +## PHPRuntime.PHPStandard +`PHPStandard` is a namespace for runtime classes of PHP. + +### PHPRuntime.PHPStandard.Constants (旧: PHPStandardClass) +TBD + +### PHPRuntime.PHPStandard.Classes +TBD + +### PHPRuntime.PHPStandard.Functions +TBD + +### PHPRuntime.GlobalStore +TBD + +## PHPRuntime.EntryPoint +TBD diff --git a/license b/license new file mode 100644 index 00000000..4d98acb7 --- /dev/null +++ b/license @@ -0,0 +1,19 @@ +Copyright (c) 2019 memory + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/nbproject/private/private.properties b/nbproject/private/private.properties deleted file mode 100644 index 08c19796..00000000 --- a/nbproject/private/private.properties +++ /dev/null @@ -1,4 +0,0 @@ -copy.src.on.open=false -index.file=index.php -run.as=SCRIPT -url=http://localhost/PHPJar/ diff --git a/nbproject/private/private.xml b/nbproject/private/private.xml deleted file mode 100644 index 6807a2ba..00000000 --- a/nbproject/private/private.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/nbproject/project.properties b/nbproject/project.properties deleted file mode 100644 index d37ef956..00000000 --- a/nbproject/project.properties +++ /dev/null @@ -1,7 +0,0 @@ -include.path=${php.global.include.path} -php.version=PHP_54 -source.encoding=UTF-8 -src.dir=. -tags.asp=false -tags.short=false -web.root=. diff --git a/nbproject/project.xml b/nbproject/project.xml deleted file mode 100644 index 4795c601..00000000 --- a/nbproject/project.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - org.netbeans.modules.php.project - - - PHPJava - - - diff --git a/phpcs.xml b/phpcs.xml new file mode 100644 index 00000000..eb2791cc --- /dev/null +++ b/phpcs.xml @@ -0,0 +1,34 @@ + + The PHPJava Coding Standards + + ./tests + + + + + + + ./src/Compiler/Emulator/Mnemonics + ./src/Compiler/Lang/Assembler/Bundler/Packages + ./src/Kernel/Types + ./src/Kernel/Mnemonics + ./src/Kernel/Structures + ./src/Packages/PHPJava/Extended/Object_.php + ./src/Packages/java/* + + + + ./src/kernel/maps + + + + + ./src/Packages/PHPJava/Extended/Object_.php + ./src/Packages/java/* + + + + + + + diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 00000000..3ca10c62 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,20 @@ + + + + + ./tests/Cases + + + + + + diff --git a/src/Compiler/Builder/Attribute.php b/src/Compiler/Builder/Attribute.php new file mode 100644 index 00000000..42e7fc16 --- /dev/null +++ b/src/Compiler/Builder/Attribute.php @@ -0,0 +1,98 @@ +constantPoolIndex->getResult(); + return $result + ->getEntryIndex(); + } + + public function getValue(): string + { + return $this->value; + } + + public function setValue(string $value): self + { + $this->value = $value; + return $this; + } + + public function hasAttribute(): bool + { + return $this->hasAttribute; + } + + public function getAttributes(): array + { + return $this->attributes; + } + + public function setAttributes(array $attributes): self + { + $this->attributes = $attributes; + return $this; + } + + public function getName(): string + { + return current( + array_slice( + explode( + '\\', + get_class($this) + ), + -1, + 1 + ) + ); + } + + public function beginPreparation(): Attribute + { + $attributeName = $this->getName(); + + $this->getEnhancedConstantPool() + ->addUtf8($attributeName); + + $this->constantPoolIndex = $this->getEnhancedConstantPool() + ->findUtf8($attributeName); + + return $this; + } +} diff --git a/src/Compiler/Builder/Attributes/Architects/Architect.php b/src/Compiler/Builder/Attributes/Architects/Architect.php new file mode 100644 index 00000000..cb64faef --- /dev/null +++ b/src/Compiler/Builder/Attributes/Architects/Architect.php @@ -0,0 +1,11 @@ +locals); + } +} diff --git a/src/Compiler/Builder/Attributes/Architects/Frames/Frame.php b/src/Compiler/Builder/Attributes/Architects/Frames/Frame.php new file mode 100644 index 00000000..1af57d0a --- /dev/null +++ b/src/Compiler/Builder/Attributes/Architects/Frames/Frame.php @@ -0,0 +1,84 @@ +programCounter = $offset; + return $this; + } + + public function getProgramCounter(): int + { + return $this->programCounter; + } + + public function setBranchTarget(int $offset): self + { + $this->branchTarget = $offset; + return $this; + } + + public function getBranchTarget(): int + { + return $this->branchTarget; + } + + public function getOffsetDelta(): int + { + return $this->offsetDelta; + } + + public function setOffsetDelta(int $offsetDelta): self + { + $this->offsetDelta = $offsetDelta; + return $this; + } + + public function getLocals(): array + { + return $this->locals; + } + + public function addLocal(int $verificationType, ...$arguments): self + { + $this->locals[] = array_merge([$verificationType], $arguments); + return $this; + } + + public function addStack(int $verificationType, ...$arguments): self + { + $this->stacks[] = array_merge([$verificationType], $arguments); + return $this; + } + + public function setLocals(array $locals): self + { + $this->locals = $locals; + return $this; + } + + public function setStacks(array $stacks): self + { + $this->stacks = $stacks; + return $this; + } + + public function getStacks(): array + { + return $this->stacks; + } +} diff --git a/src/Compiler/Builder/Attributes/Architects/Frames/FullFrame.php b/src/Compiler/Builder/Attributes/Architects/Frames/FullFrame.php new file mode 100644 index 00000000..fa8395be --- /dev/null +++ b/src/Compiler/Builder/Attributes/Architects/Frames/FullFrame.php @@ -0,0 +1,13 @@ +offsetDelta < static::MIN || $this->offsetDelta > static::MAX) { + throw new AssembleStructureException( + 'Offset delta in SameFrame is invalid.' + ); + } + return $this->offsetDelta; + } +} diff --git a/src/Compiler/Builder/Attributes/Architects/Frames/SameFrameExtended.php b/src/Compiler/Builder/Attributes/Architects/Frames/SameFrameExtended.php new file mode 100644 index 00000000..301a5bee --- /dev/null +++ b/src/Compiler/Builder/Attributes/Architects/Frames/SameFrameExtended.php @@ -0,0 +1,13 @@ +offsetDelta) < static::MIN || (64 + $this->offsetDelta) > static::MAX) { + throw new AssembleStructureException( + 'Offset delta in SameLocals1StackItemFrame is invalid.' + ); + } + return 64 + $this->offsetDelta; + } +} diff --git a/src/Compiler/Builder/Attributes/Architects/Operation.php b/src/Compiler/Builder/Attributes/Architects/Operation.php new file mode 100644 index 00000000..1a111674 --- /dev/null +++ b/src/Compiler/Builder/Attributes/Architects/Operation.php @@ -0,0 +1,126 @@ +currentIndex; + } + + public function add(int $opcode, array $arguments = []): self + { + $this->codes[] = [$opcode, $arguments]; + $this->currentIndex++; + return $this; + } + + public function set(int $index, int $opcode, array $arguments = []): self + { + $this->codes[$index] = [$opcode, $arguments]; + return $this; + } + + public function get(int $index): ?array + { + return $this->codes[$index] ?? null; + } + + public function getIterator() + { + return new \ArrayIterator($this->codes); + } + + public function toArray(): array + { + $operations = []; + foreach ($this->codes as [$opcode, $arguments]) { + $operands = []; + foreach ($arguments as [$type, $argument]) { + $operands[] = Operand::factory($type, $argument); + } + $operations[] = \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + $opcode, + ...$operands + ); + } + return $operations; + } + + public function make(): string + { + $writer = new BinaryWriter(fopen('php://memory', 'r+')); + foreach ($this->codes as [$opcode, $arguments]) { + $writer->writeUnsignedByte($opcode); + foreach ($arguments as [$type, $value]) { + if ($value instanceof ConstantPoolFinderResult) { + $value = $value + ->getResult() + ->getEntryIndex(); + } + switch ($type) { + case Int8::class: + $writer->writeByte($value); + break; + case Int16::class: + $writer->writeShort($value); + break; + case Int32::class: + $writer->writeInt($value); + break; + case Int64::class: + $writer->writeLong($value); + break; + case Uint8::class: + $writer->writeUnsignedByte($value); + break; + case Uint16::class: + $writer->writeUnsignedShort($value); + break; + case Uint32::class: + $writer->writeUnsignedInt($value); + break; + case Uint64::class: + $writer->writeUnsignedLong($value); + break; + default: + throw new CompilerException('Unsupported type: ' . $type); + } + } + } + return $writer->getStreamContents(); + } + + public function __toString(): string + { + return $this->make(); + } +} diff --git a/src/Compiler/Builder/Attributes/Code.php b/src/Compiler/Builder/Attributes/Code.php new file mode 100644 index 00000000..eba86ed8 --- /dev/null +++ b/src/Compiler/Builder/Attributes/Code.php @@ -0,0 +1,214 @@ +validateOperationArray($operations); + $this->operations = $operations; + return $this; + } + + public function setDefaultLocals(int $defaultLocals): self + { + $this->defaultLocals = $defaultLocals; + return $this; + } + + public function setExceptionTables(array $exceptionTables): self + { + $this->exceptionTables = $exceptionTables; + return $this; + } + + public function getValue(): string + { + $writer = new BinaryWriter( + fopen('php://memory', 'r+') + ); + + $maxStacks = 0; + $maxLocals = $this->defaultLocals; + $countableMaxLocals = $maxLocals; + + // Calculate max stack size from operations + $opcodeMap = new OpCode(); + + foreach ($this->operations as $index => $operation) { + $mnemonic = Runtime::MNEMONIC_NAMESPACE . '\\' . ($opcodeMap->getName($operation->getOpCode())); + /** + * @var OperationCodeInterface $opcodeInstance + */ + $opcodeInstance = new $mnemonic(); + + if ($opcodeInstance->isStackingOperation()) { + $maxStacks++; + } + + switch ($operation->getOpCode()) { + case OpCode::_astore: + case OpCode::_astore_0: + case OpCode::_astore_1: + case OpCode::_astore_2: + case OpCode::_astore_3: + case OpCode::_istore: + case OpCode::_istore_0: + case OpCode::_istore_1: + case OpCode::_istore_2: + case OpCode::_istore_3: + case OpCode::_lstore: + case OpCode::_lstore_0: + case OpCode::_lstore_1: + case OpCode::_lstore_2: + case OpCode::_lstore_3: + case OpCode::_dstore: + case OpCode::_dstore_0: + case OpCode::_dstore_1: + case OpCode::_dstore_2: + case OpCode::_dstore_3: + case OpCode::_fstore: + case OpCode::_fstore_0: + case OpCode::_fstore_1: + case OpCode::_fstore_2: + case OpCode::_fstore_3: + $maxLocals = max($maxLocals, ++$countableMaxLocals); + break; + case OpCode::_aload: + case OpCode::_aload_0: + case OpCode::_aload_1: + case OpCode::_aload_2: + case OpCode::_aload_3: + case OpCode::_iload: + case OpCode::_iload_0: + case OpCode::_iload_1: + case OpCode::_iload_2: + case OpCode::_iload_3: + case OpCode::_lload: + case OpCode::_lload_0: + case OpCode::_lload_1: + case OpCode::_lload_2: + case OpCode::_lload_3: + case OpCode::_dload: + case OpCode::_dload_0: + case OpCode::_dload_1: + case OpCode::_dload_2: + case OpCode::_dload_3: + case OpCode::_fload: + case OpCode::_fload_0: + case OpCode::_fload_1: + case OpCode::_fload_2: + case OpCode::_fload_3: + $countableMaxLocals--; + break; + case OpCode::_ldc: + /** + * @var ConstantPoolFinderResult $result + */ + $result = $operation->getOperand(0) + ->getValue(); + + if ($result->getResult()->getEntryIndex() > 0xff) { + // Change opcode automatically. + $this->operations[$index] = \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_ldc_w, + Operand::factory( + Uint16::class, + $result + ) + ); + } + break; + } + + if ($maxLocals < 0) { + throw new AssembleStructureException( + 'max locals is underflow.' + ); + } + } + + $code = new Operation(); + + foreach ($this->operations as $operation) { + $code->add( + $operation->getOpCode(), + $operation->getOperands() + ); + } + + $code = $code->make(); + + // max stack + $writer->writeUnsignedShort($maxStacks); + + // max locals + $writer->writeUnsignedShort($maxLocals); + + // code length + $writer->writeUnsignedInt(strlen($code)); + + // code + $writer->write($code); + + // exception_tables_length + $writer->writeUnsignedShort(count($this->exceptionTables)); + + // exception_tables + foreach ($this->exceptionTables as [$startPc, $endPc, $handlerPc, $catchType]) { + $writer->writeUnsignedShort($startPc); + $writer->writeUnsignedShort($endPc); + $writer->writeUnsignedShort($handlerPc); + $writer->writeUnsignedShort($catchType); + } + + // attributes length + $writer->writeUnsignedShort(count($this->getAttributes())); + + // attributes + foreach ($this->getAttributes() as $attribute) { + /** + * @var Attribute $attribute + */ + $writer->writeUnsignedShort( + $this->getEnhancedConstantPool() + ->findUtf8($attribute->getName()) + ->getResult(false) + ->getEntryIndex() + ); + $value = $attribute->getValue(); + $writer->writeUnsignedInt(strlen($value)); + $writer->write($value); + } + + return $writer->getStreamContents(); + } +} diff --git a/src/Compiler/Builder/Attributes/PHPJavaSignature.php b/src/Compiler/Builder/Attributes/PHPJavaSignature.php new file mode 100644 index 00000000..c9f0624c --- /dev/null +++ b/src/Compiler/Builder/Attributes/PHPJavaSignature.php @@ -0,0 +1,32 @@ +writeUnsignedShort(PHPJava::VERSION); + + // PHP Version + $phpVersion = PHP_VERSION; + $writer->writeUnsignedByte(strlen($phpVersion)); + $writer->write($phpVersion); + + // PHP Built Platform + $phpBuiltPlatform = php_uname(); + $writer->writeUnsignedByte(strlen($phpBuiltPlatform)); + $writer->write($phpBuiltPlatform); + + return $writer->getStreamContents(); + } +} diff --git a/src/Compiler/Builder/Attributes/SourceFile.php b/src/Compiler/Builder/Attributes/SourceFile.php new file mode 100644 index 00000000..63087736 --- /dev/null +++ b/src/Compiler/Builder/Attributes/SourceFile.php @@ -0,0 +1,36 @@ +finderResult = $finderResult; + return $this; + } + + public function getValue(): string + { + $writer = new BinaryWriter( + fopen('php://memory', 'r+') + ); + + $writer->writeUnsignedShort( + $this->finderResult + ->getResult() + ->getEntryIndex() + ); + + return $writer->getStreamContents(); + } +} diff --git a/src/Compiler/Builder/Attributes/StackMapTable.php b/src/Compiler/Builder/Attributes/StackMapTable.php new file mode 100644 index 00000000..3a1b6788 --- /dev/null +++ b/src/Compiler/Builder/Attributes/StackMapTable.php @@ -0,0 +1,345 @@ +validateOperationArray($operations); + $this->operations = $operations; + return $this; + } + + public function setDefaultLocalVariables(array $variables): self + { + $this->localVariables = $variables; + return $this; + } + + public function beginPreparation(): Attribute + { + foreach ($this->localVariables as $localVariable) { + [, $type] = $localVariable; + switch ($type) { + case Int_::class: + case Long_::class: + case Float_::class: + case Short_::class: + case Byte_::class: + case Char_::class: + // Nothing to do... + break; + default: + $this->getEnhancedConstantPool() + ->addClass($type); + break; + } + } + + $this->entries = []; + + $programCounter = 0; + $effectiveProgramCounter = null; + + $emulatedAccumulator = new Accumulator(); + $currentOffset = 0; + + foreach ($this->operations as $operation) { + $programCounter = $this->calculateProgramCounterByOperationCodes( + $this->operations, + $operation->getOpCode(), + $programCounter + ); + + $emulatedAccumulator->addBacktrace($programCounter, $operation); + + if ($emulatedAccumulator->getEffectiveProgramCounter() !== null + && $programCounter >= $emulatedAccumulator->getEffectiveProgramCounter() + ) { + $emulatedAccumulator + ->enableStack(true); + } + + /** + * @var AbstractOperationCode $mnemonic + */ + $mnemonic = Runtime::EMULATOR_MNEMONIC_NAMESPACE . '\\' . $operation->getMnemonic(); + $executor = (new $mnemonic($operation, $emulatedAccumulator, $programCounter)); + + // Execute emulated operation. + $executor + ->setConstantPool($this->getConstantPool()) + ->setConstantPoolFinder($this->getConstantPoolFinder()) + ->execute(); + } + + $this->entries = $emulatedAccumulator->getFrames(); + + if (!empty($this->entries)) { + foreach ($this->getStore()->getAll() as $variableName => $variable) { + [$index, $classType, $dimensionsOfArray] = $variable; + $classType = Formatter::buildSignature($classType, $dimensionsOfArray); + $this->getEnhancedConstantPool() + ->addClass($classType); + } + } + + return parent::beginPreparation(); + } + + public function getValue(): string + { + $writer = new BinaryWriter( + fopen('php://memory', 'r+') + ); + + $entries = $this->entries; + + // Set verified entries + usort( + $entries, + static function (FullFrame $a, FullFrame $b) { + return $a->getBranchTarget() <=> $b->getBranchTarget(); + } + ); + + /** + * Remove duplicated branch target and frame type was zero. + * + * @var FullFrame $previousFrame + */ + $entries = array_reduce( + $entries, + static function (array $carry, FullFrame $frame) use (&$entries) { + foreach ($carry as $index => $previousFrame) { + /** + * @var FullFrame $previousFrame + */ + if ($frame->getBranchTarget() === $previousFrame->getBranchTarget()) { + // Replace to new arrival entry. + $carry[$index] = $frame; + return $carry; + } + } + $carry[] = $frame; + return $carry; + }, + [] + ); + + /** + * @var FullFrame[] $frames + */ + $frames = []; + + $previousFrame = null; + foreach ($entries as $frame) { + /** + * @var FullFrame $frame + * @var null|FullFrame $previousFrame + */ + $stacks = count($frame->getStacks()); + $locals = count($frame->getLocals()); + $samePreviousLocalsAndCurrentLocals = $previousFrame !== null + && $locals === count($previousFrame->getLocals()); + + $entry = null; + $offsetDelta = $frame->getBranchTarget(); + if ($previousFrame !== null) { + $offsetDelta = $frame->getBranchTarget() - $previousFrame->getBranchTarget() - 1; + } + + if ($stacks === 0 + && ($locals === 0 || $samePreviousLocalsAndCurrentLocals) + ) { + $entry = SameFrame::init(); + } elseif ($stacks === 1 + && ($locals === 0 || $samePreviousLocalsAndCurrentLocals) + ) { + $entry = SameLocals1StackItemFrame::init(); + } elseif ($stacks === 0 && $locals > 0) { + $entry = AppendFrame::init(); + } else { + $entry = $frame; + } + + $entry + ->setOffsetDelta($offsetDelta) + ->setProgramCounter($frame->getProgramCounter()) + ->setBranchTarget($frame->getBranchTarget()) + ->setLocals($frame->getLocals()) + ->setStacks($frame->getStacks()); + + $frames[] = $previousFrame = $entry; + } + + $writer->writeUnsignedShort( + count($frames) + ); + + foreach ($frames as $entry) { + /** + * @var Frame $entry + */ + $writer->writeUnsignedByte($entry->getTag()); + switch (get_class($entry)) { + case SameFrame::class: + // Nothing to do. + break; + case SameFrameExtended::class: + /** + * @var SameFrameExtended $entry + */ + $writer->writeUnsignedShort($entry->getOffsetDelta()); + break; + case SameLocals1StackItemFrame::class: + /** + * @var SameLocals1StackItemFrame $entry + */ + $this->writeStackMapTableVerificationSegment( + $writer, + $entry->getStacks() + ); + break; + case AppendFrame::class: + /** + * @var AppendFrame $entry + */ + $writer->writeUnsignedShort($entry->getOffsetDelta()); + $this->writeStackMapTableVerificationSegment( + $writer, + $entry->getLocals() + ); + break; + case FullFrame::class: + /** + * @var FullFrame $entry + */ + $writer->writeUnsignedShort($entry->getOffsetDelta()); + $locals = []; + foreach ($this->localVariables as $variableName => $variable) { + [$index, $classType, $dimensionsOfArray] = $variable; + $classType = Formatter::buildSignature($classType, $dimensionsOfArray); + $locals[$index] = [ + VerificationTypeTag::ITEM_Object, + $this->getEnhancedConstantPool() + ->findClass($classType), + ]; + } + + ArrayTool::concat( + $locals, + ...$entry->getLocals() + ); + + $writer->writeUnsignedShort(count($locals)); + $this->writeStackMapTableVerificationSegment( + $writer, + $locals + ); + + $writer->writeUnsignedShort(count($entry->getStacks())); + $this->writeStackMapTableVerificationSegment( + $writer, + $entry->getStacks() + ); + break; + default: + throw new AssembleStructureException( + sprintf( + 'Unsupported frame class: %s', + get_class($entry) + ) + ); + } + } + + return $writer->getStreamContents(); + } + + protected function writeStackMapTableVerificationSegment(BinaryWriter $writer, array $segments): void + { + foreach ($segments as $segment) { + [$verificationTypeTag] = $segment; + $writer->writeUnsignedByte( + $verificationTypeTag + ); + switch ($verificationTypeTag) { + case VerificationTypeTag::ITEM_Top: + case VerificationTypeTag::ITEM_Integer: + case VerificationTypeTag::ITEM_Float: + case VerificationTypeTag::ITEM_Null: + case VerificationTypeTag::ITEM_UninitializedThis: + case VerificationTypeTag::ITEM_Long: + case VerificationTypeTag::ITEM_Double: + // Nothing to do. + break; + case VerificationTypeTag::ITEM_Object: + $classEntry = $segment[1]; + /** + * @var ConstantPoolFinderResult $classEntry + */ + $writer->writeUnsignedShort( + $classEntry + ->getResult() + ->getEntryIndex() + ); + break; + case VerificationTypeTag::ITEM_Uninitialized: + default: + throw new AssembleStructureException( + sprintf( + 'Unsupported verification type: %s', + $verificationTypeTag + ) + ); + } + } + } +} diff --git a/src/Compiler/Builder/BuilderInterface.php b/src/Compiler/Builder/BuilderInterface.php new file mode 100644 index 00000000..cfc6ed0f --- /dev/null +++ b/src/Compiler/Builder/BuilderInterface.php @@ -0,0 +1,7 @@ +entries as $fromEntry) { + if ($fromEntry === null) { + continue; + } + + /** + * @var AbstractInfo|EntryInterface $validateableEntry + * @var AbstractInfo|EntryInterface $entry + */ + if ($fromEntry instanceof InfoInterface + && $entry instanceof InfoInterface + && get_class($fromEntry) === get_class($entry) + && ArrayTool::compare($fromEntry->getEntries(), $entry->getEntries()) + ) { + return $this; + } + } + + $this->entries[] = $entry; + return $this; + } + + public function offsetGet($offset) + { + return $this->entries[$offset]; + } + + public function offsetSet($offset, $value) + { + if (!($value instanceof EntryInterface)) { + throw new NotAllowedDeleteException('The value is not allowed.'); + } + $this->entries[$offset] = $value; + } + + public function offsetUnset($offset) + { + throw new NotAllowedDeleteException('The entry cannot delete because the entry is an immutable.'); + } + + public function offsetExists($offset) + { + return isset($this->entries[$offset]); + } + + public function toArray(): array + { + return $this->entries; + } + + public function getIterator() + { + return new \ArrayIterator($this->entries); + } + + public function enableIntern(bool $enable): EntryCollectionInterface + { + $this->enableIntern = $enable; + return $this; + } + + public function length(): int + { + return count($this->entries); + } +} diff --git a/src/Compiler/Builder/Collection/Attributes.php b/src/Compiler/Builder/Collection/Attributes.php new file mode 100644 index 00000000..2b63f1bd --- /dev/null +++ b/src/Compiler/Builder/Collection/Attributes.php @@ -0,0 +1,9 @@ +accessFlags = $accessFlags; + $this->className = $className; + $this->name = $name; + $this->descriptor = $descriptor; + } + + public function getAccessFlags(): int + { + return $this->accessFlags; + } + + public function getClassName(): string + { + return $this->className; + } + + public function getName(): string + { + return $this->name; + } + + public function getDescriptor(): string + { + return $this->descriptor; + } + + public function getNameIndex(): int + { + /** + * @var EntryMap $result + */ + $result = $this->getEnhancedConstantPool() + ->findUtf8($this->name) + ->getResult(); + return $result + ->getEntryIndex(); + } + + public function getDescriptorIndex(): int + { + /** + * @var EntryMap $result + */ + $result = $this->getEnhancedConstantPool() + ->findUtf8($this->descriptor) + ->getResult(); + return $result + ->getEntryIndex(); + } + + public function getAttributes(): array + { + return $this->attributes; + } + + public function setAttributes(array $attributes): self + { + $this->attributes = $attributes; + return $this; + } + + public function setValue(string $value): self + { + $this->value = $value; + return $this; + } + + public function getValue(): string + { + return $this->value; + } + + public function beginPreparation(): self + { + $this->getEnhancedConstantPool() + ->addNameAndType( + $this->getName(), + $this->getDescriptor() + ); + return $this; + } +} diff --git a/src/Compiler/Builder/Finder/AbstractFinder.php b/src/Compiler/Builder/Finder/AbstractFinder.php new file mode 100644 index 00000000..61e3a7bf --- /dev/null +++ b/src/Compiler/Builder/Finder/AbstractFinder.php @@ -0,0 +1,25 @@ +constantPool = $constantPool; + } + + /** + * @param mixed ...$arguments + */ + abstract public function find(string $type, ...$arguments): FinderResultInterface; +} diff --git a/src/Compiler/Builder/Finder/ConstantPoolFinder.php b/src/Compiler/Builder/Finder/ConstantPoolFinder.php new file mode 100644 index 00000000..a06fd055 --- /dev/null +++ b/src/Compiler/Builder/Finder/ConstantPoolFinder.php @@ -0,0 +1,20 @@ +result[$key])) { + return $this->result[$key]; + } + return $this->result[$key] = new ConstantPoolFinderResult($this, $this->constantPool, $type, ...$arguments); + } +} diff --git a/src/Compiler/Builder/Finder/FinderInterface.php b/src/Compiler/Builder/Finder/FinderInterface.php new file mode 100644 index 00000000..367527d9 --- /dev/null +++ b/src/Compiler/Builder/Finder/FinderInterface.php @@ -0,0 +1,8 @@ +finder = $finder; + $this->constantPool = $constantPool; + $this->type = $type; + $this->arguments = $arguments; + } + + public function getArguments(): array + { + return $this->arguments; + } + + abstract public function getResult(bool $enableCache = true); +} diff --git a/src/Compiler/Builder/Finder/Result/ConstantPoolFinderResult.php b/src/Compiler/Builder/Finder/Result/ConstantPoolFinderResult.php new file mode 100644 index 00000000..2d64a2ff --- /dev/null +++ b/src/Compiler/Builder/Finder/Result/ConstantPoolFinderResult.php @@ -0,0 +1,171 @@ +constantPool as $index => $entry) { + /** + * @var AbstractInfo $entry + */ + if (!($entry instanceof $this->type)) { + continue; + } + + $key = 'INTERNED_' . crc32($this->type . implode($this->arguments)); + + if ($enableCache === true && isset($this->resultCaches[$key])) { + return $this->resultCaches[$key]; + } + + $texts = []; + switch ($this->type) { + case FieldrefInfo::class: + case MethodrefInfo::class: + [[$type1, $constantPoolIndex1], [$type2, $constantPoolIndex2]] = $entry->getStructureEntries(); + + /** + * @var ClassInfo $classInfo + * @var NameAndTypeInfo $nameAndTypeInfo + */ + $classInfo = $this->constantPool[$constantPoolIndex1]; + $nameAndTypeInfo = $this->constantPool[$constantPoolIndex2]; + + [[$type, $classInfoConstantPoolIndex]] = $classInfo->getStructureEntries(); + $texts[] = $this->filterUtf8EntryTextByStructureEntry( + $this->constantPool[$classInfoConstantPoolIndex]->getStructureEntries() + ); + + [[$nameAndTypeType1, $nameAndTypeConstantPoolIndex1], [$nameAndTypeType1, $nameAndTypeConstantPoolIndex2]] = $nameAndTypeInfo->getStructureEntries(); + $texts[] = $this->filterUtf8EntryTextByStructureEntry( + $this->constantPool[$nameAndTypeConstantPoolIndex1]->getStructureEntries() + ); + $texts[] = $this->filterUtf8EntryTextByStructureEntry( + $this->constantPool[$nameAndTypeConstantPoolIndex2]->getStructureEntries() + ); + + break; + case NameAndTypeInfo::class: + [[$type1, $constantPoolIndex1], [$type1, $constantPoolIndex2]] = $entry->getStructureEntries(); + $texts[] = $this->filterUtf8EntryTextByStructureEntry( + $this->constantPool[$constantPoolIndex1]->getStructureEntries() + ); + $texts[] = $this->filterUtf8EntryTextByStructureEntry( + $this->constantPool[$constantPoolIndex2]->getStructureEntries() + ); + break; + case StringInfo::class: + case ClassInfo::class: + /** + * @var EntryMap $result + */ + [[$type, $constantPoolIndex]] = $entry->getStructureEntries(); + $texts[] = $this->filterUtf8EntryTextByStructureEntry( + $this->constantPool[$constantPoolIndex]->getStructureEntries() + ); + break; + case IntegerInfo::class: + [[$type, $value]] = $entry->getStructureEntries(); + if ($this->arguments[0] === $value) { + return $this->resultCaches[$key] = new EntryMap($index, $entry); + } + break; + case Utf8Info::class: + $texts[] = $this->filterUtf8EntryTextByStructureEntry( + $entry->getStructureEntries() + ); + break; + default: + throw new FinderException('The finder type is not implemented yet. (Type: ' . $this->type . ')'); + } + + if ($this->isValidText($texts)) { + $entryMap = new EntryMap($index, $entry); + if ($enableCache === false) { + return $entryMap; + } + // Returns found index. + return $this->resultCaches[$key] = $entryMap; + } + } + + throw new FinderException('The entry is not found. (arguments: ' . implode(', ', $this->arguments) . ')'); + } + + public function __debugInfo() + { + // Omit infos. + return [ + '(omitted)', + ]; + } + + public function getType(): string + { + return $this->type; + } + + private function isValidText(array $texts): bool + { + if (empty($texts)) { + return false; + } + $results = []; + foreach ($texts as $pointer => $text) { + $results[] = $text === $this->arguments[$pointer]; + } + return !in_array( + false, + $results, + true + ); + } + + private function findUtf8Entry(string $text): string + { + /** + * @var EntryMap $result + */ + $result = $this->finder + ->find(Utf8Info::class, $text) + ->getResult(); + + return $this->filterUtf8EntryTextByStructureEntry( + $result + ->getEntry() + ->getStructureEntries() + ); + } + + private function filterUtf8EntryTextByStructureEntry(array $structureEntry): string + { + [, [$length, $entry]] = $structureEntry; + return $entry; + } +} diff --git a/src/Compiler/Builder/Finder/Result/FinderResultInterface.php b/src/Compiler/Builder/Finder/Result/FinderResultInterface.php new file mode 100644 index 00000000..36df3f91 --- /dev/null +++ b/src/Compiler/Builder/Finder/Result/FinderResultInterface.php @@ -0,0 +1,11 @@ +operations = $operations; + } + + public function __debugInfo() + { + return [ + 'size' => count($this->operations), + 'operations' => '(omitted...)', + ]; + } + + public function append(Operation ...$operations): self + { + $this->operations = array_merge( + $this->operations, + $operations + ); + return $this; + } + + public function expand(): array + { + return $this->operations; + } +} diff --git a/src/Compiler/Builder/Generator/Operation/Operand.php b/src/Compiler/Builder/Generator/Operation/Operand.php new file mode 100644 index 00000000..7bd47eb1 --- /dev/null +++ b/src/Compiler/Builder/Generator/Operation/Operand.php @@ -0,0 +1,71 @@ +type; + } + + public function getValue() + { + return $this->value; + } + + public function __construct(string $type, &$value) + { + $this->type = $type; + $this->value = $value; + } + + public function make(): array + { + return [ + $this->type, + $this->value, + ]; + } +} diff --git a/src/Compiler/Builder/Generator/Operation/Operation.php b/src/Compiler/Builder/Generator/Operation/Operation.php new file mode 100644 index 00000000..d5d0cfe8 --- /dev/null +++ b/src/Compiler/Builder/Generator/Operation/Operation.php @@ -0,0 +1,87 @@ +opcode; + } + + public function getOperand(int $index): ?Operand + { + return $this->operands[$index] ?? null; + } + + public function getOperands(): array + { + return array_reduce( + $this->operands, + static function ($carry, ?Operand $operand) { + if ($operand === null) { + return $carry; + } + $carry[] = $operand + ->make(); + return $carry; + }, + [] + ); + } + + public function enableEffectiveProgramCounter(bool $enable): self + { + $this->enableEffectiveProgramCounter = $enable; + return $this; + } + + public function isEffectiveProgramCounter(): bool + { + return $this->enableEffectiveProgramCounter; + } + + public function getOperandTypes(): array + { + return array_map( + static function (Operand $operand) { + // return type + return $operand + ->getType(); + }, + $this->operands + ); + } + + public function getMnemonic() + { + return '_' . $this->mnemonic; + } + + public function __construct(int $opcode, ?Operand ...$operands) + { + $this->opcode = $opcode; + $this->operands = $operands; + $this->mnemonic = ltrim((new OpCode())->getName($opcode), '_'); + } + + public function make(): array + { + return [ + $this->getOpCode(), + $this->getOperands(), + ]; + } +} diff --git a/src/Compiler/Builder/Generator/Operation/OperationGeneratorInterface.php b/src/Compiler/Builder/Generator/Operation/OperationGeneratorInterface.php new file mode 100644 index 00000000..b9db45ad --- /dev/null +++ b/src/Compiler/Builder/Generator/Operation/OperationGeneratorInterface.php @@ -0,0 +1,10 @@ +marker; + } + + public function __construct(int $marker, string ...$operandTypes) + { + $this->marker = $marker; + $this->operandTypes = $operandTypes; + } + + public function is(string $marker): bool + { + return $this->marker === $marker; + } + + public function getOperandTypes(): array + { + return $this->operandTypes; + } +} diff --git a/src/Compiler/Builder/Maps/EntryMap.php b/src/Compiler/Builder/Maps/EntryMap.php new file mode 100644 index 00000000..96931f10 --- /dev/null +++ b/src/Compiler/Builder/Maps/EntryMap.php @@ -0,0 +1,34 @@ +entryIndex = $entryIndex; + $this->entry = $entry; + } + + public function getEntryIndex(): int + { + return $this->entryIndex; + } + + public function getEntry(): AbstractInfo + { + return $this->entry; + } +} diff --git a/src/Compiler/Builder/Maps/MapInterface.php b/src/Compiler/Builder/Maps/MapInterface.php new file mode 100644 index 00000000..8d85ba3c --- /dev/null +++ b/src/Compiler/Builder/Maps/MapInterface.php @@ -0,0 +1,7 @@ +accessFlags = $accessFlags; + $this->className = $className; + $this->name = $name; + $this->descriptor = $descriptor; + } + + public function getAccessFlags(): int + { + return $this->accessFlags; + } + + public function getClassName(): string + { + return $this->className; + } + + public function getName(): string + { + return $this->name; + } + + public function getDescriptor(): string + { + return $this->descriptor; + } + + public function getNameIndex(): int + { + /** + * @var EntryMap $result + */ + $result = $this->getEnhancedConstantPool() + ->findUtf8($this->name) + ->getResult(); + return $result + ->getEntryIndex(); + } + + public function getDescriptorIndex(): int + { + /** + * @var EntryMap $result + */ + $result = $this->getEnhancedConstantPool() + ->findUtf8($this->descriptor) + ->getResult(); + return $result + ->getEntryIndex(); + } + + public function getAttributes(): array + { + return $this->attributes; + } + + public function setAttributes(array $attributes): self + { + $this->attributes = $attributes; + return $this; + } + + public function beginPreparation(): self + { + $this->getEnhancedConstantPool() + ->addNameAndType( + $this->getName(), + $this->getDescriptor() + ); + + return $this; + } +} diff --git a/src/Compiler/Builder/Segments/AbstractSegment.php b/src/Compiler/Builder/Segments/AbstractSegment.php new file mode 100644 index 00000000..71546d59 --- /dev/null +++ b/src/Compiler/Builder/Segments/AbstractSegment.php @@ -0,0 +1,30 @@ +classFileStructureBuilder = $classFileStructureBuilder; + $this->binaryWriter = $binaryWriter; + } + + public static function init(ClassFileStructure $classFileStructureBuilder, BinaryWriter $binaryWriter) + { + return new static($classFileStructureBuilder, $binaryWriter); + } +} diff --git a/src/Compiler/Builder/Segments/AccessFlags.php b/src/Compiler/Builder/Segments/AccessFlags.php new file mode 100644 index 00000000..a4681b90 --- /dev/null +++ b/src/Compiler/Builder/Segments/AccessFlags.php @@ -0,0 +1,13 @@ +binaryWriter->writeUnsignedShort( + $this->classFileStructureBuilder->getAccessFlags() + ); + } +} diff --git a/src/Compiler/Builder/Segments/Attributes/AbstractAttribute.php b/src/Compiler/Builder/Segments/Attributes/AbstractAttribute.php new file mode 100644 index 00000000..99538c87 --- /dev/null +++ b/src/Compiler/Builder/Segments/Attributes/AbstractAttribute.php @@ -0,0 +1,30 @@ +attributes = $attributes; + $this->constantPool = $constantPool; + $this->binaryWriter = $binaryWriter; + } + + public static function init( + array $attributes, + array $constantPool, + BinaryWriter $binaryWriter + ) { + return new static($attributes, $constantPool, $binaryWriter); + } +} diff --git a/src/Compiler/Builder/Segments/Attributes/AttributeSegmentInterface.php b/src/Compiler/Builder/Segments/Attributes/AttributeSegmentInterface.php new file mode 100644 index 00000000..47569c10 --- /dev/null +++ b/src/Compiler/Builder/Segments/Attributes/AttributeSegmentInterface.php @@ -0,0 +1,16 @@ +attributes as $attribute) { + /** + * @var Attribute $attribute + */ + // Write attribute_name_index + $this->binaryWriter->writeUnsignedShort($attribute->getConstantPoolIndex()); + + $value = $attribute->getValue(); + + // Write attribute_length + $this->binaryWriter->writeUnsignedInt(strlen($value)); + + // Write attribute + $this->binaryWriter->write($value); + } + } +} diff --git a/src/Compiler/Builder/Segments/Attributes/AttributesCount.php b/src/Compiler/Builder/Segments/Attributes/AttributesCount.php new file mode 100644 index 00000000..0083e951 --- /dev/null +++ b/src/Compiler/Builder/Segments/Attributes/AttributesCount.php @@ -0,0 +1,11 @@ +binaryWriter->writeUnsignedShort(count($this->attributes)); + } +} diff --git a/src/Compiler/Builder/Segments/ConstantPool.php b/src/Compiler/Builder/Segments/ConstantPool.php new file mode 100644 index 00000000..dcf6295b --- /dev/null +++ b/src/Compiler/Builder/Segments/ConstantPool.php @@ -0,0 +1,52 @@ +classFileStructureBuilder->getConstantPool() as $index => $entry) { + /** + * @var AbstractInfo $entry + */ + // Skip constant pool entry at the first index. + if ($index === 0) { + continue; + } + + /** + * @var AbstractInfo $entry + */ + $this->binaryWriter->writeUnsignedByte($entry->getTag()); + foreach ($entry->getStructureEntries() as [$type, $structureEntry]) { + switch ($type) { + case Uint16::class: + $this->binaryWriter->writeUnsignedShort($structureEntry); + break; + case Uint32::class: + $this->binaryWriter->writeUnsignedInt($structureEntry); + break; + case Uint64::class: + $this->binaryWriter->writeUnsignedLong($structureEntry); + break; + case Bytes::class: + $this->binaryWriter->write($structureEntry); + break; + default: + throw new CompilerException('Unsupported type: ' . $type); + } + } + } + } +} diff --git a/src/Compiler/Builder/Segments/ConstantPoolCount.php b/src/Compiler/Builder/Segments/ConstantPoolCount.php new file mode 100644 index 00000000..24807e86 --- /dev/null +++ b/src/Compiler/Builder/Segments/ConstantPoolCount.php @@ -0,0 +1,13 @@ +binaryWriter->writeUnsignedShort( + count($this->classFileStructureBuilder->getConstantPool()) + ); + } +} diff --git a/src/Compiler/Builder/Segments/Fields.php b/src/Compiler/Builder/Segments/Fields.php new file mode 100644 index 00000000..94f72926 --- /dev/null +++ b/src/Compiler/Builder/Segments/Fields.php @@ -0,0 +1,23 @@ +classFileStructureBuilder->getFields() as $field) { + /** + * @var Field $field + */ + $this->binaryWriter->writeUnsignedShort($field->getAccessFlags()); + $this->binaryWriter->writeUnsignedShort($field->getNameIndex()); + $this->binaryWriter->writeUnsignedShort($field->getDescriptorIndex()); + + // attributes_count + $this->binaryWriter->writeUnsignedShort(0); + } + } +} diff --git a/src/Compiler/Builder/Segments/FieldsCount.php b/src/Compiler/Builder/Segments/FieldsCount.php new file mode 100644 index 00000000..fedecf60 --- /dev/null +++ b/src/Compiler/Builder/Segments/FieldsCount.php @@ -0,0 +1,11 @@ +binaryWriter->writeUnsignedShort(count($this->classFileStructureBuilder->getFields())); + } +} diff --git a/src/Compiler/Builder/Segments/Interfaces.php b/src/Compiler/Builder/Segments/Interfaces.php new file mode 100644 index 00000000..68120f6f --- /dev/null +++ b/src/Compiler/Builder/Segments/Interfaces.php @@ -0,0 +1,10 @@ +binaryWriter->writeUnsignedShort(count($this->classFileStructureBuilder->getInterfaces())); + } +} diff --git a/src/Compiler/Builder/Segments/MagicByte.php b/src/Compiler/Builder/Segments/MagicByte.php new file mode 100644 index 00000000..6fb92611 --- /dev/null +++ b/src/Compiler/Builder/Segments/MagicByte.php @@ -0,0 +1,13 @@ +binaryWriter->writeUnsignedInt( + \PHPJava\Core\JVM\Validations\MagicByte::getMagicByte() + ); + } +} diff --git a/src/Compiler/Builder/Segments/MajorVersion.php b/src/Compiler/Builder/Segments/MajorVersion.php new file mode 100644 index 00000000..05af0114 --- /dev/null +++ b/src/Compiler/Builder/Segments/MajorVersion.php @@ -0,0 +1,11 @@ +binaryWriter->writeUnsignedShort($this->classFileStructureBuilder->getMajorVersion()); + } +} diff --git a/src/Compiler/Builder/Segments/Methods.php b/src/Compiler/Builder/Segments/Methods.php new file mode 100644 index 00000000..013c5189 --- /dev/null +++ b/src/Compiler/Builder/Segments/Methods.php @@ -0,0 +1,57 @@ +classFileStructureBuilder->getMethods() as $method) { + /** + * @var Method $method + */ + $name = $this->classFileStructureBuilder->getConstantPool()[$method->getNameIndex()]; + $descriptor = $this->classFileStructureBuilder->getConstantPool()[$method->getDescriptorIndex()]; + + if (!($name instanceof Utf8Info)) { + throw new CompilerException( + 'The method entry is not implemented by Utf8Info. (index: ' . $method->getNameIndex() . ', Segment: ' . __CLASS__ . ')' + ); + } + + if (!($descriptor instanceof Utf8Info)) { + throw new CompilerException( + 'The method entry is not implemented by Utf8Info. (index: ' . $method->getDescriptorIndex() . ', Segment: ' . __CLASS__ . ')' + ); + } + + $this->binaryWriter->writeUnsignedShort($method->getAccessFlags()); + $this->binaryWriter->writeUnsignedShort($method->getNameIndex()); + $this->binaryWriter->writeUnsignedShort($method->getDescriptorIndex()); + + // Build attributes_count + AttributesCount::init( + $method->getAttributes(), + $this->classFileStructureBuilder->getConstantPool(), + $this->binaryWriter + )->build(); + + if (count($method->getAttributes()) === 0) { + continue; + } + + // Build attributes + Attributes::init( + $method->getAttributes(), + $this->classFileStructureBuilder->getConstantPool(), + $this->binaryWriter + )->build(); + } + } +} diff --git a/src/Compiler/Builder/Segments/MethodsCount.php b/src/Compiler/Builder/Segments/MethodsCount.php new file mode 100644 index 00000000..15644d1d --- /dev/null +++ b/src/Compiler/Builder/Segments/MethodsCount.php @@ -0,0 +1,11 @@ +binaryWriter->writeUnsignedShort(count($this->classFileStructureBuilder->getMethods())); + } +} diff --git a/src/Compiler/Builder/Segments/MinorVersion.php b/src/Compiler/Builder/Segments/MinorVersion.php new file mode 100644 index 00000000..0db223d1 --- /dev/null +++ b/src/Compiler/Builder/Segments/MinorVersion.php @@ -0,0 +1,11 @@ +binaryWriter->writeUnsignedShort($this->classFileStructureBuilder->getMinorVersion()); + } +} diff --git a/src/Compiler/Builder/Segments/SegmentInterface.php b/src/Compiler/Builder/Segments/SegmentInterface.php new file mode 100644 index 00000000..dc3f9a96 --- /dev/null +++ b/src/Compiler/Builder/Segments/SegmentInterface.php @@ -0,0 +1,13 @@ +classFileStructureBuilder + ->getSuperClass(); + + $ref = $this->classFileStructureBuilder->getConstantPool()[$entryIndex]; + + if (!($ref instanceof ClassInfo)) { + throw new CompilerException( + 'The entry is not implemented by ClassInfo. (Index: ' . $entryIndex . ', Segment: ' . __CLASS__ . ')' + ); + } + + $this->binaryWriter->writeUnsignedShort( + $entryIndex + ); + } +} diff --git a/src/Compiler/Builder/Segments/ThisClass.php b/src/Compiler/Builder/Segments/ThisClass.php new file mode 100644 index 00000000..2966d91e --- /dev/null +++ b/src/Compiler/Builder/Segments/ThisClass.php @@ -0,0 +1,27 @@ +classFileStructureBuilder + ->getThisClass(); + + $ref = $this->classFileStructureBuilder->getConstantPool()[$entryIndex]; + + if (!($ref instanceof ClassInfo)) { + throw new CompilerException( + 'The entry is not implemented by ClassInfo. (Index: ' . $entryIndex . ', Segment: ' . __CLASS__ . ')' + ); + } + + $this->binaryWriter->writeUnsignedShort( + $entryIndex + ); + } +} diff --git a/src/Compiler/Builder/Signatures/AbstractAccessFlag.php b/src/Compiler/Builder/Signatures/AbstractAccessFlag.php new file mode 100644 index 00000000..f17e7403 --- /dev/null +++ b/src/Compiler/Builder/Signatures/AbstractAccessFlag.php @@ -0,0 +1,14 @@ +flagValue; + } +} diff --git a/src/Compiler/Builder/Signatures/AccessFlagInterface.php b/src/Compiler/Builder/Signatures/AccessFlagInterface.php new file mode 100644 index 00000000..8b3deb97 --- /dev/null +++ b/src/Compiler/Builder/Signatures/AccessFlagInterface.php @@ -0,0 +1,9 @@ +flagValue |= Flag::ACC_PUBLIC; + return $this; + } + + public function enableSuper(): self + { + $this->flagValue |= Flag::ACC_SUPER; + return $this; + } + + public function enableFinal(): self + { + $this->flagValue |= Flag::ACC_FINAL; + return $this; + } + + public function enableInterface(): self + { + $this->flagValue |= Flag::ACC_INTERFACE; + return $this; + } + + public function enableAbstract(): self + { + $this->flagValue |= Flag::ACC_ABSTRACT; + return $this; + } + + public function enableSynthetic(): self + { + $this->flagValue |= Flag::ACC_SYNTHETIC; + return $this; + } + + public function enableAnnotation(): self + { + $this->flagValue |= Flag::ACC_ANNOTATION; + return $this; + } + + public function enableEnum(): self + { + $this->flagValue |= Flag::ACC_ENUM; + return $this; + } + + public function enableModule(): self + { + $this->flagValue |= Flag::ACC_MODULE; + return $this; + } +} diff --git a/src/Compiler/Builder/Signatures/Descriptor.php b/src/Compiler/Builder/Signatures/Descriptor.php new file mode 100644 index 00000000..0a7d017b --- /dev/null +++ b/src/Compiler/Builder/Signatures/Descriptor.php @@ -0,0 +1,96 @@ +arguments[] = [$type, $dimensionsOfArray]; + return $this; + } + + public function setReturn(string $type): self + { + $this->return = $type; + return $this; + } + + public function make(): string + { + $arguments = implode( + array_map( + static function (array $argument) { + [$type, $dimensionsOfArray] = $argument; + $string = str_repeat('[', $dimensionsOfArray); + $type = Formatter::convertPHPPrimitiveTypeToJavaType( + ltrim($type, '\\') + ); + switch ($type) { + case Char_::class: + case Byte_::class: + case Double_::class: + case Float_::class: + case Long_::class: + case Short_::class: + case Int_::class: + case Void_::class: + $string .= TypeResolver::resolveSignatureByType($type); + break; + default: + $path = Formatter::convertPHPNamespacesToJava( + $type, + '/' + ); + $string .= 'L' . $path . ';'; + break; + } + return $string; + }, + $this->arguments + ) + ); + + if ($this->return === null) { + return $arguments; + } + + $returnSignature = Formatter::convertPrimitiveValueToJavaSignature( + $this->return + ); + + if ($returnSignature === null) { + $returnSignature = TypeResolver::resolve( + Formatter::convertPHPNamespacesToJava( + $this->return, + '/' + ) + ); + + if ($returnSignature[0] === 'L') { + $returnSignature .= ';'; + } + } + return '(' . $arguments . ')' . $returnSignature; + } +} diff --git a/src/Compiler/Builder/Signatures/DescriptorInterface.php b/src/Compiler/Builder/Signatures/DescriptorInterface.php new file mode 100644 index 00000000..88f4d454 --- /dev/null +++ b/src/Compiler/Builder/Signatures/DescriptorInterface.php @@ -0,0 +1,9 @@ +flagValue |= Flag::ACC_PUBLIC; + return $this; + } + + public function enableStatic(): self + { + $this->flagValue |= Flag::ACC_STATIC; + return $this; + } + + public function enableFinal(): self + { + $this->flagValue |= Flag::ACC_FINAL; + return $this; + } + + public function enableProtected(): self + { + $this->flagValue |= Flag::ACC_PROTECTED; + return $this; + } + + public function enablePrivate(): self + { + $this->flagValue |= Flag::ACC_PRIVATE; + return $this; + } + + public function enableSynthetic(): self + { + $this->flagValue |= Flag::ACC_SYNTHETIC; + return $this; + } + + public function enableTransient(): self + { + $this->flagValue |= Flag::ACC_TRANSIENT; + return $this; + } + + public function enableEnum(): self + { + $this->flagValue |= Flag::ACC_ENUM; + return $this; + } +} diff --git a/src/Compiler/Builder/Signatures/MethodAccessFlag.php b/src/Compiler/Builder/Signatures/MethodAccessFlag.php new file mode 100644 index 00000000..53fcb354 --- /dev/null +++ b/src/Compiler/Builder/Signatures/MethodAccessFlag.php @@ -0,0 +1,81 @@ +flagValue |= Flag::ACC_STATIC; + return $this; + } + + public function enablePublic(): self + { + $this->flagValue |= Flag::ACC_PUBLIC; + return $this; + } + + public function enablePrivate(): self + { + $this->flagValue |= Flag::ACC_PRIVATE; + return $this; + } + + public function enableProtected(): self + { + $this->flagValue |= Flag::ACC_PROTECTED; + return $this; + } + + public function enableFinal(): self + { + $this->flagValue |= Flag::ACC_FINAL; + return $this; + } + + public function enableSynchronized(): self + { + $this->flagValue |= Flag::ACC_SYNCHRONIZED; + return $this; + } + + public function enableBridge(): self + { + $this->flagValue |= Flag::ACC_BRIDGE; + return $this; + } + + public function enableVarArgs(): self + { + $this->flagValue |= Flag::ACC_VARARGS; + return $this; + } + + public function enableNative(): self + { + $this->flagValue |= Flag::ACC_NATIVE; + return $this; + } + + public function enableAbstract(): self + { + $this->flagValue |= Flag::ACC_ABSTRACT; + return $this; + } + + public function enableStrict(): self + { + $this->flagValue |= Flag::ACC_STRICT; + return $this; + } + + public function enableSynthetic(): self + { + $this->flagValue |= Flag::ACC_SYNTHETIC; + return $this; + } +} diff --git a/src/Compiler/Builder/Structures/ClassFileStructure.php b/src/Compiler/Builder/Structures/ClassFileStructure.php new file mode 100644 index 00000000..d7346a52 --- /dev/null +++ b/src/Compiler/Builder/Structures/ClassFileStructure.php @@ -0,0 +1,149 @@ +minorVersion = $version; + return $this; + } + + public function setMajorVersion(int $version): self + { + $this->majorVersion = $version; + return $this; + } + + public function setConstantPool(array $cp): self + { + $this->constantPool = $cp; + return $this; + } + + public function setThisClass(FinderResultInterface $constantPoolIndex): self + { + $this->thisClass = $constantPoolIndex; + return $this; + } + + public function setSuperClass(FinderResultInterface $constantPoolIndex): self + { + $this->superClass = $constantPoolIndex; + return $this; + } + + public function setInterfaces(array $interfaces): self + { + $this->interfaces = $interfaces; + return $this; + } + + public function setFields(array $fields): self + { + $this->fields = $fields; + return $this; + } + + public function setMethods(array $methods): self + { + $this->methods = $methods; + return $this; + } + + public function setAttributes(array $attributes): self + { + $this->attributes = $attributes; + return $this; + } + + public function setAccessFlags(int $accessFlags): self + { + $this->accessFlags = $accessFlags; + return $this; + } + + public function getMinorVersion(): int + { + return $this->minorVersion; + } + + public function getMajorVersion(): int + { + return $this->majorVersion; + } + + public function getConstantPool(): array + { + return $this->constantPool; + } + + public function getThisClass(): int + { + /** + * @var EntryMap $result + */ + $result = $this->thisClass->getResult(); + return $result + ->getEntryIndex(); + } + + public function getSuperClass(): int + { + /** + * @var EntryMap $result + */ + $result = $this->superClass->getResult(); + return $result + ->getEntryIndex(); + } + + public function getInterfaces(): array + { + return $this->interfaces; + } + + public function getFields(): array + { + return $this->fields; + } + + public function getMethods(): array + { + return $this->methods; + } + + public function getAttributes(): array + { + return $this->attributes; + } + + public function getAccessFlags(): int + { + return $this->accessFlags; + } +} diff --git a/src/Compiler/Builder/Structures/EntryCollectionInterface.php b/src/Compiler/Builder/Structures/EntryCollectionInterface.php new file mode 100644 index 00000000..5e2e85b0 --- /dev/null +++ b/src/Compiler/Builder/Structures/EntryCollectionInterface.php @@ -0,0 +1,20 @@ +tag; + } + + public function getEntries(): array + { + return $this->entries; + } + + public function getStructure(): array + { + return $this->structure; + } + + public function getStructureEntries(): array + { + $counter = 0; + return array_reduce( + $this->structure, + function ($carry, $item) use (&$counter) { + /** + * @var FinderResultInterface|int $entryIndex + */ + $entryIndex = $this->entries[$counter++]; + if ($entryIndex instanceof FinderResultInterface) { + /** + * @var EntryMap $result + */ + $result = $entryIndex->getResult(); + $entryIndex = $result->getEntryIndex(); + } + $item[] = $entryIndex; + $carry[] = $item; + return $carry; + }, + [] + ); + } + + public static function factory(...$arguments): self + { + return new static(...$arguments); + } +} diff --git a/src/Compiler/Builder/Structures/Info/ClassInfo.php b/src/Compiler/Builder/Structures/Info/ClassInfo.php new file mode 100644 index 00000000..f3b60e62 --- /dev/null +++ b/src/Compiler/Builder/Structures/Info/ClassInfo.php @@ -0,0 +1,22 @@ +entries[] = $constantPoolIndex; + } +} diff --git a/src/Compiler/Builder/Structures/Info/FieldrefInfo.php b/src/Compiler/Builder/Structures/Info/FieldrefInfo.php new file mode 100644 index 00000000..49bf9c75 --- /dev/null +++ b/src/Compiler/Builder/Structures/Info/FieldrefInfo.php @@ -0,0 +1,27 @@ +entries[] = $constantPoolIndex1; + $this->entries[] = $constantPoolIndex2; + } +} diff --git a/src/Compiler/Builder/Structures/Info/IntegerInfo.php b/src/Compiler/Builder/Structures/Info/IntegerInfo.php new file mode 100644 index 00000000..166e9b36 --- /dev/null +++ b/src/Compiler/Builder/Structures/Info/IntegerInfo.php @@ -0,0 +1,22 @@ +entries[] = $value; + } +} diff --git a/src/Compiler/Builder/Structures/Info/MethodrefInfo.php b/src/Compiler/Builder/Structures/Info/MethodrefInfo.php new file mode 100644 index 00000000..480d1b65 --- /dev/null +++ b/src/Compiler/Builder/Structures/Info/MethodrefInfo.php @@ -0,0 +1,27 @@ +entries[] = $constantPoolIndex1; + $this->entries[] = $constantPoolIndex2; + } +} diff --git a/src/Compiler/Builder/Structures/Info/NameAndTypeInfo.php b/src/Compiler/Builder/Structures/Info/NameAndTypeInfo.php new file mode 100644 index 00000000..4e836c1a --- /dev/null +++ b/src/Compiler/Builder/Structures/Info/NameAndTypeInfo.php @@ -0,0 +1,27 @@ +entries[] = $constantPoolIndex1; + $this->entries[] = $constantPoolIndex2; + } +} diff --git a/src/Compiler/Builder/Structures/Info/StringInfo.php b/src/Compiler/Builder/Structures/Info/StringInfo.php new file mode 100644 index 00000000..8dcb7ae7 --- /dev/null +++ b/src/Compiler/Builder/Structures/Info/StringInfo.php @@ -0,0 +1,23 @@ +entries[] = $constantPoolIndex; + } +} diff --git a/src/Compiler/Builder/Structures/Info/Utf8Info.php b/src/Compiler/Builder/Structures/Info/Utf8Info.php new file mode 100644 index 00000000..801a311c --- /dev/null +++ b/src/Compiler/Builder/Structures/Info/Utf8Info.php @@ -0,0 +1,25 @@ +entries[] = strlen($value); + $this->entries[] = $value; + } +} diff --git a/src/Compiler/Builder/Structures/InfoInterface.php b/src/Compiler/Builder/Structures/InfoInterface.php new file mode 100644 index 00000000..9f368188 --- /dev/null +++ b/src/Compiler/Builder/Structures/InfoInterface.php @@ -0,0 +1,8 @@ +classFileStructureBuilder = $classFileStructureBuilder; + } + + public function compile($stream): void + { + if (!is_resource($stream)) { + throw new CompilerException('The stream is not a resource.'); + } + $binaryWriter = new BinaryWriter($stream); + if (!$binaryWriter->isWritable()) { + throw new CompilerException('The stream is not writable.'); + } + + $binaryWriter->enableBuffer(true); + + // Write magic + MagicByte::init($this->classFileStructureBuilder, $binaryWriter) + ->build(); + + // Write minor_version + MinorVersion::init($this->classFileStructureBuilder, $binaryWriter) + ->build(); + + // Write major_version + MajorVersion::init($this->classFileStructureBuilder, $binaryWriter) + ->build(); + + // Write constant_pool_count + ConstantPoolCount::init($this->classFileStructureBuilder, $binaryWriter) + ->build(); + + // Write constant_pool + ConstantPool::init($this->classFileStructureBuilder, $binaryWriter) + ->build(); + + // Write access_flags + AccessFlags::init($this->classFileStructureBuilder, $binaryWriter) + ->build(); + + // Write this_class + ThisClass::init($this->classFileStructureBuilder, $binaryWriter) + ->build(); + + // Write super_class + SuperClass::init($this->classFileStructureBuilder, $binaryWriter) + ->build(); + + // Write interfaces_count + InterfacesCount::init($this->classFileStructureBuilder, $binaryWriter) + ->build(); + + // Write interfaces + Interfaces::init($this->classFileStructureBuilder, $binaryWriter) + ->build(); + + // Write fields_count + FieldsCount::init($this->classFileStructureBuilder, $binaryWriter) + ->build(); + + // Write fields + Fields::init($this->classFileStructureBuilder, $binaryWriter) + ->build(); + + // Write methods_count + MethodsCount::init($this->classFileStructureBuilder, $binaryWriter) + ->build(); + + // Write methods + Methods::init($this->classFileStructureBuilder, $binaryWriter) + ->build(); + + // Build attributes_count + AttributesCount::init( + $this->classFileStructureBuilder->getAttributes(), + $this->classFileStructureBuilder->getConstantPool(), + $binaryWriter + )->build(); + + // Build attributes + Attributes::init( + $this->classFileStructureBuilder->getAttributes(), + $this->classFileStructureBuilder->getConstantPool(), + $binaryWriter + )->build(); + } +} diff --git a/src/Compiler/Emulator/Accumulator.php b/src/Compiler/Emulator/Accumulator.php new file mode 100644 index 00000000..15dde476 --- /dev/null +++ b/src/Compiler/Emulator/Accumulator.php @@ -0,0 +1,155 @@ +debugTool = new DebugTool(__CLASS__); + } + + public function popFromOperandStack() + { + $this->debugTool->getLogger()->debug( + 'Popped an item, remaining stacks: ' . (count($this->stacks) - 1) + ); + + if ((count($this->stacks) - 1) < 0) { + // Build backtrace + $backtrace = ''; + foreach ($this->backtraces as [$programCounter, $operation]) { + /** + * @var Operation $operation + */ + $backtrace .= sprintf( + "%004s\t0x%02X\t%d\t%s\n", + $programCounter, + $operation->getOpCode(), + count($operation->getOperands()), + ltrim($operation->getMnemonic(), '_') + ); + } + throw new EmulatorException( + 'Cannot pop an item because stack is underflow.' . "\n" . + 'Backtraces: ' . "\n" . + $backtrace + ); + } + + if (!$this->enableStack) { + // Dup a stack + $stack = array_pop($this->stacks); + $this->stacks[] = $stack; + return $stack; + } + + return array_pop($this->stacks); + } + + public function pushToOperandStack($item): self + { + $this->debugTool->getLogger()->debug( + 'Stack an item, remaining stacks: ' . (count($this->stacks) + 1) + ); + + if (!$this->enableStack) { + return $this; + } + $this->stacks[] = $item; + return $this; + } + + public function getFromLocal(int $index) + { + if (!array_key_exists($index, $this->locals)) { + throw new EmulatorException( + 'Cannot get an item because item is not available.' + ); + } + return $this->locals[$index]; + } + + public function setToLocal(int $index, $value): self + { + $this->locals[$index] = $value; + return $this; + } + + public function getStacks(): array + { + return $this->stacks; + } + + public function getLocals(): array + { + return $this->locals; + } + + public function countStacks(): int + { + return count($this->stacks); + } + + public function countLocals(): int + { + return count($this->locals); + } + + public function enableStack(bool $enable): self + { + $this->enableStack = $enable; + return $this; + } + + public function setEffectiveProgramCounter(int $programCounter): self + { + $this->effectiveProgramCounter = $programCounter; + return $this; + } + + public function getEffectiveProgramCounter(): int + { + return $this->effectiveProgramCounter; + } + + public function addFrame(Frame $frame): self + { + $this->frames[] = $frame; + return $this; + } + + public function getFrames(): array + { + return $this->frames; + } + + public function addBacktrace(int $programCounter, Operation $operation): self + { + $this->backtraces[] = [$programCounter, $operation]; + return $this; + } +} diff --git a/src/Compiler/Emulator/Mnemonics/AbstractOperationCode.php b/src/Compiler/Emulator/Mnemonics/AbstractOperationCode.php new file mode 100644 index 00000000..fd3a9d2f --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/AbstractOperationCode.php @@ -0,0 +1,27 @@ +operation = $operation; + $this->accumulator = $accumulator; + $this->programCounter = $programCounter; + } + + abstract public function execute(): void; +} diff --git a/src/Compiler/Emulator/Mnemonics/OperationCodeInterface.php b/src/Compiler/Emulator/Mnemonics/OperationCodeInterface.php new file mode 100644 index 00000000..b4b08b66 --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/OperationCodeInterface.php @@ -0,0 +1,13 @@ +accumulator->pushToOperandStack( + [ + VerificationTypeTag::ITEM_Object, + $this->getEnhancedConstantPool() + ->findClass(\PHPJava\Packages\java\lang\String_::class), + ] + ); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_aload_1.php b/src/Compiler/Emulator/Mnemonics/_aload_1.php new file mode 100644 index 00000000..00a1c450 --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_aload_1.php @@ -0,0 +1,21 @@ +accumulator->pushToOperandStack( + [ + VerificationTypeTag::ITEM_Object, + $this->getEnhancedConstantPool() + ->findClass(\PHPJava\Packages\java\lang\String_::class), + ] + ); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_aload_2.php b/src/Compiler/Emulator/Mnemonics/_aload_2.php new file mode 100644 index 00000000..e0303893 --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_aload_2.php @@ -0,0 +1,21 @@ +accumulator->pushToOperandStack( + [ + VerificationTypeTag::ITEM_Object, + $this->getEnhancedConstantPool() + ->findClass(\PHPJava\Packages\java\lang\String_::class), + ] + ); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_aload_3.php b/src/Compiler/Emulator/Mnemonics/_aload_3.php new file mode 100644 index 00000000..f9d5c4db --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_aload_3.php @@ -0,0 +1,21 @@ +accumulator->pushToOperandStack( + [ + VerificationTypeTag::ITEM_Object, + $this->getEnhancedConstantPool() + ->findClass(\PHPJava\Packages\java\lang\String_::class), + ] + ); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_anewarray.php b/src/Compiler/Emulator/Mnemonics/_anewarray.php new file mode 100644 index 00000000..5dec877c --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_anewarray.php @@ -0,0 +1,15 @@ +accumulator + ->setToLocal( + 0, + $this->accumulator + ->popFromOperandStack() + ); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_astore_1.php b/src/Compiler/Emulator/Mnemonics/_astore_1.php new file mode 100644 index 00000000..0d27bfb9 --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_astore_1.php @@ -0,0 +1,18 @@ +accumulator + ->setToLocal( + 1, + $this->accumulator + ->popFromOperandStack() + ); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_astore_2.php b/src/Compiler/Emulator/Mnemonics/_astore_2.php new file mode 100644 index 00000000..d22e4b92 --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_astore_2.php @@ -0,0 +1,18 @@ +accumulator + ->setToLocal( + 2, + $this->accumulator + ->popFromOperandStack() + ); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_astore_3.php b/src/Compiler/Emulator/Mnemonics/_astore_3.php new file mode 100644 index 00000000..479c1a9b --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_astore_3.php @@ -0,0 +1,18 @@ +accumulator + ->setToLocal( + 3, + $this->accumulator + ->popFromOperandStack() + ); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_athrow.php b/src/Compiler/Emulator/Mnemonics/_athrow.php new file mode 100644 index 00000000..df4309e2 --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_athrow.php @@ -0,0 +1,15 @@ +accumulator + ->pushToOperandStack( + [VerificationTypeTag::ITEM_Integer] + ); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_caload.php b/src/Compiler/Emulator/Mnemonics/_caload.php new file mode 100644 index 00000000..f10c8f98 --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_caload.php @@ -0,0 +1,15 @@ +accumulator->popFromOperandStack(); + $this->accumulator->pushToOperandStack($stack); + $this->accumulator->pushToOperandStack($stack); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_dup2.php b/src/Compiler/Emulator/Mnemonics/_dup2.php new file mode 100644 index 00000000..edfda938 --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_dup2.php @@ -0,0 +1,15 @@ +pushToOperandStackByType( + $this->getReturnType(), + $this->getClassName() + ); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_goto.php b/src/Compiler/Emulator/Mnemonics/_goto.php new file mode 100644 index 00000000..e8e337f0 --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_goto.php @@ -0,0 +1,13 @@ +addFrame(); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_goto_w.php b/src/Compiler/Emulator/Mnemonics/_goto_w.php new file mode 100644 index 00000000..67f8e9e3 --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_goto_w.php @@ -0,0 +1,15 @@ +accumulator->popFromOperandStack(); + $this->accumulator->popFromOperandStack(); + $this->accumulator->pushToOperandStack( + [VerificationTypeTag::ITEM_Integer] + ); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_iaload.php b/src/Compiler/Emulator/Mnemonics/_iaload.php new file mode 100644 index 00000000..ef05028d --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_iaload.php @@ -0,0 +1,15 @@ +accumulator->popFromOperandStack(); + $this->accumulator->popFromOperandStack(); + $this->accumulator->pushToOperandStack( + [VerificationTypeTag::ITEM_Integer] + ); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_iastore.php b/src/Compiler/Emulator/Mnemonics/_iastore.php new file mode 100644 index 00000000..26411186 --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_iastore.php @@ -0,0 +1,15 @@ +accumulator->pushToOperandStack( + [VerificationTypeTag::ITEM_Integer] + ); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_iconst_1.php b/src/Compiler/Emulator/Mnemonics/_iconst_1.php new file mode 100644 index 00000000..1c1b5297 --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_iconst_1.php @@ -0,0 +1,17 @@ +accumulator->pushToOperandStack( + [VerificationTypeTag::ITEM_Integer] + ); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_iconst_2.php b/src/Compiler/Emulator/Mnemonics/_iconst_2.php new file mode 100644 index 00000000..f332cc90 --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_iconst_2.php @@ -0,0 +1,17 @@ +accumulator->pushToOperandStack( + [VerificationTypeTag::ITEM_Integer] + ); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_iconst_3.php b/src/Compiler/Emulator/Mnemonics/_iconst_3.php new file mode 100644 index 00000000..14b9a59e --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_iconst_3.php @@ -0,0 +1,17 @@ +accumulator->pushToOperandStack( + [VerificationTypeTag::ITEM_Integer] + ); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_iconst_4.php b/src/Compiler/Emulator/Mnemonics/_iconst_4.php new file mode 100644 index 00000000..b04d8d40 --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_iconst_4.php @@ -0,0 +1,17 @@ +accumulator->pushToOperandStack( + [VerificationTypeTag::ITEM_Integer] + ); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_iconst_5.php b/src/Compiler/Emulator/Mnemonics/_iconst_5.php new file mode 100644 index 00000000..33981f44 --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_iconst_5.php @@ -0,0 +1,17 @@ +accumulator->pushToOperandStack( + [VerificationTypeTag::ITEM_Integer] + ); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_iconst_m1.php b/src/Compiler/Emulator/Mnemonics/_iconst_m1.php new file mode 100644 index 00000000..2ce90f3b --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_iconst_m1.php @@ -0,0 +1,15 @@ +accumulator->popFromOperandStack(); + $this->accumulator->popFromOperandStack(); + $this->accumulator->pushToOperandStack( + [VerificationTypeTag::ITEM_Integer] + ); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_if_acmpeq.php b/src/Compiler/Emulator/Mnemonics/_if_acmpeq.php new file mode 100644 index 00000000..c9c44b63 --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_if_acmpeq.php @@ -0,0 +1,15 @@ +accumulator + ->popFromOperandStack(); + $this->accumulator + ->popFromOperandStack(); + + $this->addFrame(); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_if_icmpne.php b/src/Compiler/Emulator/Mnemonics/_if_icmpne.php new file mode 100644 index 00000000..c6b48db9 --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_if_icmpne.php @@ -0,0 +1,15 @@ +accumulator + ->popFromOperandStack(); + + $this->addFrame(); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_ifge.php b/src/Compiler/Emulator/Mnemonics/_ifge.php new file mode 100644 index 00000000..f7430952 --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_ifge.php @@ -0,0 +1,16 @@ +accumulator + ->popFromOperandStack(); + + $this->addFrame(); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_ifgt.php b/src/Compiler/Emulator/Mnemonics/_ifgt.php new file mode 100644 index 00000000..915f6f70 --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_ifgt.php @@ -0,0 +1,16 @@ +accumulator + ->popFromOperandStack(); + + $this->addFrame(); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_ifle.php b/src/Compiler/Emulator/Mnemonics/_ifle.php new file mode 100644 index 00000000..81b0db41 --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_ifle.php @@ -0,0 +1,15 @@ +accumulator + ->popFromOperandStack(); + + $this->addFrame(); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_ifne.php b/src/Compiler/Emulator/Mnemonics/_ifne.php new file mode 100644 index 00000000..3c3b57aa --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_ifne.php @@ -0,0 +1,16 @@ +accumulator + ->popFromOperandStack(); + + $this->addFrame(); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_ifnonnull.php b/src/Compiler/Emulator/Mnemonics/_ifnonnull.php new file mode 100644 index 00000000..68bd59c0 --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_ifnonnull.php @@ -0,0 +1,15 @@ +accumulator->pushToOperandStack( + $this->accumulator + ->getFromLocal( + $this->accumulator->popFromOperandStack() + ) + ); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_iload_0.php b/src/Compiler/Emulator/Mnemonics/_iload_0.php new file mode 100644 index 00000000..88e04710 --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_iload_0.php @@ -0,0 +1,19 @@ +accumulator->pushToOperandStack( + [ + VerificationTypeTag::ITEM_Integer, + ] + ); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_iload_1.php b/src/Compiler/Emulator/Mnemonics/_iload_1.php new file mode 100644 index 00000000..33d40b5d --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_iload_1.php @@ -0,0 +1,19 @@ +accumulator->pushToOperandStack( + [ + VerificationTypeTag::ITEM_Integer, + ] + ); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_iload_2.php b/src/Compiler/Emulator/Mnemonics/_iload_2.php new file mode 100644 index 00000000..d082824a --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_iload_2.php @@ -0,0 +1,19 @@ +accumulator->pushToOperandStack( + [ + VerificationTypeTag::ITEM_Integer, + ] + ); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_iload_3.php b/src/Compiler/Emulator/Mnemonics/_iload_3.php new file mode 100644 index 00000000..262f01f2 --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_iload_3.php @@ -0,0 +1,19 @@ +accumulator->pushToOperandStack( + [ + VerificationTypeTag::ITEM_Integer, + ] + ); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_imul.php b/src/Compiler/Emulator/Mnemonics/_imul.php new file mode 100644 index 00000000..f257cf14 --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_imul.php @@ -0,0 +1,19 @@ +accumulator->popFromOperandStack(); + $this->accumulator->popFromOperandStack(); + $this->accumulator->pushToOperandStack( + [VerificationTypeTag::ITEM_Integer] + ); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_ineg.php b/src/Compiler/Emulator/Mnemonics/_ineg.php new file mode 100644 index 00000000..73e4b577 --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_ineg.php @@ -0,0 +1,15 @@ +getSignature(2); + + for ($i = 0; $i < $signature['arguments_count']; $i++) { + $this->accumulator->popFromOperandStack(); + } + + $this->accumulator->popFromOperandStack(); + + $this->pushToOperandStackByType( + $this->getReturnType(), + $this->getClassName() + ); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_invokestatic.php b/src/Compiler/Emulator/Mnemonics/_invokestatic.php new file mode 100644 index 00000000..a9cb7366 --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_invokestatic.php @@ -0,0 +1,22 @@ +getSignature(2); + + for ($i = 0; $i < $signature['arguments_count']; $i++) { + $this->accumulator->popFromOperandStack(); + } + + $this->pushToOperandStackByType( + $this->getReturnType(), + $this->getClassName() + ); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_invokevirtual.php b/src/Compiler/Emulator/Mnemonics/_invokevirtual.php new file mode 100644 index 00000000..b3925d54 --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_invokevirtual.php @@ -0,0 +1,24 @@ +getSignature(2); + + for ($i = 0; $i < $signature['arguments_count']; $i++) { + $this->accumulator->popFromOperandStack(); + } + + $this->accumulator->popFromOperandStack(); + + $this->pushToOperandStackByType( + $this->getReturnType(), + $this->getClassName() + ); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_ior.php b/src/Compiler/Emulator/Mnemonics/_ior.php new file mode 100644 index 00000000..44f71390 --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_ior.php @@ -0,0 +1,19 @@ +accumulator->popFromOperandStack(); + $this->accumulator->popFromOperandStack(); + $this->accumulator->pushToOperandStack( + [VerificationTypeTag::ITEM_Integer] + ); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_irem.php b/src/Compiler/Emulator/Mnemonics/_irem.php new file mode 100644 index 00000000..8d22ed10 --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_irem.php @@ -0,0 +1,19 @@ +accumulator->popFromOperandStack(); + $this->accumulator->popFromOperandStack(); + $this->accumulator->pushToOperandStack( + [VerificationTypeTag::ITEM_Integer] + ); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_ireturn.php b/src/Compiler/Emulator/Mnemonics/_ireturn.php new file mode 100644 index 00000000..af1671d0 --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_ireturn.php @@ -0,0 +1,15 @@ +accumulator + ->setToLocal( + 0, + $this->accumulator + ->popFromOperandStack() + ); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_istore_1.php b/src/Compiler/Emulator/Mnemonics/_istore_1.php new file mode 100644 index 00000000..8d239228 --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_istore_1.php @@ -0,0 +1,18 @@ +accumulator + ->setToLocal( + 1, + $this->accumulator + ->popFromOperandStack() + ); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_istore_2.php b/src/Compiler/Emulator/Mnemonics/_istore_2.php new file mode 100644 index 00000000..90547ffc --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_istore_2.php @@ -0,0 +1,18 @@ +accumulator + ->setToLocal( + 2, + $this->accumulator + ->popFromOperandStack() + ); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_istore_3.php b/src/Compiler/Emulator/Mnemonics/_istore_3.php new file mode 100644 index 00000000..3d891e51 --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_istore_3.php @@ -0,0 +1,18 @@ +accumulator + ->setToLocal( + 3, + $this->accumulator + ->popFromOperandStack() + ); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_isub.php b/src/Compiler/Emulator/Mnemonics/_isub.php new file mode 100644 index 00000000..963e6f4d --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_isub.php @@ -0,0 +1,19 @@ +accumulator->popFromOperandStack(); + $this->accumulator->popFromOperandStack(); + $this->accumulator->pushToOperandStack( + [VerificationTypeTag::ITEM_Integer] + ); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_iushr.php b/src/Compiler/Emulator/Mnemonics/_iushr.php new file mode 100644 index 00000000..fb7a1784 --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_iushr.php @@ -0,0 +1,15 @@ +operation->getOperand(0)->getValue(); + switch (get_class($finderResult->getResult()->getEntry())) { + case IntegerInfo::class: + $this->getEnhancedConstantPool() + ->addClass(\PHPJava\Packages\java\lang\Integer::class); + + $this->accumulator->pushToOperandStack( + [ + VerificationTypeTag::ITEM_Integer, + ] + ); + break; + case StringInfo::class: + $this->getEnhancedConstantPool() + ->addClass(\PHPJava\Packages\java\lang\String_::class); + + $this->accumulator->pushToOperandStack( + [ + VerificationTypeTag::ITEM_Object, + $this->getEnhancedConstantPool() + ->findClass(\PHPJava\Packages\java\lang\String_::class), + ] + ); + break; + default: + throw new AssembleStructureException( + 'Unsupported entry type: ' . get_class($finderResult->getResult()->getEntry()) + ); + } + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_ldc2_w.php b/src/Compiler/Emulator/Mnemonics/_ldc2_w.php new file mode 100644 index 00000000..bc77e618 --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_ldc2_w.php @@ -0,0 +1,15 @@ +pushToOperandStackByType( + 'class', + $this + ->operation + ->getOperand(0) + ->getValue() + ->getArguments()[0] + ); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_newarray.php b/src/Compiler/Emulator/Mnemonics/_newarray.php new file mode 100644 index 00000000..c640d7e0 --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_newarray.php @@ -0,0 +1,15 @@ +accumulator->getStacks()) > 0) { + throw new AssembleStructureException( + 'Operand Stack is not empty.' + ); + } + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_saload.php b/src/Compiler/Emulator/Mnemonics/_saload.php new file mode 100644 index 00000000..a3a4767a --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_saload.php @@ -0,0 +1,15 @@ +accumulator->pushToOperandStack( + [ + VerificationTypeTag::ITEM_Integer, + ] + ); + } +} diff --git a/src/Compiler/Emulator/Mnemonics/_swap.php b/src/Compiler/Emulator/Mnemonics/_swap.php new file mode 100644 index 00000000..64ca1ba4 --- /dev/null +++ b/src/Compiler/Emulator/Mnemonics/_swap.php @@ -0,0 +1,15 @@ +operation->getOperand(0)->getValue()->getArguments()[0]; + } + + public function getSignature(int $index): array + { + return Formatter::parseSignature( + $this + ->operation + ->getOperand(0) + ->getValue() + ->getArguments()[$index] + ) ?? []; + } + + public function getClassName(): ?string + { + return $this->getSignature(2)[0]['type'] ?? null; + } + + public function getReturnType(): ?string + { + return $this->getSignature(2)[0]['type'] ?? null; + } + + public function pushToOperandStackByType(string $type, string $className = null): void + { + if (!TypeResolver::isPrimitive($type)) { + $this->accumulator->pushToOperandStack( + [ + VerificationTypeTag::ITEM_Object, + $this->getEnhancedConstantPool() + ->findClass($className), + ] + ); + return; + } + // TODO: Rename parseSignature + switch ($type) { + case Void_::class: + break; + case Int_::class: + $this->accumulator->pushToOperandStack( + [ + VerificationTypeTag::ITEM_Integer, + ] + ); + break; + default: + throw new AssembleStructureException( + 'Invalid signature type: ' . $this->getReturnType() + ); + } + } + + public function addFrame(): void + { + $branchTarget = $this->programCounter + $this->operation + ->getOperand(0) + ->getValue(); + + if ($this->operation->isEffectiveProgramCounter()) { + $this->accumulator + ->enableStack(false) + ->setEffectiveProgramCounter($branchTarget); + } + + $entry = FullFrame::init() + ->setBranchTarget($branchTarget) + ->setProgramCounter($this->programCounter); + + foreach ($this->accumulator->getLocals() as $local) { + $entry->addLocal( + $local[0], + ...array_slice($local, 1) + ); + } + + $this->accumulator + ->enableStack( + $this->operation->getOpCode() !== OpCode::_goto + ); + + foreach ($this->accumulator->getStacks() as $stack) { + $entry->addStack( + $stack[0], + ...array_slice($stack, 1) + ); + } + + $this->accumulator + ->addFrame($entry); + } +} diff --git a/src/Compiler/Lang/Assembler/AbstractAssembler.php b/src/Compiler/Lang/Assembler/AbstractAssembler.php new file mode 100644 index 00000000..094a37bf --- /dev/null +++ b/src/Compiler/Lang/Assembler/AbstractAssembler.php @@ -0,0 +1,56 @@ +node = $node; + } + + abstract public function assemble(); +} diff --git a/src/Compiler/Lang/Assembler/AssemblerInterface.php b/src/Compiler/Lang/Assembler/AssemblerInterface.php new file mode 100644 index 00000000..a02c29d7 --- /dev/null +++ b/src/Compiler/Lang/Assembler/AssemblerInterface.php @@ -0,0 +1,20 @@ +. + * @method AssemblerInterface setStore(Store $store) + * @method Store getStore() + */ +interface AssemblerInterface +{ + /** + * Must to set array or void. + * + * @return array|void + */ + public function assemble(); +} diff --git a/src/Compiler/Lang/Assembler/Bundler/AbstractBundler.php b/src/Compiler/Lang/Assembler/Bundler/AbstractBundler.php new file mode 100644 index 00000000..a8956a84 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Bundler/AbstractBundler.php @@ -0,0 +1,59 @@ +streamReader = $streamReader; + return $this; + } + + public function getStreamReader(): StreamReaderInterface + { + return $this->streamReader; + } +} diff --git a/src/Compiler/Lang/Assembler/Bundler/PHPStandardClass.php b/src/Compiler/Lang/Assembler/Bundler/PHPStandardClass.php new file mode 100644 index 00000000..c2662531 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Bundler/PHPStandardClass.php @@ -0,0 +1,158 @@ +constantPool = new ConstantPool(); + $this->constantPoolFinder = new ConstantPoolFinder($this->constantPool); + + $this->methods = new Methods(); + $this->fields = new Fields(); + + foreach (static::BUNDLE_PACKAGES as $package) { + /** + * @var PackageBundlerInterface $package + */ + $packageInstance = $package::factory() + ->setConstantPool($this->constantPool); + + foreach ($packageInstance->getDefinedConstants() as [$name, $value, $returnSignature]) { + $this->getEnhancedConstantPool() + ->addFieldref( + $className, + $name, + $descriptor = (new Descriptor()) + ->addArgument($returnSignature) + ->make() + ); + $this->fields + ->add( + (new Field( + (new FieldAccessFlag()) + ->enablePublic() + ->enableStatic() + ->enableFinal() + ->make(), + $className, + $name, + $descriptor + )) + ->setConstantPool($this->getConstantPool()) + ->setConstantPoolFinder($this->getConstantPoolFinder()) + ->setValue((string) $value) + ); + } + } + + $this->getEnhancedConstantPool() + ->addClass($className) + ->addClass(Object_::class); + + $this->assignStaticInitializer($className); + + $compiler = new Compiler( + (new ClassFileStructure()) + ->setMinorVersion($minorVersion) + ->setMajorVersion($majorVersion) + ->setAccessFlags( + (new \PHPJava\Compiler\Builder\Signatures\ClassAccessFlag()) + ->enableSuper() + ->enablePublic() + ->make() + ) + ->setThisClass( + $this->getEnhancedConstantPool() + ->findClass($className) + ) + ->setSuperClass( + $this->getEnhancedConstantPool() + ->findClass(Object_::class) + ) + ->setMethods( + $this + ->methods + ->toArray() + ) + ->setFields( + $this + ->fields + ->toArray() + ) + ->setAttributes( + (new Attributes()) + ->add( + (new PHPJavaSignature()) + ->setConstantPool($this->getConstantPool()) + ->setConstantPoolFinder($this->getConstantPoolFinder()) + ->beginPreparation() + ) + ->add( + (new SourceFile()) + ->setConstantPool($this->getConstantPool()) + ->setConstantPoolFinder($this->getConstantPoolFinder()) + ->setFileNameIndexInConstantPool( + $this->getConstantPoolFinder() + ->find( + Utf8Info::class, + $className . '.php' + ) + ) + ->beginPreparation() + ) + ->toArray() + ) + ->setConstantPool( + $this + ->constantPool + ->add(Utf8Info::factory($className . '.php')) + ->toArray() + ) + ); + + $compiler->compile( + $this + ->getStreamReader() + ->getDistributeStreamByClassPath($className) + ); + } +} diff --git a/src/Compiler/Lang/Assembler/Bundler/PackageBundlerInterface.php b/src/Compiler/Lang/Assembler/Bundler/PackageBundlerInterface.php new file mode 100644 index 00000000..0a7492f5 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Bundler/PackageBundlerInterface.php @@ -0,0 +1,20 @@ +getConstants() as $name => $value) { + $constantInfo = new \ReflectionClassConstant($this, $name); + try { + if ($phpDocument = $constantInfo->getDocComment()) { + $documentBlock = \phpDocumentor\Reflection\DocBlockFactory::createInstance() + ->create($phpDocument); + + // Need @export flag. + if (!$documentBlock->hasTag('export') || !$documentBlock->hasTag('var')) { + continue; + } + + /** + * @var \phpDocumentor\Reflection\DocBlock\Tags\Var_ $varType + */ + $varType = current($documentBlock->getTagsByName('var')); + $constants[] = [ + $name, + $value, + ltrim((string) $varType->getType(), '\\'), + ]; + } + } catch (\InvalidArgumentException $e) { + } + } + + return $constants; + } + + public function getDefinedMethods(): array + { + $methods = []; + $reflectionClass = new \ReflectionClass($this); + foreach ($reflectionClass->getMethods() as $method) { + } + return $methods; + } +} diff --git a/src/Compiler/Lang/Assembler/Bundler/Packages/Constants.php b/src/Compiler/Lang/Assembler/Bundler/Packages/Constants.php new file mode 100644 index 00000000..6441296b --- /dev/null +++ b/src/Compiler/Lang/Assembler/Bundler/Packages/Constants.php @@ -0,0 +1,82 @@ +className = implode( + '.', + array_merge( + $this->getNamespace() ?? [], + [$this->node->name->name] + ) + ); + [$majorVersion, $minorVersion] = SDKVersionResolver::resolveByVersion( + Runtime::PHP_COMPILER_JDK_VERSION + ); + + $this->constantPool = new ConstantPool(); + $this->constantPoolFinder = new ConstantPoolFinder($this->constantPool); + + $this->methods = new Methods(); + $this->fields = new Fields(); + + foreach ($this->node->getProperties() as $property) { + $this + ->bindParameters(FieldAssembler::factory($property)) + ->setCollection($this->fields) + ->assemble(); + } + + foreach ($this->node->getMethods() as $method) { + $store = new Store(); + if (!$method->isStatic()) { + $store->store( + 'this', + $this->getClassName() + ); + } + $this + ->setOperation(new Operation()) + ->setStore($store) + ->setReferenceCounter(new ReferenceCounter()) + ->bindParameters(MethodAssembler::factory($method)) + ->setCollection($this->methods) + ->assemble(); + } + + $this->getEnhancedConstantPool() + ->addClass($this->className) + ->addClass(Object_::class) + ->addClass(Runtime::PHP_STANDARD_CLASS_NAME); + + $this->assignStaticInitializer($this->className); + $this->assignDynamicInitializer($this->className); + + $compiler = new Compiler( + (new ClassFileStructure()) + ->setMinorVersion($minorVersion) + ->setMajorVersion($majorVersion) + ->setThisClass( + $this + ->getEnhancedConstantPool() + ->findClass($this->className) + ) + ->setSuperClass( + $this + ->getEnhancedConstantPool() + ->findClass(Object_::class) + ) + ->setMethods( + $this + ->methods + ->toArray() + ) + ->setFields( + $this + ->fields + ->toArray() + ) + ->setAttributes( + (new Attributes()) + ->add( + (new PHPJavaSignature()) + ->setConstantPool($this->getConstantPool()) + ->setConstantPoolFinder($this->getConstantPoolFinder()) + ->beginPreparation() + ) + ->add( + (new SourceFile()) + ->setConstantPool($this->getConstantPool()) + ->setConstantPoolFinder($this->getConstantPoolFinder()) + ->setFileNameIndexInConstantPool( + $this->getConstantPoolFinder() + ->find( + Utf8Info::class, + $this->node->name->name . '.php' + ) + ) + ->beginPreparation() + ) + ->toArray() + ) + ->setConstantPool( + $this->constantPool + ->add(Utf8Info::factory($this->node->name->name . '.php')) + ->toArray() + ) + ); + + $compiler->compile( + $this + ->getStreamReader() + ->getDistributeStreamByClassPath( + $this->className + ) + ); + } + + public function getMethods(): Methods + { + return $this->methods; + } + + public function getClassName(): string + { + return $this->className; + } +} diff --git a/src/Compiler/Lang/Assembler/ClassAssemblerInterface.php b/src/Compiler/Lang/Assembler/ClassAssemblerInterface.php new file mode 100644 index 00000000..0fc2d7d7 --- /dev/null +++ b/src/Compiler/Lang/Assembler/ClassAssemblerInterface.php @@ -0,0 +1,8 @@ +constantPool = $constantPool; + $this->constantPoolFinder = $constantPoolFinder; + } + + /** + * @return ConstantPoolEnhancer + */ + public static function factory(ConstantPool $constantPool, ConstantPoolFinder $constantPoolFinder): self + { + static $caches = []; + $cacheKey = spl_object_hash($constantPool) . spl_object_hash($constantPoolFinder); + + if (isset($caches[$cacheKey])) { + return $caches[$cacheKey]; + } + + return $caches[$cacheKey] = new static($constantPool, $constantPoolFinder); + } + + /** + * @return ConstantPoolEnhancer + */ + public function addClass(string $className): self + { + $className = Formatter::convertPHPNamespacesToJava($className, '/'); + + // Add UTF8 strings. + $this->constantPool + ->add( + Utf8Info::factory($className) + ); + + // Add Info Structure + $this->constantPool + ->add( + ClassInfo::factory( + $this->constantPoolFinder + ->find( + Utf8Info::class, + $className + ) + ) + ); + + return $this; + } + + /** + * @return ConstantPoolEnhancer + */ + public function addString(string $string): self + { + // Add UTF8 strings. + $this->constantPool + ->add( + Utf8Info::factory($string) + ); + + // Add Info structures. + $this->constantPool + ->add( + StringInfo::factory( + $this->constantPoolFinder + ->find( + Utf8Info::class, + $string + ) + ) + ); + + return $this; + } + + /** + * @return ConstantPoolEnhancer + */ + public function addInteger(int $value): self + { + $this->constantPool + ->add( + IntegerInfo::factory($value) + ); + + return $this; + } + + public function addNameAndType(string $name, string $descriptor): self + { + // Add UTF8 strings. + $this->constantPool + ->add( + Utf8Info::factory($name) + ) + ->add( + Utf8Info::factory($descriptor) + ); + + // Add NameAndType structures + $this->constantPool + ->add( + NameAndTypeInfo::factory( + $this->constantPoolFinder + ->find( + Utf8Info::class, + $name + ), + $this->constantPoolFinder + ->find( + Utf8Info::class, + $descriptor + ) + ) + ); + return $this; + } + + /** + * @return ConstantPoolEnhancer + */ + public function addFieldref(string $className, string $fieldName, string $fieldDescriptor): self + { + $className = Formatter::convertPHPNamespacesToJava($className, '/'); + + // Add NameAndType structure + $this->addNameAndType( + $fieldName, + $fieldDescriptor + ); + + // Add Info Structure + $this->constantPool + ->add( + FieldrefInfo::factory( + $this->constantPoolFinder + ->find( + ClassInfo::class, + $className + ), + $this->constantPoolFinder + ->find( + NameAndTypeInfo::class, + $fieldName, + $fieldDescriptor + ) + ) + ); + + return $this; + } + + /** + * @return ConstantPoolEnhancer + */ + public function addMethodref(string $className, string $methodName, string $methodDescriptor): self + { + $className = Formatter::convertPHPNamespacesToJava($className, '/'); + + $this->addNameAndType( + $methodName, + $methodDescriptor + ); + + // Add Info structures + $this->constantPool + ->add( + MethodrefInfo::factory( + $this->constantPoolFinder + ->find( + ClassInfo::class, + $className + ), + $this->constantPoolFinder + ->find( + NameAndTypeInfo::class, + $methodName, + $methodDescriptor + ) + ) + ); + return $this; + } + + public function addUtf8(string $text): self + { + $this->constantPool + ->add( + Utf8Info::factory($text) + ); + return $this; + } + + public function findString(string $text): FinderResultInterface + { + return $this->constantPoolFinder->find( + StringInfo::class, + $text + ); + } + + public function findUtf8(string $text): FinderResultInterface + { + return $this->constantPoolFinder->find( + Utf8Info::class, + $text + ); + } + + public function findClass(string $classPath): FinderResultInterface + { + return $this->constantPoolFinder->find( + ClassInfo::class, + Formatter::convertPHPNamespacesToJava( + $classPath, + '/' + ) + ); + } + + public function findMethod(string $classPath, string $methodName, string $descriptor): FinderResultInterface + { + return $this->constantPoolFinder->find( + MethodrefInfo::class, + Formatter::convertPHPNamespacesToJava( + $classPath, + '/' + ), + $methodName, + $descriptor + ); + } + + public function findField(string $classPath, string $methodName, string $descriptor): FinderResultInterface + { + return $this->constantPoolFinder->find( + FieldrefInfo::class, + Formatter::convertPHPNamespacesToJava( + $classPath, + '/' + ), + $methodName, + $descriptor + ); + } +} diff --git a/src/Compiler/Lang/Assembler/EntryPointClassAssembler.php b/src/Compiler/Lang/Assembler/EntryPointClassAssembler.php new file mode 100644 index 00000000..b63a3353 --- /dev/null +++ b/src/Compiler/Lang/Assembler/EntryPointClassAssembler.php @@ -0,0 +1,247 @@ +nodes = $nodes; + $this->constantPool = new ConstantPool(); + $this->constantPoolFinder = new ConstantPoolFinder($this->constantPool); + $this->operation = new \PHPJava\Compiler\Builder\Attributes\Architects\Operation(); + $this->store = new Store(); + } + + public function assemble(): void + { + $this->className = implode( + '.', + array_merge( + $this->getNamespace() ?? [], + [Runtime::PHP_ENTRY_POINT_CLASS_NAME] + ) + ); + + [$majorVersion, $minorVersion] = SDKVersionResolver::resolveByVersion( + Runtime::PHP_COMPILER_JDK_VERSION + ); + + $this->methods = new Methods(); + $this->fields = new Fields(); + + $this->getEnhancedConstantPool() + ->addClass($this->className) + ->addClass(Object_::class) + ->addClass(Runtime::PHP_STANDARD_CLASS_NAME); + + // Get operations in outside statements. + $operations = StatementProcessor::factory() + ->setStreamReader($this->getStreamReader()) + ->setEntryPointClassAssembler($this) + ->setStructureAccessorsLocator($this->getStructureAccessorsLocator()) + ->execute($this->nodes); + + $operations[] = \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_return + ); + + $compiler = new Compiler( + (new ClassFileStructure()) + ->setMinorVersion($minorVersion) + ->setMajorVersion($majorVersion) + ->setThisClass( + $this + ->getEnhancedConstantPool() + ->findClass($this->className) + ) + ->setSuperClass( + $this + ->getEnhancedConstantPool() + ->findClass(Object_::class) + ) + ->setMethods( + $this + ->methods + ->add( + (new Method( + (new \PHPJava\Compiler\Builder\Signatures\MethodAccessFlag()) + ->enablePublic() + ->enableStatic() + ->make(), + $this->getClassName(), + 'main', + Descriptor::factory() + ->addArgument( + \PHPJava\Packages\java\lang\String::class, + 1 + ) + ->setReturn(Void_::class) + ->make() + )) + ->setConstantPool($this->getConstantPool()) + ->setConstantPoolFinder($this->getConstantPoolFinder()) + ->setAttributes( + (new Attributes()) + ->add( + (new Code()) + ->setConstantPool($this->getConstantPool()) + ->setConstantPoolFinder($this->getConstantPoolFinder()) + ->setDefaultLocals(1) + ->setCode($operations) + ->setAttributes( + (new Attributes()) + ->add( + (new StackMapTable()) + ->setStore($this->getStore()) + ->setConstantPool($this->getConstantPool()) + ->setConstantPoolFinder($this->getConstantPoolFinder()) + ->setDefaultLocalVariables($this->getStore()->getAll()) + ->setOperation( + $operations + ) + ->beginPreparation() + ) + ->toArray() + ) + ->beginPreparation() + ) + ->toArray() + ) + ->beginPreparation() + ) + ->toArray() + ) + ->setFields( + $this + ->fields + ->toArray() + ) + ->setAttributes( + (new Attributes()) + ->add( + (new PHPJavaSignature()) + ->setConstantPool($this->getConstantPool()) + ->setConstantPoolFinder($this->getConstantPoolFinder()) + ->beginPreparation() + ) + ->add( + (new SourceFile()) + ->setConstantPool($this->getConstantPool()) + ->setConstantPoolFinder($this->getConstantPoolFinder()) + ->setFileNameIndexInConstantPool( + $this->getConstantPoolFinder() + ->find( + Utf8Info::class, + $this->className . '.php' + ) + ) + ->beginPreparation() + ) + ->toArray() + ) + ->setConstantPool( + $this->constantPool + ->add(Utf8Info::factory($this->className . '.php')) + ->toArray() + ) + ); + + $compiler->compile( + $this + ->getStreamReader() + ->getDistributeStreamByClassPath( + $this->className + ) + ); + } + + public function getMethods(): Methods + { + return $this->methods; + } + + public function getClassName(): string + { + return $this->className; + } +} diff --git a/src/Compiler/Lang/Assembler/Enum/NodeExtractorEnum.php b/src/Compiler/Lang/Assembler/Enum/NodeExtractorEnum.php new file mode 100644 index 00000000..e7cff703 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Enum/NodeExtractorEnum.php @@ -0,0 +1,10 @@ +create((string) $this->node->getDocComment()); + + if ($typedDocument === false) { + throw new AssembleStructureException( + 'A property is needed PHP document #' . $this->node->getStartLine() + ); + } + + /** + * @var \phpDocumentor\Reflection\DocBlock\Tags\Var_[] $varType + */ + $varType = $typedDocument->getTagsByName('var'); + [$variableName, $parameterInfo] = $this->parseFromDocument($varType[0]); + + $fieldAccessFlag = (new \PHPJava\Compiler\Builder\Signatures\FieldAccessFlag()); + + if ($this->node->isPublic()) { + $fieldAccessFlag->enablePublic(); + } + + if ($this->node->isPrivate()) { + $fieldAccessFlag->enablePrivate(); + } + + if ($this->node->isStatic()) { + $fieldAccessFlag->enableStatic(); + } + + if ($this->node->isProtected()) { + $fieldAccessFlag->enableProtected(); + } + + $fieldAccessFlag = $fieldAccessFlag->make(); + + $descriptor = Descriptor::factory() + ->addArgument($parameterInfo['type']) + ->make(); + + foreach ($this->node->props as $property) { + /** + * @var PropertyProperty $property + */ + $field = (new Field( + $fieldAccessFlag, + $this->getClassAssembler()->getClassName(), + (string) $property->name, + $descriptor + ))->setConstantPool($this->getConstantPool()) + ->setConstantPoolFinder($this->getConstantPoolFinder()) + ->setValue($property->default->value) + ->beginPreparation(); + + // Register a Fieldref to Constant Pool + $this->getEnhancedConstantPool() + ->addFieldref( + $this->getClassAssembler()->getClassName(), + (string) $property->name, + $descriptor + ); + + $this->getCollection() + ->add($field); + } + } +} diff --git a/src/Compiler/Lang/Assembler/MethodAssembler.php b/src/Compiler/Lang/Assembler/MethodAssembler.php new file mode 100644 index 00000000..4875b5ff --- /dev/null +++ b/src/Compiler/Lang/Assembler/MethodAssembler.php @@ -0,0 +1,222 @@ +methodName = $this->node->name->name; + + $this->attribute = new Attributes(); + + $parameters = $this->parseParameterFromNode($this->node); + + $descriptorObject = Descriptor::factory() + // TODO: All method returns void. Will implement return type. + ->setReturn(Void_::class); + + foreach ($parameters as $keyName => $value) { + // Fill local storage number. + $this->assembleAssignVariable( + $keyName, + $value['type'], + $value['dimensions_of_array'] + ); + + $descriptorObject->addArgument( + $value['type'], + $value['dimensions_of_array'] + ); + } + + $descriptor = $descriptorObject->make(); + + $methodAccessFlag = (new \PHPJava\Compiler\Builder\Signatures\MethodAccessFlag()); + + if ($this->node->isPublic()) { + $methodAccessFlag->enablePublic(); + } + + if ($this->node->isPrivate()) { + $methodAccessFlag->enablePrivate(); + } + + if ($this->node->isStatic()) { + $methodAccessFlag->enableStatic(); + } + + if ($this->node->isFinal()) { + $methodAccessFlag->enableFinal(); + } + + if ($this->node->isProtected()) { + $methodAccessFlag->enableProtected(); + } + + if ($this->node->isAbstract()) { + $methodAccessFlag->enableAbstract(); + } + + $method = ( + new Method( + $methodAccessFlag->make(), + $this->getClassAssembler()->getClassName(), + $this->methodName, + $descriptor + ) + ) + ->setConstantPool($this->getConstantPool()) + ->setConstantPoolFinder($this->getConstantPoolFinder()) + ->beginPreparation(); + + // Add to methods section + $this->getCollection() + ->add($method); + + $operations = []; + $defaultLocalVariables = $this->getStore()->getAll(); + + ArrayTool::concat( + $operations, + ...$this->bindParameters(StatementProcessor::factory()) + ->setMethodAssembler($this) + ->setClassAssembler($this->getClassAssembler()) + ->execute($this->node->getStmts()) + ); + + $operations[] = \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_return + ); + + // Start to add `dup` command + $referenceCounter = $this->getReferenceCounter(); + + foreach (($referenceCounter->get(ReferenceCounter::CLASS_HASH_MAP) ?? []) as $className => $info) { + $tmpOperations = array_slice( + $operations, + 0, + $info['frame_index'] + ); + + $operationConcatenater = Concat::factory($operations[$info['frame_index']]); + + foreach ($info['frames'] as $frame) { + $operationConcatenater + ->append( + \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_dup + ) + ); + } + + $operations = array_merge( + $tmpOperations, + [$operationConcatenater], + array_slice( + $operations, + ($info['frame_index'] + 1) + ) + ); + } + + // expand operations + foreach ($operations as $index => $operation) { + if (!($operation instanceof Concat)) { + continue; + } + $operations = array_merge( + array_slice( + $operations, + 0, + $index + ), + $operation->expand(), + array_slice( + $operations, + $index + 1 + ), + ); + } + + // Add operation codes for print expressions. + $this->getAttribute() + ->add( + (new Code()) + ->setConstantPool($this->getConstantPool()) + ->setConstantPoolFinder($this->getConstantPoolFinder()) + ->setDefaultLocals( + count($defaultLocalVariables) + ) + ->setCode($operations) + ->setAttributes( + (new Attributes()) + ->add( + (new StackMapTable()) + ->setStore($this->getStore()) + ->setConstantPool($this->getConstantPool()) + ->setConstantPoolFinder($this->getConstantPoolFinder()) + ->setDefaultLocalVariables($defaultLocalVariables) + ->setOperation( + $operations + ) + ->beginPreparation() + ) + ->toArray() + ) + ->beginPreparation() + ); + + $method + ->setAttributes( + $this->getAttribute() + ->toArray() + ); + } + + public function getAttribute(): Attributes + { + return $this->attribute; + } + + public function getMethodName(): string + { + return $this->methodName; + } +} diff --git a/src/Compiler/Lang/Assembler/ParameterServiceInterface.php b/src/Compiler/Lang/Assembler/ParameterServiceInterface.php new file mode 100644 index 00000000..9a8ada6f --- /dev/null +++ b/src/Compiler/Lang/Assembler/ParameterServiceInterface.php @@ -0,0 +1,39 @@ +constantPool = $this->constantPool ?? $this + ->getEntryPointClassAssembler() + ->getConstantPool(); + + $this->constantPoolFinder = $this->constantPoolFinder ?? $this + ->getEntryPointClassAssembler() + ->getConstantPoolFinder(); + + $this->operation = $this->operation ?? $this + ->getEntryPointClassAssembler() + ->getOperation(); + + $this->store = $this->store ?? $this + ->getEntryPointClassAssembler() + ->getStore(); + + // Return an empty array + return []; + } +} diff --git a/src/Compiler/Lang/Assembler/Processors/ExpressionProcessor.php b/src/Compiler/Lang/Assembler/Processors/ExpressionProcessor.php new file mode 100644 index 00000000..dd91a320 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Processors/ExpressionProcessor.php @@ -0,0 +1,315 @@ +assembleAssignFromNode($expression) + ); + break; + case \PhpParser\Node\Expr\PostInc::class: + ArrayTool::concat( + $operations, + ...$this->assemblePostIncFromNode($expression) + ); + break; + case \PhpParser\Node\Expr\PostDec::class: + ArrayTool::concat( + $operations, + ...$this->assemblePostDecFromNode($expression) + ); + break; + case \PhpParser\Node\Expr\Print_::class: + ArrayTool::concat( + $operations, + ...$this->assemblePrintFromNode($expression) + ); + break; + case \PhpParser\Node\Scalar\String_::class: + ArrayTool::concat( + $operations, + ...$this->assembleLoadStringFromNode( + $expression, + $classType + ) + ); + break; + case \PhpParser\Node\Scalar\LNumber::class: + ArrayTool::concat( + $operations, + ...$this->assembleLoadNumber( + $expression->value, + $classType + ) + ); + break; + case \PhpParser\Node\Expr\Variable::class: + ArrayTool::concat( + $operations, + ...$this->assembleLoadVariableFromNode( + $expression, + $classType + ) + ); + break; + case \PhpParser\Node\Expr\ConstFetch::class: + ArrayTool::concat( + $operations, + ...$this->assembleLoadConstFromNode( + $expression, + $classType + ) + ); + break; + case \PhpParser\Node\Expr\New_::class: + ArrayTool::concat( + $operations, + ...$this->assembleConstructorFromNode( + $expression, + $classType + ) + ); + break; + case \PhpParser\Node\Scalar\MagicConst\Class_::class: // __CLASS__ + case \PhpParser\Node\Scalar\MagicConst\Method::class: // __METHOD__ + case \PhpParser\Node\Scalar\MagicConst\Namespace_::class: // __NAMESPACE__ + case \PhpParser\Node\Scalar\MagicConst\Dir::class: // __DIR__ + case \PhpParser\Node\Scalar\MagicConst\File::class: // __FILE__ + case \PhpParser\Node\Scalar\MagicConst\Function_::class: // __FUNCTION__ + case \PhpParser\Node\Scalar\MagicConst\Trait_::class: // __TRAIT__ + case \PhpParser\Node\Scalar\MagicConst\Line::class: // __LINE__ + ArrayTool::concat( + $operations, + ...$this + ->assembleLoadMagicConstFromNode( + $expression, + $classType + ) + ); + break; + case \PhpParser\Node\Expr\BinaryOp\Concat::class: + ArrayTool::concat( + $operations, + ...$this->execute( + [ + // Left operator. + $expression->left, + $expression->right, + ], + $callback + ) + ); + break; + case \PhpParser\Node\Expr\BinaryOp\BooleanAnd::class: + case \PhpParser\Node\Expr\BinaryOp\BooleanOr::class: + case \PhpParser\Node\Expr\BinaryOp\Mul::class: + case \PhpParser\Node\Expr\BinaryOp\Div::class: + case \PhpParser\Node\Expr\BinaryOp\Minus::class: + case \PhpParser\Node\Expr\BinaryOp\Plus::class: + case \PhpParser\Node\Expr\BinaryOp\Mod::class: + ArrayTool::concat( + $operations, + ...$this->assembleCalculateOperationFromNode( + $expression->left, + $expression->right, + $this->convertNodeToOpCode($expression), + $callback + ) + ); + break; + case \PhpParser\Node\Expr\BinaryOp\Greater::class: + case \PhpParser\Node\Expr\BinaryOp\Smaller::class: + case \PhpParser\Node\Expr\BinaryOp\GreaterOrEqual::class: + case \PhpParser\Node\Expr\BinaryOp\SmallerOrEqual::class: + ArrayTool::concat( + $operations, + ...$this->assembleCalculateOperationFromNode( + $expression->left, + $expression->right, + OpCode::_isub, + $callback + ), + ...$this->assembleConditions( + $this->convertNodeToOpCode($expression), + [ + \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_iconst_1 + ), + ], + [ + \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_iconst_0 + ), + ] + ) + ); + break; + case \PhpParser\Node\Expr\BinaryOp\NotIdentical::class: + case \PhpParser\Node\Expr\BinaryOp\Identical::class: + /** + * @var \PhpParser\Node\Expr\BinaryOp\Identical $conditionNode + */ + // Left operator. + $leftOperands = $this->execute( + [$expression->left], + $callback + ); + + // Right operator. + $rightOperands = $this->execute( + [$expression->right], + $callback + ); + + $lastLeftOperand = array_slice($leftOperands, -1, 1)[0]; + $lastRightOperand = array_slice($rightOperands, -1, 1)[0]; + switch ([MnemonicResolver::resolveTypeByOpCode($lastLeftOperand), MnemonicResolver::resolveTypeByOpCode($lastRightOperand)]) { + case [Int_::class, Int_::class]: + ArrayTool::concat( + $operations, + ...$leftOperands, + ...$rightOperands + ); + + ArrayTool::concat( + $operations, + ...$this->assembleStaticCallMethodOperations( + Integer::class, + 'compare', + Descriptor::factory() + ->addArgument(Int_::class) + ->addArgument(Int_::class) + ->setReturn(Int_::class) + ->make() + ) + ); + + ArrayTool::concat( + $operations, + ...$this->assembleConditions( + $this->convertNodeToOpCode($expression), + [ + \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_iconst_1 + ), + ], + [ + \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_iconst_0 + ), + ] + ) + ); + break; + default: + throw new AssembleStructureException( + 'Unsupported operation type' + ); + } + break; + case \PhpParser\Node\Expr\MethodCall::class: + /** + * @var \PhpParser\Node\Expr\MethodCall $expression + */ + ArrayTool::concat( + $operations, + ...$this->assembleDynamicMethodCallFromNode( + $expression + ) + ); + break; + case \PhpParser\Node\Expr\StaticCall::class: + /** + * @var \PhpParser\Node\Expr\StaticCall $expression + */ + ArrayTool::concat( + $operations, + ...$this->assembleStaticMethodCallFromNode( + $expression + ) + ); + break; + default: + throw new AssembleStructureException( + 'Unsupported expression: ' . get_class($expression) + ); + } + + if ($callback !== null) { + $callback( + $operations, + $nodeType, + $classType + ); + } + } + return $operations; + } +} diff --git a/src/Compiler/Lang/Assembler/Processors/ProcessorInterface.php b/src/Compiler/Lang/Assembler/Processors/ProcessorInterface.php new file mode 100644 index 00000000..1d5323fc --- /dev/null +++ b/src/Compiler/Lang/Assembler/Processors/ProcessorInterface.php @@ -0,0 +1,28 @@ +getDeclaresAccessor() ?? new Declares($this->declares); + + $entryPointClassAssembler = EntryPointClassAssembler::factory(...$this->extractNodes($statement->stmts, NodeExtractorEnum::EXTRACT_OUTSIDES)) + ->setNamespace($statement->name->parts) + ->setConstantPool($entryPointConstantPool) + ->setConstantPoolFinder($entryPointConstantPoolFinder) + ->setDeclaresAccessor($declares) + ->setStructureAccessorsLocator($this->getStructureAccessorsLocator()) + ->setStreamReader($this->getStreamReader()); + + $this + ->setNamespace($statement->name->parts) + ->setConstantPool($entryPointConstantPool) + ->setConstantPoolFinder($entryPointConstantPoolFinder) + ->setStreamReader($this->getStreamReader()) + ->setDeclaresAccessor($declares) + ->setEntryPointClassAssembler($entryPointClassAssembler) + ->setStructureAccessorsLocator($this->getStructureAccessorsLocator()) + ->execute($statement->stmts); + + $entryPointClassAssembler + ->assemble(); + break; + case \PhpParser\Node\Stmt\Use_::class: + /** + * @var \PhpParser\Node\Stmt\Use_ $statement + */ + $this->imports[] = $statement; + // no break + case \PhpParser\Node\Stmt\Declare_::class: + /** + * @var \PhpParser\Node\Stmt\Declare_ $statement + */ + $this->declares[] = $statement; + break; + case \PhpParser\Node\Stmt\Class_::class: + /** + * @var \PhpParser\Node\Stmt\Class_ $statement + */ + $declares = $this->getDeclaresAccessor() ?? new Declares($this->declares); + + ClassAssembler::factory($statement) + ->setStreamReader($this->getStreamReader()) + ->setNamespace($this->getNamespace()) + ->setStructureAccessorsLocator($this->getStructureAccessorsLocator()) + ->setDeclaresAccessor($declares) + ->setImportsAccessor($this->getImportsAccessor() ?? new Imports($this->imports)) + ->assemble(); + break; + case \PhpParser\Node\Stmt\If_::class: + /** + * @var \PhpParser\Node\Stmt\If_ $statement + */ + $assembler = IfStatementAssembler::factory($statement); + break; + case \PhpParser\Node\Stmt\Echo_::class: + /** + * @var \PhpParser\Node\Stmt\Echo_ $statement + */ + $assembler = EchoStatementAssembler::factory($statement); + break; + case \PhpParser\Node\Stmt\Expression::class: + /** + * @var \PhpParser\Node\Stmt\Expression $statement + */ + $assembler = ExpressionStatementAssembler::factory($statement); + break; + case \PhpParser\Node\Stmt\For_::class: + /** + * @var \PhpParser\Node\Stmt\For_ $statement + */ + $assembler = ForStatementAssembler::factory($statement); + break; + case \PhpParser\Node\Stmt\Nop::class: + break; + default: + throw new AssembleStructureException( + 'Unknown statement: ' . get_class($statement) . ' on ' . get_class($this) + ); + } + if ($assembler === null) { + continue; + } + /** + * @var AbstractAssembler $assembler + */ + ArrayTool::concat( + $operations, + ...$this->bindParameters($assembler) + ->assemble() + ); + } + return $operations; + } +} diff --git a/src/Compiler/Lang/Assembler/Processors/Traits/AssignableFromNode.php b/src/Compiler/Lang/Assembler/Processors/Traits/AssignableFromNode.php new file mode 100644 index 00000000..063b1410 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Processors/Traits/AssignableFromNode.php @@ -0,0 +1,60 @@ +var->name; + $value = $expression->expr; + + $operations = []; + $expressionTypes = []; + + $expressions = $this->bindParameters(ExpressionProcessor::factory()) + ->execute( + [$value], + function (array &$operations, string $nodeType, ?string $classType) use (&$expressionTypes) { + if ($nodeType === \PhpParser\Node\Expr\BinaryOp\Concat::class || $classType === null) { + return; + } + $expressionTypes[] = $classType; + } + ); + + $type = current($expressionTypes); + + $localVariableAssignOperation = $this->assembleAssignVariable( + $name, + $type + ); + + ArrayTool::concat( + $operations, + ...$expressions, + ...$localVariableAssignOperation + ); + + return $operations; + } +} diff --git a/src/Compiler/Lang/Assembler/Processors/Traits/ConstLoadableFromNode.php b/src/Compiler/Lang/Assembler/Processors/Traits/ConstLoadableFromNode.php new file mode 100644 index 00000000..ee7437dc --- /dev/null +++ b/src/Compiler/Lang/Assembler/Processors/Traits/ConstLoadableFromNode.php @@ -0,0 +1,87 @@ +name->parts[0]; + + if (strtolower($constName) === 'true') { + $classType = Byte_::class; + return [ + \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_iconst_1 + ), + ]; + } + if (strtolower($constName) === 'false') { + $classType = Byte_::class; + return [ + \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_iconst_0 + ), + ]; + } + + $packageInstance = \PHPJava\Compiler\Lang\Assembler\Bundler\Packages\Constants::factory() + ->setConstantPool($this->getConstantPool()); + + $result = ArrayTool::containInMultipleDimension($packageInstance->getDefinedConstants(), 0, $constName); + if ($result === null) { + throw new AssembleStructureException( + 'The constant is not provided on PHP_STANDARD class (' + . Runtime::PHP_STANDARD_CLASS_NAME + . Runtime::PHP_STANDARD_CLASS_METHOD_PREFIX + . $constName . ')' + ); + } + + [, , $signature] = $result; + + $this->getEnhancedConstantPool() + ->addFieldref( + Runtime::PHP_STANDARD_CLASS_NAME, + $constName, + (new Descriptor()) + ->addArgument($signature) + ->make() + ); + + ArrayTool::concat( + $operations, + ...$this->assembleLoadStaticField( + Runtime::PHP_STANDARD_CLASS_NAME, + $constName, + $signature + ) + ); + + return $operations; + } +} diff --git a/src/Compiler/Lang/Assembler/Processors/Traits/ConstractableFromNode.php b/src/Compiler/Lang/Assembler/Processors/Traits/ConstractableFromNode.php new file mode 100644 index 00000000..e4d8c7f1 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Processors/Traits/ConstractableFromNode.php @@ -0,0 +1,90 @@ +class instanceof Node\Name)) { + throw new AssembleStructureException( + 'Unsupported class node type: ' . get_class($expression->class) + ); + } + + $classPath = implode( + '\\', + $expression->class->parts + ); + + $this->getReferenceCounter() + ->makeHashMap( + $classPath, + ReferenceCounter::CLASS_HASH_MAP, + $this->getOperation() + ->getCurrentIndex() + ); + + $classType = $classPath; + + $operations = []; + + $operations[] = \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_new, + Operand::factory( + Uint16::class, + $this->getEnhancedConstantPool() + ->findClass($classPath) + ), + ); + + // Dup from operand stack + $operations[] = \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_dup + ); + + // TODO: Implement to allows to pass arguments. + $descriptor = Descriptor::factory() + ->setReturn(Void_::class); + + // invoke special constructor for + $operations[] = \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_invokespecial, + Operand::factory( + Uint16::class, + $this->getEnhancedConstantPool() + ->findMethod( + $classPath, + '', + $descriptor + ->make() + ) + ), + ); + + return $operations; + } +} diff --git a/src/Compiler/Lang/Assembler/Processors/Traits/MagicConstLoadableFromNode.php b/src/Compiler/Lang/Assembler/Processors/Traits/MagicConstLoadableFromNode.php new file mode 100644 index 00000000..6954aaf3 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Processors/Traits/MagicConstLoadableFromNode.php @@ -0,0 +1,175 @@ +getClassAssembler(); + + $this->getEnhancedConstantPool() + ->addString($class->getClassName()); + + $operations[] = \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_ldc, + Operand::factory( + Uint8::class, + $this->getConstantPoolFinder()->find( + StringInfo::class, + $class->getClassName() + ) + ) + ); + break; + case \PhpParser\Node\Scalar\MagicConst\Method::class: // __METHOD__ + /** + * @var ClassAssembler $class + */ + $class = $this->getClassAssembler(); + + /** + * @var MethodAssembler $method + */ + $method = $this->getMethodAssembler(); + + $methodName = $class->getClassName() . '::' . $method->getMethodName(); + + $this->getEnhancedConstantPool() + ->addString($methodName); + + $operations[] = \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_ldc, + Operand::factory( + Uint8::class, + $this->getConstantPoolFinder()->find( + StringInfo::class, + $methodName + ) + ) + ); + break; + case \PhpParser\Node\Scalar\MagicConst\Namespace_::class: // __NAMESPACE__ + $namespace = '\\' . implode('\\', $this->getNamespace() ?? []); + $this->getEnhancedConstantPool() + ->addString($namespace); + + $operations[] = \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_ldc, + Operand::factory( + Uint8::class, + $this->getConstantPoolFinder()->find( + StringInfo::class, + $namespace + ) + ) + ); + + break; + case \PhpParser\Node\Scalar\MagicConst\Dir::class: // __DIR__ + $filePath = dirname($this->getStreamReader()->getFilePath()); + $this->getEnhancedConstantPool() + ->addString($filePath); + + $operations[] = \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_ldc, + Operand::factory( + Uint8::class, + $this->getConstantPoolFinder()->find( + StringInfo::class, + $filePath + ) + ) + ); + break; + case \PhpParser\Node\Scalar\MagicConst\File::class: // __FILE__ + $filePath = $this->getStreamReader()->getFilePath(); + $this->getEnhancedConstantPool() + ->addString($filePath); + + $operations[] = \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_ldc, + Operand::factory( + Uint8::class, + $this->getConstantPoolFinder()->find( + StringInfo::class, + $filePath + ) + ) + ); + break; + case \PhpParser\Node\Scalar\MagicConst\Function_::class: // __FUNCTION__ + /** + * @var MethodAssembler $method + */ + $method = $this->getMethodAssembler(); + + $methodName = $method->getMethodName(); + + $this->getEnhancedConstantPool() + ->addString($methodName); + + $operations[] = \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_ldc, + Operand::factory( + Uint8::class, + $this->getConstantPoolFinder()->find( + StringInfo::class, + $methodName + ) + ) + ); + break; + case \PhpParser\Node\Scalar\MagicConst\Trait_::class: // __TRAIT__ + throw new AssembleStructureException( + 'The __TRAIT__ is not implemented yet.' + ); + case \PhpParser\Node\Scalar\MagicConst\Line::class: // __LINE__ + $line = $expression->getLine(); + $this->getEnhancedConstantPool() + ->addInteger($line); + + $operations[] = \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_ldc, + Operand::factory( + Uint8::class, + $this->getConstantPoolFinder()->find( + IntegerInfo::class, + $line + ) + ) + ); + + break; + } + + $classType = String_::class; + + return $operations; + } +} diff --git a/src/Compiler/Lang/Assembler/Processors/Traits/MethodCallableFromNode.php b/src/Compiler/Lang/Assembler/Processors/Traits/MethodCallableFromNode.php new file mode 100644 index 00000000..5dfd50c8 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Processors/Traits/MethodCallableFromNode.php @@ -0,0 +1,160 @@ +var; + $methodName = $expression->name; + + if (!($var instanceof Variable)) { + throw new AssembleStructureException( + 'Unsupported callee type: ' . get_class($var) + ); + } + + if (!($methodName instanceof Identifier)) { + throw new AssembleStructureException( + 'Unsupported method name type: ' . get_class($methodName) + ); + } + + $descriptorObject = (new Descriptor()) + ->setReturn(Void_::class); + + [, $callFromClass] = $this->getStore() + ->get( + $var->name, + false + ); + + $operations = []; + + ArrayTool::concat( + $operations, + ...$this->assembleLoadLocalVariable( + $var->name + ), + ...$this->assembleCallMethodOperations( + $callFromClass, + $methodName->name, + $descriptorObject->make() + ) + ); + + return $operations; + } + + private function assembleStaticMethodCallFromNode(StaticCall $expression): array + { + $targetClass = strtolower($expression->class->parts[0]); + $methodName = $expression->name; + + if (!($methodName instanceof Identifier)) { + throw new AssembleStructureException( + 'Unsupported method name type: ' . get_class($methodName) + ); + } + + $callee = null; + + if ($targetClass === 'self') { + $callee = $this->getClassAssembler()->getClassName(); + } elseif ($targetClass === 'static') { + // TODO: Implement late static bindings. + $callee = $this->getClassAssembler()->getClassName(); + } else { + $callee = $targetClass; + } + + $descriptorObject = (new Descriptor()) + ->setReturn(Void_::class); + + $operations = []; + + $methodStructure = $this->getStructureAccessorsLocator() + ->getClassesStructureAccessor() + ->find($this->getClassAssembler()->getClassName()) + ->find((string) $methodName); + + $parameters = array_values( + $this->parseParameterFromNode( + $methodStructure + ) + ); + + foreach ($expression->args as $index => $arg) { + if (!($arg instanceof Arg)) { + throw new AssembleStructureException( + 'Does not support an argument type: ' . get_class($arg) . ' of #' . ($index + 1) + ); + } + $argValue = $arg->value; + + $descriptorObject->addArgument( + $parameters[$index]['type'] + ); + + $appendOperations = []; + switch ($parameters[$index]['type']) { + case String_::class: + $appendOperations = $this->assembleLoadString( + $argValue->value + ); + break; + case Int_::class: + $appendOperations = $this->assembleLoadNumber( + $argValue->value + ); + break; + default: + // TODO: support the other typed class. + throw new AssembleStructureException( + 'Unsupported type ' . $parameters[$index]['type'] + ); + } + + ArrayTool::concat( + $operations, + ...$appendOperations + ); + } + + ArrayTool::concat( + $operations, + ...$this->assembleStaticCallMethodOperations( + $callee, + $methodName->name, + $descriptorObject->make() + ) + ); + + return $operations; + } +} diff --git a/src/Compiler/Lang/Assembler/Processors/Traits/OperationCalculatableFromNode.php b/src/Compiler/Lang/Assembler/Processors/Traits/OperationCalculatableFromNode.php new file mode 100644 index 00000000..caeee071 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Processors/Traits/OperationCalculatableFromNode.php @@ -0,0 +1,42 @@ +execute( + [ + // Left operator. + $left, + + // Right operator. + $right, + ], + $callback + ), + ...[ + // Calculate operation + \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + $calculateOpCode + ), + ] + ); + return $operations; + } +} diff --git a/src/Compiler/Lang/Assembler/Processors/Traits/PostDecrementableFromNode.php b/src/Compiler/Lang/Assembler/Processors/Traits/PostDecrementableFromNode.php new file mode 100644 index 00000000..07e3e643 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Processors/Traits/PostDecrementableFromNode.php @@ -0,0 +1,57 @@ +var->name; + + [$localStorageNumber, $classType] = $this->getStore()->get($name); + + switch ($classType) { + case Byte_::class: + case Short_::class: + case Int_::class: + $operations[] = Operation::create( + OpCode::_iinc, + Operand::factory( + Uint8::class, + $localStorageNumber + ), + Operand::factory( + Uint8::class, + -1 + ) + ); + break; + default: + throw new AssembleStructureException( + 'Unsupported decrease a value: ' . $classType + ); + } + } +} diff --git a/src/Compiler/Lang/Assembler/Processors/Traits/PostIncrementableFromNode.php b/src/Compiler/Lang/Assembler/Processors/Traits/PostIncrementableFromNode.php new file mode 100644 index 00000000..c8945483 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Processors/Traits/PostIncrementableFromNode.php @@ -0,0 +1,62 @@ +var->name; + + [$localStorageNumber, $classType] = $this->getStore() + ->get( + $name + ); + + switch ($classType) { + case Byte_::class: + case Short_::class: + case Int_::class: + $operations[] = Operation::create( + OpCode::_iinc, + Operand::factory( + Uint8::class, + $localStorageNumber + ), + Operand::factory( + Uint8::class, + 1 + ) + ); + break; + default: + throw new AssembleStructureException( + 'Unsupported increase a value: ' . $classType + ); + } + + return $operations; + } +} diff --git a/src/Compiler/Lang/Assembler/Processors/Traits/PrintableFromNode.php b/src/Compiler/Lang/Assembler/Processors/Traits/PrintableFromNode.php new file mode 100644 index 00000000..3bec0011 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Processors/Traits/PrintableFromNode.php @@ -0,0 +1,31 @@ +assembleOutput([$expression->expr]); + + // Returns 1 by the PHP feature. + $operations[] = Operation::create(OpCode::_iconst_1); + + return $operations; + } +} diff --git a/src/Compiler/Lang/Assembler/Processors/Traits/StringLoadableFromNode.php b/src/Compiler/Lang/Assembler/Processors/Traits/StringLoadableFromNode.php new file mode 100644 index 00000000..84c18104 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Processors/Traits/StringLoadableFromNode.php @@ -0,0 +1,51 @@ +assembleLoadString($expression->value); + $classType = String_::class; + + return $operations; + } + + public function assembleLoadString(string $value): array + { + $operations = []; + // Add to ConstantPool + $this->getEnhancedConstantPool() + ->addString($value); + + $operations[] = \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_ldc, + Operand::factory( + Uint8::class, + $this->getConstantPoolFinder()->find( + StringInfo::class, + $value + ) + ) + ); + + return $operations; + } +} diff --git a/src/Compiler/Lang/Assembler/Processors/Traits/VariableLoadableFromNode.php b/src/Compiler/Lang/Assembler/Processors/Traits/VariableLoadableFromNode.php new file mode 100644 index 00000000..f8f921f8 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Processors/Traits/VariableLoadableFromNode.php @@ -0,0 +1,54 @@ +name; + [$storedNumber, $classType] = $this + ->getStore() + ->get($variableName); + + $loadOperation = MnemonicResolver::resolveLoadByNumberAndType($storedNumber, $classType); + + if (MnemonicResolver::inDefaultLocalStorageNumber($storedNumber)) { + $operations[] = \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + $loadOperation + ); + } else { + $operations[] = \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + $loadOperation, + Operand::factory( + Uint16::class, + $storedNumber + ) + ); + } + + return $operations; + } +} diff --git a/src/Compiler/Lang/Assembler/Statements/EchoStatementAssembler.php b/src/Compiler/Lang/Assembler/Statements/EchoStatementAssembler.php new file mode 100644 index 00000000..f91abfcb --- /dev/null +++ b/src/Compiler/Lang/Assembler/Statements/EchoStatementAssembler.php @@ -0,0 +1,38 @@ +assembleOutput($this->node->exprs); + } +} diff --git a/src/Compiler/Lang/Assembler/Statements/ExpressionStatementAssembler.php b/src/Compiler/Lang/Assembler/Statements/ExpressionStatementAssembler.php new file mode 100644 index 00000000..7ea73b77 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Statements/ExpressionStatementAssembler.php @@ -0,0 +1,26 @@ +bindParameters(ExpressionProcessor::factory()) + ->execute([$this->node->expr]); + } +} diff --git a/src/Compiler/Lang/Assembler/Statements/ForStatementAssembler.php b/src/Compiler/Lang/Assembler/Statements/ForStatementAssembler.php new file mode 100644 index 00000000..ac555f85 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Statements/ForStatementAssembler.php @@ -0,0 +1,137 @@ +calculateProgramCounterByOperationCodes( + $operations + ); + + $initialize = $this->bindParameters(ExpressionProcessor::factory()) + ->execute($this->node->init); + + ArrayTool::concat( + $operations, + ...$initialize + ); + + $startConditionsOffset = $this->calculateProgramCounterByOperationCodes( + $operations + ); + + $conditions = $this->bindParameters(ExpressionProcessor::factory()) + ->execute($this->node->cond); + + ArrayTool::concat( + $operations, + ...$conditions, + ...[ + ReplaceMarker::create( + OpCode::_ifeq, + Int16::class + ), + ], + ...$this->bindParameters(StatementProcessor::factory()) + ->execute($this->node->stmts) + ); + + $endConditionsOffset = $this->calculateProgramCounterByOperationCodes( + $operations + ); + + $loops = $this->bindParameters(ExpressionProcessor::factory()) + ->execute($this->node->loop); + + ArrayTool::concat( + $operations, + ...$loops + ); + + $finallyOffset = $this->calculateProgramCounterByOperationCodes( + $operations + ) - $startOffset - $startConditionsOffset; + + ArrayTool::concat( + $operations, + ...[ + ReplaceMarker::create( + OpCode::_goto, + Int16::class + ), + ] + ); + + $nextStatementOffset = $this->calculateProgramCounterByOperationCodes( + $operations + ); + + $currentOffset = 0; + // Replace with offset ReplaceMarker + foreach ($operations as &$operation) { + $currentOffset = $this->calculateProgramCounterByOperationCodes( + $operations, + $operation->getOpCode(), + $currentOffset + ); + + /** + * @var Operation|ReplaceMarker $operation + */ + if (!($operation instanceof ReplaceMarker)) { + continue; + } + + switch ($operation->getOpCode()) { + case OpCode::_ifeq: + $operation = Operation::create( + OpCode::_ifeq, + Operand::factory( + Int16::class, + $nextStatementOffset - $currentOffset + ) + ); + break; + case OpCode::_goto: + $operation = Operation::create( + OpCode::_goto, + Operand::factory( + Int16::class, + // Loop back + -$finallyOffset + ) + ); + break; + } + } + + return $operations; + } +} diff --git a/src/Compiler/Lang/Assembler/Statements/IfStatementAssembler.php b/src/Compiler/Lang/Assembler/Statements/IfStatementAssembler.php new file mode 100644 index 00000000..a3c94837 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Statements/IfStatementAssembler.php @@ -0,0 +1,185 @@ +bindParameters(ExpressionProcessor::factory()) + ->execute( + [$this->node->cond] + ); + + // Decide start offset for finding. + $startOffset = $this->calculateProgramCounterByOperationCodes( + $operations + ); + + ArrayTool::concat( + $operations, + ...[ + ReplaceMarker::create(OpCode::_ifeq, Int16::class), + ] + ); + + ArrayTool::concat( + $operations, + ...$this->bindParameters(StatementProcessor::factory()) + ->execute($this->node->stmts) + ); + + // Jump to finish + ArrayTool::concat( + $operations, + ...[ + ReplaceMarker::create(OpCode::_goto, Int16::class), + ] + ); + + $programCounters = [ + $this->calculateProgramCounterByOperationCodes( + $operations + ) - $startOffset, + ]; + + // Proceed elseif statements + foreach ($this->node->elseifs as $elseif) { + /** + * @var \PhpParser\Node\Stmt\ElseIf_ $elseif + */ + $elseIfStatementOperations = []; + + // Add condition + ArrayTool::concat( + $elseIfStatementOperations, + ...$this->bindParameters(ExpressionProcessor::factory()) + ->execute( + [$elseif->cond] + ) + ); + + // Decide start offset for finding. + $startOffset = $this->calculateProgramCounterByOperationCodes( + $elseIfStatementOperations + ); + + ArrayTool::concat( + $elseIfStatementOperations, + ...[ + ReplaceMarker::create(OpCode::_ifeq, Int16::class), + ], + ...$this->bindParameters(StatementProcessor::factory()) + ->execute($elseif->stmts), + // Jump to + ...[ + ReplaceMarker::create(OpCode::_goto, Int16::class), + ] + ); + + ArrayTool::concat( + $operations, + ...$elseIfStatementOperations + ); + + $programCounters[] = $this->calculateProgramCounterByOperationCodes( + $elseIfStatementOperations + ) - $startOffset; + } + + if (isset($this->node->else->stmts)) { + ArrayTool::concat( + $operations, + ...$this->bindParameters(StatementProcessor::factory()) + ->execute($this->node->else->stmts) + ); + } + + $finallyPosition = $this->calculateProgramCounterByOperationCodes( + $operations + ); + + // Replace markers + $currentOffset = 0; + foreach ($operations as &$operation) { + $currentOffset = $this->calculateProgramCounterByOperationCodes( + $operations, + $operation->getOpCode(), + $currentOffset + ); + + /** + * @var Operation|ReplaceMarker $operation + */ + if (!($operation instanceof ReplaceMarker)) { + continue; + } + + switch ($operation->getOpCode()) { + case OpCode::_ifeq: + $operation = Operation::create( + OpCode::_ifeq, + Operand::factory( + Int16::class, + array_shift($programCounters) + ) + ); + break; + case OpCode::_goto: + $operation = Operation::create( + OpCode::_goto, + Operand::factory( + Int16::class, + $finallyPosition - $currentOffset + ) + ); + break; + } + } + + return $operations; + } +} diff --git a/src/Compiler/Lang/Assembler/Statements/Maps/OperatorNode.php b/src/Compiler/Lang/Assembler/Statements/Maps/OperatorNode.php new file mode 100644 index 00000000..94996a45 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Statements/Maps/OperatorNode.php @@ -0,0 +1,23 @@ + [OpCode::_iand], + \PhpParser\Node\Expr\BinaryOp\BooleanOr::class => [OpCode::_ior], + \PhpParser\Node\Expr\BinaryOp\Plus::class => [OpCode::_iadd], + \PhpParser\Node\Expr\BinaryOp\Minus::class => [OpCode::_isub], + \PhpParser\Node\Expr\BinaryOp\Div::class => [OpCode::_idiv], + \PhpParser\Node\Expr\BinaryOp\Mul::class => [OpCode::_imul], + \PhpParser\Node\Expr\BinaryOp\Mod::class => [OpCode::_irem], + ]; + + public static function resolve(string $class): ?int + { + return static::OPERATORS_MAP[$class] ?? null; + } +} diff --git a/src/Compiler/Lang/Assembler/Statements/StatementAssemblerInterface.php b/src/Compiler/Lang/Assembler/Statements/StatementAssemblerInterface.php new file mode 100644 index 00000000..96f39cf5 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Statements/StatementAssemblerInterface.php @@ -0,0 +1,7 @@ +hashMap[$referenceCounterType][$classPath]['frames'][] = $frameIndex; + return $this; + } + + public function makeHashMap(string $classPath, int $referenceCounterType, int $frameIndex): self + { + $this->hashMap[$referenceCounterType][$classPath] = [ + 'frame_index' => $frameIndex, + 'frames' => [], + ]; + return $this; + } + + public function get(int $referenceCounterType): ?array + { + return $this->hashMap[$referenceCounterType] ?? null; + } +} diff --git a/src/Compiler/Lang/Assembler/Store/Store.php b/src/Compiler/Lang/Assembler/Store/Store.php new file mode 100644 index 00000000..1bfdeef5 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Store/Store.php @@ -0,0 +1,106 @@ + false, + // [iadf]store_1 / [iadf]load_1 + 1 => false, + // [iadf]store_2 / [iadf]load_2 + 2 => false, + // [iadf]store_3 / [iadf]load_3 + 3 => false, + ]; + + public function getAll(): array + { + return $this->hashTable; + } + + /** + * Store on memory. + * + * @param $value + * @return int the store method returns settled local storage number + */ + public function store(string $name, $value, int $dimensionsOfArray = 0): int + { + $availableLocalStorageNumber = $this->getAvailableLocalStorageNumber(); + + if ($availableLocalStorageNumber === -1) { + // Allocate localStorage + $storedNumber[] = false; + + // Retry to get + $availableLocalStorageNumber = $this->getAvailableLocalStorageNumber(); + } + + $this->hashTable[$name] = [$availableLocalStorageNumber, $value, $dimensionsOfArray]; + + // Use available localStorage number + $this->fill( + $availableLocalStorageNumber + ); + + // Returns available localstorage number. + return $availableLocalStorageNumber; + } + + public function fill(int $number): self + { + $this->storedNumber[$number] = true; + return $this; + } + + /** + * @throws AssembleStructureException + */ + public function get(string $name, bool $free = true): array + { + $value = $this->hashTable[$name] ?? null; + + if ($value === null) { + throw new AssembleStructureException( + 'Specified variable name is not set (' . $name . ').' + ); + } + + if ($free) { + $this->storedNumber[$value[0]] = false; + } + return $value; + } + + /** + * @throws AssembleStructureException + */ + public function getStoredNumber(string $name): int + { + $value = $this->hashTable[$name] ?? null; + if ($value === null) { + throw new AssembleStructureException( + 'Specified variable name is not set (' . $name . ').' + ); + } + + // 0 is a localstorage number. + return $value[0]; + } + + public function getAvailableLocalStorageNumber(): int + { + foreach ($this->storedNumber as $index => $storedNumber) { + if ($storedNumber === false) { + return $index; + } + } + return -1; + } +} diff --git a/src/Compiler/Lang/Assembler/Structure/Accessor/AbstractAccessor.php b/src/Compiler/Lang/Assembler/Structure/Accessor/AbstractAccessor.php new file mode 100644 index 00000000..2a088d8e --- /dev/null +++ b/src/Compiler/Lang/Assembler/Structure/Accessor/AbstractAccessor.php @@ -0,0 +1,18 @@ +nodes = $nodes; + } +} diff --git a/src/Compiler/Lang/Assembler/Structure/Accessor/AccessorInterface.php b/src/Compiler/Lang/Assembler/Structure/Accessor/AccessorInterface.php new file mode 100644 index 00000000..7950f05d --- /dev/null +++ b/src/Compiler/Lang/Assembler/Structure/Accessor/AccessorInterface.php @@ -0,0 +1,7 @@ +nodes as $node) { + $class = $this->findOnNode($node, $className); + if ($class !== null) { + return new Methods($class->stmts); + } + } + throw new AccessorException( + 'Not found ' . $className + ); + } + + protected function findOnNode(Node $node, string $path, array $tracedPath = []): ?Node\Stmt\Class_ + { + switch (get_class($node)) { + case \PhpParser\Node\Stmt\Use_::class: + // Nothing to do + break; + case \PhpParser\Node\Stmt\Namespace_::class: + /** + * @var \PhpParser\Node\Stmt\Namespace_ $node + */ + $tracedPath = array_merge($tracedPath, $node->name->parts); + foreach ($node->stmts as $statement) { + $result = $this + ->findOnNode( + $statement, + $path, + $tracedPath + ); + if ($result !== null) { + return $result; + } + } + break; + case \PhpParser\Node\Stmt\Class_::class: + /** + * @var \PhpParser\Node\Stmt\Class_ $node + */ + $targetedClassPath = '\\' . ltrim(implode('\\', $tracedPath) . '\\' . $node->name, '\\'); + if ($targetedClassPath === $path) { + return $node; + } + break; + default: + throw new AssembleStructureException( + 'Cannot find structure on class or ' . get_class($node) . ' is not supported statement.' + ); + } + return null; + } +} diff --git a/src/Compiler/Lang/Assembler/Structure/Accessor/Declares.php b/src/Compiler/Lang/Assembler/Structure/Accessor/Declares.php new file mode 100644 index 00000000..b0db7213 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Structure/Accessor/Declares.php @@ -0,0 +1,7 @@ +nodes as $node) { + /** + * @var \PhpParser\Node\Stmt\Use_ $node + */ + foreach ($node->uses as $useNode) { + /** + * @var \PhpParser\Node\Stmt\UseUse $useNode + */ + $merged = ltrim(implode('\\', $useNode->name->parts), '\\'); + $comparedResult = $isTopLevelNamespace + ? $this->compareAtFirst( + $path, + '\\' . $merged + ) + : $this->compareAtLast( + $path, + $merged + ); + + if ($comparedResult) { + return $merged; + } + } + } + throw new NotFoundImportException( + 'Not found import path for ' . $path + ); + } + + protected function compareAtFirst(string $path, string $target): bool + { + return (bool) preg_match('/\A' . preg_quote($path, '/') . '/', $target); + } + + protected function compareAtLast(string $path, string $target): bool + { + return (bool) preg_match('/' . preg_quote($path, '/') . '\z/', $target); + } +} diff --git a/src/Compiler/Lang/Assembler/Structure/Accessor/Methods.php b/src/Compiler/Lang/Assembler/Structure/Accessor/Methods.php new file mode 100644 index 00000000..ef30465a --- /dev/null +++ b/src/Compiler/Lang/Assembler/Structure/Accessor/Methods.php @@ -0,0 +1,24 @@ +nodes as $node) { + /** + * @var ClassMethod $node + */ + if ($node->name->name === $methodName) { + return $node; + } + } + throw new AccessorException( + 'Not found method ' . $methodName + ); + } +} diff --git a/src/Compiler/Lang/Assembler/Structure/Accessor/StructureAccessorsLocator.php b/src/Compiler/Lang/Assembler/Structure/Accessor/StructureAccessorsLocator.php new file mode 100644 index 00000000..e7ec0e67 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Structure/Accessor/StructureAccessorsLocator.php @@ -0,0 +1,34 @@ +classesStructureAccessor = $classesStructureAccessor; + $this->functionsStructureAccessor = $functionsStructureAccessor; + } + + public function getClassesStructureAccessor(): Classes + { + return $this->classesStructureAccessor; + } + + public function getFunctionsStructureAccessor(): Functions + { + return $this->functionsStructureAccessor; + } +} diff --git a/src/Compiler/Lang/Assembler/Traits/Bindable.php b/src/Compiler/Lang/Assembler/Traits/Bindable.php new file mode 100644 index 00000000..7f94ec83 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Traits/Bindable.php @@ -0,0 +1,54 @@ +setNamespace($this->getNamespace()) + ->setConstantPool($this->getConstantPool()) + ->setConstantPoolFinder($this->getConstantPoolFinder()) + ->setOperation($this->getOperation()) + ->setStreamReader($this->getStreamReader()) + ->setStructureAccessorsLocator($this->getStructureAccessorsLocator()) + ->setImportsAccessor($this->getImportsAccessor()) + ->setDeclaresAccessor($this->getDeclaresAccessor()) + ->setClassAssembler( + $this instanceof ClassAssembler + ? $this + : ($this->getClassAssembler() ?? $this->getEntryPointClassAssembler()) + ) + ->setMethodAssembler( + $this instanceof MethodAssembler + ? $this + : $this->getMethodAssembler() + ) + ->setStore($this->getStore()) + ->setReferenceCounter($this->getReferenceCounter()) + ->setEntryPointClassAssembler($this->getEntryPointClassAssembler()); + } +} diff --git a/src/Compiler/Lang/Assembler/Traits/Calculatable.php b/src/Compiler/Lang/Assembler/Traits/Calculatable.php new file mode 100644 index 00000000..98bd5914 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Traits/Calculatable.php @@ -0,0 +1,78 @@ +getOpCode() === $targetOpCode + && $counter >= $offset + ) { + return $counter; + } + + // Count opcode + $counter++; + + /** + * @var \PHPJava\Compiler\Builder\Generator\Operation\Operation|ReplaceMarker $operation + */ + foreach ($operation->getOperandTypes() as $operandType) { + /** + * @var TypeInterface $operandType + */ + switch ($operandType) { + case Int8::class: + case Uint8::class: + case Int16::class: + case Uint16::class: + case Int32::class: + case Uint32::class: + case Int64::class: + case Uint64::class: + $counter += $operandType::sizeOf(); + break; + default: + throw new AssembleStructureException( + 'Does not calculate count of values. Operand in ReplacerMaker is invalid.' + ); + } + } + } + return $counter; + } +} diff --git a/src/Compiler/Lang/Assembler/Traits/ClassAssemblerManageable.php b/src/Compiler/Lang/Assembler/Traits/ClassAssemblerManageable.php new file mode 100644 index 00000000..016fd27b --- /dev/null +++ b/src/Compiler/Lang/Assembler/Traits/ClassAssemblerManageable.php @@ -0,0 +1,21 @@ +classAssembler = $methodAssembler; + return $this; + } + + public function getClassAssembler(): ?ClassAssemblerInterface + { + return $this->classAssembler; + } +} diff --git a/src/Compiler/Lang/Assembler/Traits/CollectionManageable.php b/src/Compiler/Lang/Assembler/Traits/CollectionManageable.php new file mode 100644 index 00000000..f16d1a9a --- /dev/null +++ b/src/Compiler/Lang/Assembler/Traits/CollectionManageable.php @@ -0,0 +1,21 @@ +collection = $collection; + return $this; + } + + public function getCollection(): EntryCollectionInterface + { + return $this->collection; + } +} diff --git a/src/Compiler/Lang/Assembler/Traits/ConstantPoolManageable.php b/src/Compiler/Lang/Assembler/Traits/ConstantPoolManageable.php new file mode 100644 index 00000000..a99d1072 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Traits/ConstantPoolManageable.php @@ -0,0 +1,47 @@ +constantPool = $constantPool; + return $this; + } + + public function setConstantPoolFinder(?ConstantPoolFinder $constantPoolFinder): self + { + $this->constantPoolFinder = $constantPoolFinder; + return $this; + } + + public function getConstantPool(): ConstantPool + { + if (!isset($this->constantPool)) { + throw new AssembleStructureException( + 'The ConstantPool is not set. ' . + 'You must to set the ConstantPool.' + ); + } + return $this->constantPool; + } + + public function getConstantPoolFinder(): ConstantPoolFinder + { + if (!isset($this->constantPoolFinder)) { + throw new AssembleStructureException( + 'The ConstantPoolFinder is not set. ' . + 'You must to set the ConstantPoolFinder.' + ); + } + return $this->constantPoolFinder; + } +} diff --git a/src/Compiler/Lang/Assembler/Traits/DeclareManageable.php b/src/Compiler/Lang/Assembler/Traits/DeclareManageable.php new file mode 100644 index 00000000..ab34722c --- /dev/null +++ b/src/Compiler/Lang/Assembler/Traits/DeclareManageable.php @@ -0,0 +1,32 @@ +declaresAccessor = $declaresAccessor; + return $this; + } + + public function getDeclaresAccessor(): ?Declares + { + return $this->declaresAccessor; + } +} diff --git a/src/Compiler/Lang/Assembler/Traits/DynamicInitializerAssignable.php b/src/Compiler/Lang/Assembler/Traits/DynamicInitializerAssignable.php new file mode 100644 index 00000000..27117ddf --- /dev/null +++ b/src/Compiler/Lang/Assembler/Traits/DynamicInitializerAssignable.php @@ -0,0 +1,111 @@ +setReturn(Void_::class) + ->make(); + + try { + // Assert method. + $this->getEnhancedConstantPool() + ->findMethod( + $className, + '', + $descriptor + ) + ->getResult(false); + } catch (FinderException $e) { + // Add if not exists on the ConstantPool. + $this->getEnhancedConstantPool() + ->addMethodref( + Object_::class, + '', + $descriptor + ) + ->addMethodref( + $className, + '', + $descriptor + ); + + $dynamicInitializerOperations = [ + // Load `THIS` + \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_aload_0 + ), + + // Call parent + \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_invokespecial, + Operand::factory( + Uint16::class, + $this->getEnhancedConstantPool() + ->findMethod( + Object_::class, + '', + $descriptor + ) + ) + ), + \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_return + ), + ]; + + $this->methods + ->add( + (new Method( + (new MethodAccessFlag())->make(), + $className, + '', + $descriptor + )) + ->setConstantPool($this->getConstantPool()) + ->setConstantPoolFinder($this->getConstantPoolFinder()) + ->setAttributes( + (new Attributes()) + ->add( + (new Code()) + ->setConstantPool($this->getConstantPool()) + ->setConstantPoolFinder($this->getConstantPoolFinder()) + ->setDefaultLocals( + // This is the `THIS` parameter. + 1 + ) + ->setCode($dynamicInitializerOperations) + ->beginPreparation() + ) + ->toArray() + ) + ); + } + + return $this; + } +} diff --git a/src/Compiler/Lang/Assembler/Traits/Enhancer/ConstantPoolEnhanceable.php b/src/Compiler/Lang/Assembler/Traits/Enhancer/ConstantPoolEnhanceable.php new file mode 100644 index 00000000..2a500658 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Traits/Enhancer/ConstantPoolEnhanceable.php @@ -0,0 +1,16 @@ +getConstantPool(), + $this->getConstantPoolFinder() + ); + } +} diff --git a/src/Compiler/Lang/Assembler/Traits/Enhancer/Operation/Castable.php b/src/Compiler/Lang/Assembler/Traits/Enhancer/Operation/Castable.php new file mode 100644 index 00000000..886d0db4 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Traits/Enhancer/Operation/Castable.php @@ -0,0 +1,69 @@ +assembleClassConstructor( + $objectClass, + Descriptor::factory() + ->addArgument($classType) + ->setReturn(Void_::class) + ->make(), + true, + ...$arguments + ), + ...$this->assembleCallMethodOperations( + $objectClass, + 'toString', + Descriptor::factory() + ->setReturn( + \PHPJava\Packages\java\lang\String::class + ) + ->make(), + ...$arguments + ) + ); + + return $operations; + } +} diff --git a/src/Compiler/Lang/Assembler/Traits/Enhancer/Operation/ClassConstractable.php b/src/Compiler/Lang/Assembler/Traits/Enhancer/Operation/ClassConstractable.php new file mode 100644 index 00000000..491baee6 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Traits/Enhancer/Operation/ClassConstractable.php @@ -0,0 +1,73 @@ +getEnhancedConstantPool() + ->addClass( + $classPath + ) + ->addMethodref( + $classPath, + '', + $descriptor + ); + + // Add instantiate class + $operations[] = \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_new, + Operand::factory( + Uint16::class, + $this->getEnhancedConstantPool() + ->findClass( + $classPath + ) + ) + ); + + if ($returnClass === true) { + // Dup a class + $operations[] = \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_dup + ); + } + + ArrayTool::concat( + $operations, + ...$arguments + ); + + // Call + $operations[] = \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_invokespecial, + Operand::factory( + Uint16::class, + $this->getEnhancedConstantPool() + ->findMethod( + $classPath, + '', + $descriptor + ) + ) + ); + + return $operations; + } +} diff --git a/src/Compiler/Lang/Assembler/Traits/Enhancer/Operation/Conditionable.php b/src/Compiler/Lang/Assembler/Traits/Enhancer/Operation/Conditionable.php new file mode 100644 index 00000000..12529002 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Traits/Enhancer/Operation/Conditionable.php @@ -0,0 +1,95 @@ +calculateProgramCounterByOperationCodes( + $operations + ); + + ArrayTool::concat( + $operations, + ...[ + ReplaceMarker::create($conditionalOpCode, Int16::class), + ], + ...$ifStatementOperations, + ...[ + ReplaceMarker::create(OpCode::_goto, Int16::class), + ] + ); + + $programCounters = [ + $this->calculateProgramCounterByOperationCodes( + $operations + ) - $startOffset, + ]; + + ArrayTool::concat( + $operations, + ...$elseStatementOperations + ); + + $finallyPosition = $this->calculateProgramCounterByOperationCodes( + $operations + ); + + // Replace markers + $currentOffset = 0; + foreach ($operations as &$operation) { + /** + * @var Operation|ReplaceMarker $operation + */ + if (!($operation instanceof ReplaceMarker)) { + continue; + } + + $currentOffset = $this->calculateProgramCounterByOperationCodes( + $operations, + $operation->getOpCode(), + $currentOffset + ); + + switch ($operation->getOpCode()) { + case $conditionalOpCode: + $operation = Operation::create( + $operation->getOpCode(), + Operand::factory( + Int16::class, + array_shift($programCounters) + ) + ); + break; + case OpCode::_goto: + $operation = Operation::create( + OpCode::_goto, + Operand::factory( + Int16::class, + $this->calculateProgramCounterByOperationCodes($elseStatementOperations) + 1 + 2 + ) + )->enableEffectiveProgramCounter(true); + break; + } + } + return $operations; + } +} diff --git a/src/Compiler/Lang/Assembler/Traits/Enhancer/Operation/FieldAssignable.php b/src/Compiler/Lang/Assembler/Traits/Enhancer/Operation/FieldAssignable.php new file mode 100644 index 00000000..7b650cf7 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Traits/Enhancer/Operation/FieldAssignable.php @@ -0,0 +1,71 @@ +getEnhancedConstantPool() + ->addString($value); + $operations[] = Operation::create( + OpCode::_ldc, + Operand::factory( + Uint8::class, + $this->getEnhancedConstantPool() + ->findString($value) + ) + ); + } else { + switch ($parsedSignature[0]['type']) { + case Int_::class: + ArrayTool::concat( + $operations, + ...$this->assembleLoadNumber( + (int) $value + ) + ); + break; + default: + throw new AssembleStructureException( + 'Unsupported signature type: ' . $descriptor + ); + } + } + + $operations[] = Operation::create( + OpCode::_putstatic, + Operand::factory( + Uint16::class, + $this->getEnhancedConstantPool() + ->findField( + $className, + $fieldName, + $descriptor + ) + ) + ); + return $operations; + } +} diff --git a/src/Compiler/Lang/Assembler/Traits/Enhancer/Operation/FieldLoadable.php b/src/Compiler/Lang/Assembler/Traits/Enhancer/Operation/FieldLoadable.php new file mode 100644 index 00000000..12683781 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Traits/Enhancer/Operation/FieldLoadable.php @@ -0,0 +1,38 @@ +getEnhancedConstantPool() + ->findField( + $class, + $fieldName, + (new Descriptor()) + ->addArgument($signature) + ->make() + ) + ) + ); + + return $operations; + } +} diff --git a/src/Compiler/Lang/Assembler/Traits/Enhancer/Operation/LocalVariableAssignable.php b/src/Compiler/Lang/Assembler/Traits/Enhancer/Operation/LocalVariableAssignable.php new file mode 100644 index 00000000..daac9262 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Traits/Enhancer/Operation/LocalVariableAssignable.php @@ -0,0 +1,62 @@ +getStore()->store( + $variableName, + $classType, + $dimensionsOfArray + ); + + return $this->assembleStoreOperation( + $localStorageNumber, + $classType + ); + } + + public function assembleStoreOperation(int $localStorageNumber, string $classType) + { + $operations = []; + + // Add to operation code + $storeOperation = MnemonicResolver::resolveStoreByNumberAndType( + $localStorageNumber, + $classType + ); + + $operands = []; + + // Add a localstorage number operand. + if (!MnemonicResolver::inDefaultLocalStorageNumber($localStorageNumber)) { + $operands[] = Operand::factory( + Uint16::class, + $localStorageNumber + ); + } + + $operations[] = \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + $storeOperation, + ...$operands + ); + + return $operations; + } +} diff --git a/src/Compiler/Lang/Assembler/Traits/Enhancer/Operation/LocalVariableLoadable.php b/src/Compiler/Lang/Assembler/Traits/Enhancer/Operation/LocalVariableLoadable.php new file mode 100644 index 00000000..60272eed --- /dev/null +++ b/src/Compiler/Lang/Assembler/Traits/Enhancer/Operation/LocalVariableLoadable.php @@ -0,0 +1,38 @@ +getStore()->get( + $variableName, + false + ); + + $operations = []; + + $loadOperation = MnemonicResolver::resolveLoadByNumberAndType( + $localStorageNumber, + $classType + ); + $operations[] = \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + $loadOperation + ); + + return $operations; + } +} diff --git a/src/Compiler/Lang/Assembler/Traits/Enhancer/Operation/MethodCallable.php b/src/Compiler/Lang/Assembler/Traits/Enhancer/Operation/MethodCallable.php new file mode 100644 index 00000000..0015bf0f --- /dev/null +++ b/src/Compiler/Lang/Assembler/Traits/Enhancer/Operation/MethodCallable.php @@ -0,0 +1,80 @@ +getEnhancedConstantPool() + ->addClass($classPath) + ->addMethodref( + $classPath, + $methodName, + $descriptor + ); + + $operations = []; + + // Call a method + $operations[] = \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_invokevirtual, + Operand::factory( + Uint16::class, + $this->getEnhancedConstantPool() + ->findMethod( + $classPath, + $methodName, + $descriptor + ) + ) + ); + + return $operations; + } + + public function assembleStaticCallMethodOperations( + string $classPath, + string $methodName, + string $descriptor + ): array { + // Register required methods + $this->getEnhancedConstantPool() + ->addClass($classPath) + ->addMethodref( + $classPath, + $methodName, + $descriptor + ); + + $operations = []; + + // Call a method + $operations[] = \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_invokestatic, + Operand::factory( + Uint16::class, + $this->getEnhancedConstantPool() + ->findMethod( + $classPath, + $methodName, + $descriptor + ) + ) + ); + + return $operations; + } +} diff --git a/src/Compiler/Lang/Assembler/Traits/Enhancer/Operation/MethodConstantValueReturnable.php b/src/Compiler/Lang/Assembler/Traits/Enhancer/Operation/MethodConstantValueReturnable.php new file mode 100644 index 00000000..e413ecdf --- /dev/null +++ b/src/Compiler/Lang/Assembler/Traits/Enhancer/Operation/MethodConstantValueReturnable.php @@ -0,0 +1,64 @@ +getEnhancedConstantPool() + ->addString($value); + + $returnOperation = OpCode::_areturn; + + $operations[] = \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_ldc, + Operand::factory( + Uint8::class, + $this->getBundler() + ->getConstantPoolFinder() + ->find( + StringInfo::class, + $value + ) + ) + ); + } elseif (is_int($value)) { + $returnOperation = OpCode::_ireturn; + ArrayTool::concat( + $operations, + ...$this->assembleLoadNumber( + $value + ) + ); + } else { + throw new AssembleStructureException( + 'Unknown return type: ' . gettype($value) + ); + } + + $operations[] = \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + $returnOperation + ); + + return $operations; + } +} diff --git a/src/Compiler/Lang/Assembler/Traits/Enhancer/Operation/NamespaceManageable.php b/src/Compiler/Lang/Assembler/Traits/Enhancer/Operation/NamespaceManageable.php new file mode 100644 index 00000000..c6e67636 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Traits/Enhancer/Operation/NamespaceManageable.php @@ -0,0 +1,31 @@ +namespace = $namespace; + return $this; + } + + public function getNamespace(): ?array + { + return $this->namespace; + } +} diff --git a/src/Compiler/Lang/Assembler/Traits/Enhancer/Operation/NumberLoadable.php b/src/Compiler/Lang/Assembler/Traits/Enhancer/Operation/NumberLoadable.php new file mode 100644 index 00000000..73619ea7 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Traits/Enhancer/Operation/NumberLoadable.php @@ -0,0 +1,115 @@ += Byte_::MIN && $value <= Byte_::MAX) { + $type = Byte_::class; + $loadOperation = OpCode::_bipush; + $loadOperand = $value; + } elseif ($value >= Short_::MIN && $value <= Short_::MAX) { + $type = Short_::class; + $loadOperation = OpCode::_sipush; + $loadOperand = $value; + } elseif ($value >= Int_::MIN && $value <= Int_::MAX) { + $this->getEnhancedConstantPool() + ->addInteger($value); + $type = Int_::class; + $loadOperation = OpCode::_ldc; + $loadOperand = $this->getConstantPoolFinder() + ->find( + IntegerInfo::class, + $value + ); + } else { + throw new AssembleStructureException( + sprintf( + 'Unable to kind integer value: ' . $value + ) + ); + } + + $operations = []; + + if ($loadOperand !== null) { + $size = null; + switch ($loadOperation) { + case OpCode::_ldc: + case OpCode::_bipush: + $size = Uint8::class; + break; + case OpCode::_sipush: + $size = Uint16::class; + break; + default: + throw new AssembleStructureException( + 'Unsupported load operation: ' . $size + ); + } + + $loadOperand = Operand::factory( + $size, + $loadOperand + ); + } + + // Add to operation + + if ($loadOperand === null) { + $operations[] = \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + $loadOperation + ); + } else { + $operations[] = \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + $loadOperation, + $loadOperand + ); + } + + return $operations; + } +} diff --git a/src/Compiler/Lang/Assembler/Traits/Enhancer/Operation/Outputable.php b/src/Compiler/Lang/Assembler/Traits/Enhancer/Operation/Outputable.php new file mode 100644 index 00000000..f7243ac1 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Traits/Enhancer/Operation/Outputable.php @@ -0,0 +1,90 @@ +getConstantPoolFinder()->find( + FieldrefInfo::class, + Formatter::convertPHPNamespacesToJava( + \PHPJava\Packages\java\lang\System::class, + '/' + ), + 'out', + Descriptor::factory() + ->addArgument(\PHPJava\Packages\java\io\PrintStream::class) + ->make() + ) + ) + ); + + // Concat string. + $stringConcatOperations = $this->assembleConcatStringOperation( + ...$this->bindParameters(ExpressionProcessor::factory()) + ->execute( + $expressions + ) + ); + + ArrayTool::concat( + $operations, + ...$stringConcatOperations + ); + + $descriptor = Descriptor::factory() + ->addArgument(\PHPJava\Packages\java\lang\String::class) + ->setReturn( + Void_::class + ) + ->make(); + + $this->getEnhancedConstantPool() + ->addClass(\PHPJava\Packages\java\lang\System::class) + ->addClass(\PHPJava\Packages\java\io\PrintStream::class); + + $this->getEnhancedConstantPool() + ->addFieldref( + \PHPJava\Packages\java\lang\System::class, + 'out', + Descriptor::factory() + ->addArgument(\PHPJava\Packages\java\io\PrintStream::class) + ->make() + ); + + $operations[] = $this->assembleCallMethodOperations( + \PHPJava\Packages\java\io\PrintStream::class, + 'print', + $descriptor + )[0]; + + return $operations; + } +} diff --git a/src/Compiler/Lang/Assembler/Traits/Enhancer/Operation/StringConcatable.php b/src/Compiler/Lang/Assembler/Traits/Enhancer/Operation/StringConcatable.php new file mode 100644 index 00000000..0810781f --- /dev/null +++ b/src/Compiler/Lang/Assembler/Traits/Enhancer/Operation/StringConcatable.php @@ -0,0 +1,98 @@ +assembleCastToString( + MnemonicResolver::isLDCOperation($arguments[0]->getOpCode()) + ? $arguments[0]->getOperand(0)->getValue()->getType() + : MnemonicResolver::resolveTypeByOpCode($arguments[0]), + ...$arguments + ); + } catch (ResolverException $e) { + } + + return $arguments; + } + + $descriptor = Descriptor::factory() + ->addArgument( + \PHPJava\Packages\java\lang\String::class + ) + ->setReturn( + \PHPJava\Packages\java\lang\StringBuilder::class + ) + ->make(); + + $operations = []; + + ArrayTool::concat( + $operations, + ...$this->assembleClassConstructor( + \PHPJava\Packages\java\lang\StringBuilder::class, + Descriptor::factory() + ->setReturn( + Void_::class + ) + ->make(), + true + ) + ); + + foreach ($arguments as $operation) { + try { + $convertedOperations = $this->assembleCastToString( + MnemonicResolver::isLDCOperation($operation->getOpCode()) + ? $operation->getOperand(0)->getValue()->getType() + : MnemonicResolver::resolveTypeByOpCode($operation), + $operation + ); + } catch (ResolverException $e) { + $convertedOperations = [$operation]; + } + + ArrayTool::concat( + $operations, + // Load value. + ...$convertedOperations, + // Call the append method. + ...$this->assembleCallMethodOperations( + \PHPJava\Packages\java\lang\StringBuilder::class, + 'append', + $descriptor + ) + ); + } + + ArrayTool::concat( + $operations, + ...$this->assembleCallMethodOperations( + \PHPJava\Packages\java\lang\StringBuilder::class, + 'toString', + Descriptor::factory() + ->setReturn( + \PHPJava\Packages\java\lang\String::class + ) + ->make() + ) + ); + + return $operations; + } +} diff --git a/src/Compiler/Lang/Assembler/Traits/EntryPointClassAssemblerManageable.php b/src/Compiler/Lang/Assembler/Traits/EntryPointClassAssemblerManageable.php new file mode 100644 index 00000000..a6bcd425 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Traits/EntryPointClassAssemblerManageable.php @@ -0,0 +1,32 @@ +entryPointClassAssembler = $entryPointClassAssembler; + return $this; + } + + public function getEntryPointClassAssembler(): ?EntryPointClassAssembler + { + return $this->entryPointClassAssembler; + } +} diff --git a/src/Compiler/Lang/Assembler/Traits/ImportManageable.php b/src/Compiler/Lang/Assembler/Traits/ImportManageable.php new file mode 100644 index 00000000..7cd139ee --- /dev/null +++ b/src/Compiler/Lang/Assembler/Traits/ImportManageable.php @@ -0,0 +1,46 @@ +importsAccessor = $importsAccessor; + return $this; + } + + public function getImportsAccessor(): ?Imports + { + return $this->importsAccessor; + } + + public function convertWithImport(string $omittedImportPath): string + { + try { + return $this + ->getImportsAccessor() + ->resolvePath( + $omittedImportPath + ); + } catch (NotFoundImportException $e) { + return ltrim($omittedImportPath, '\\'); + } + } +} diff --git a/src/Compiler/Lang/Assembler/Traits/MethodAssemblerManageable.php b/src/Compiler/Lang/Assembler/Traits/MethodAssemblerManageable.php new file mode 100644 index 00000000..6807da8d --- /dev/null +++ b/src/Compiler/Lang/Assembler/Traits/MethodAssemblerManageable.php @@ -0,0 +1,21 @@ +methodAssembler = $methodAssembler; + return $this; + } + + public function getMethodAssembler(): ?MethodAssembler + { + return $this->methodAssembler; + } +} diff --git a/src/Compiler/Lang/Assembler/Traits/NodeConvertible.php b/src/Compiler/Lang/Assembler/Traits/NodeConvertible.php new file mode 100644 index 00000000..ff10685a --- /dev/null +++ b/src/Compiler/Lang/Assembler/Traits/NodeConvertible.php @@ -0,0 +1,45 @@ +filterExtractingNodes( + $nodes, + $extractType + ); + return $nodes; + } + + protected function filterExtractingNodes(array &$nodes, int $extractType): void + { + foreach ($nodes as $key => $node) { + switch (get_class($node)) { + case \PhpParser\Node\Stmt\Namespace_::class: + $this->filterExtractingNodes($node->stmts, $extractType); + break; + case \PhpParser\Node\Stmt\Declare_::class: + case \PhpParser\Node\Stmt\Use_::class: + case \PhpParser\Node\Stmt\Class_::class: + if ($extractType !== NodeExtractorEnum::EXTRACT_MODULES) { + unset($nodes[$key]); + } + break; + default: + if ($extractType !== NodeExtractorEnum::EXTRACT_OUTSIDES) { + unset($nodes[$key]); + } + break; + } + } + $nodes = array_values($nodes); + } +} diff --git a/src/Compiler/Lang/Assembler/Traits/OperationManageable.php b/src/Compiler/Lang/Assembler/Traits/OperationManageable.php new file mode 100644 index 00000000..45c53918 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Traits/OperationManageable.php @@ -0,0 +1,28 @@ +operation = $operation; + return $this; + } + + public function getOperation(): ?Operation + { + return $this->operation; + } +} diff --git a/src/Compiler/Lang/Assembler/Traits/ParameterParseable.php b/src/Compiler/Lang/Assembler/Traits/ParameterParseable.php new file mode 100644 index 00000000..1ebe2fd3 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Traits/ParameterParseable.php @@ -0,0 +1,155 @@ + literal type + ]; + foreach ($node->getParams() as $parameter) { + /** + * @var \PhpParser\Node\Param $parameter + */ + $parameters[$parameter->var->name] = $parameter->type; + } + + $paramOrdersTable = []; + + foreach ($node->params as $index => $param) { + /** + * @var \PhpParser\Node\Param $param + */ + $paramOrdersTable[$param->var->name] = $index; + } + + foreach (($node->getAttribute('comments') ?? []) as $commentAttribute) { + /** + * @var \PhpParser\Comment\Doc $commentAttribute + */ + $documentBlock = \phpDocumentor\Reflection\DocBlockFactory::createInstance() + ->create($commentAttribute->getText()); + + foreach ($documentBlock->getTagsByName('param') as $documentedParameter) { + /** + * @var Param $documentedParameter + */ + [$variableName, $parameterInfo] = $this->parseFromDocument($documentedParameter); + + if (!isset($paramOrdersTable[$variableName])) { + throw new AssembleStructureException( + 'Parameter is invalid $' . $variableName + ); + } + + // Update variable detail. + $parameters[$variableName] = $parameterInfo + [ + 'order' => $paramOrdersTable[$variableName], + ]; + } + } + + foreach ($parameters as $variableName => $parameter) { + if ($parameter === null) { + throw new AssembleStructureException( + 'Parameter length are mismatch or $' . $variableName . ' is not defined parameter type.' + ); + } + if ($parameter instanceof Node\Identifier) { + $type = $parameter->name; + $parameters[$variableName] = [ + 'type' => $this->convertWithImport( + Formatter::convertPHPPrimitiveTypeToJavaType($type) + ), + 'dimensions_of_array' => 0, + 'order' => $paramOrdersTable[$variableName], + ]; + } + } + + uasort( + $parameters, + static function ($a, $b) { + return $a['order'] <=> $b['order']; + } + ); + + return $parameters; + } + + public function parseFromDocument(TagWithType $documentedParameter): array + { + /** + * @var null|\phpDocumentor\Reflection\Types\Object_ $typeObject + */ + $typeObject = $documentedParameter + ->getType(); + $stringifiedType = (string) $typeObject; + + $type = $stringifiedType; + + if ($typeObject instanceof \phpDocumentor\Reflection\Types\Array_) { + $typeObject = $typeObject + ->getValueType(); + } + + if ($typeObject instanceof \phpDocumentor\Reflection\Types\Object_) { + $fullPath = (string) $typeObject->getFqsen(); + + $type = $typeObject + ->getFqsen() + ->getName(); + + // FIXED: phpDocumentor has an omitted path completion problem + // This statement fix it. + if ($fullPath !== '\\' . ((string) $type)) { + $type = $fullPath; + } + } + + $variableName = $documentedParameter->getVariableName(); + + // Update variable detail. + return [ + $variableName === '' + ? null + : $variableName, + [ + 'type' => $this->convertWithImport( + Formatter::convertPHPPrimitiveTypeToJavaType( + str_replace( + '[]', + '', + $type + ) + ) + ), + 'dimensions_of_array' => substr_count( + $stringifiedType, + '[]' + ), + ], + ]; + } +} diff --git a/src/Compiler/Lang/Assembler/Traits/ReferenceCounterManageable.php b/src/Compiler/Lang/Assembler/Traits/ReferenceCounterManageable.php new file mode 100644 index 00000000..92b5a5a4 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Traits/ReferenceCounterManageable.php @@ -0,0 +1,31 @@ +referenceCounter = $referenceCounter; + return $this; + } + + public function getReferenceCounter(): ?ReferenceCounter + { + return $this->referenceCounter; + } +} diff --git a/src/Compiler/Lang/Assembler/Traits/StaticInitializerAssignable.php b/src/Compiler/Lang/Assembler/Traits/StaticInitializerAssignable.php new file mode 100644 index 00000000..86641022 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Traits/StaticInitializerAssignable.php @@ -0,0 +1,88 @@ +fields->length() === 0) { + return $this; + } + $descriptor = (new Descriptor()) + ->setReturn(Void_::class) + ->make(); + + // The is not required methodref, it is not possible to invoke in the class. + // But it need a method name and descriptor info in UTF-8 format. + // See: https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-2.html#jvms-2.9 + $this->getEnhancedConstantPool() + ->addUtf8('') + ->addUtf8($descriptor); + + $staticInitializerOperations = []; + foreach ($this->fields as $field) { + /** + * @var Field $field + */ + ArrayTool::concat( + $staticInitializerOperations, + ...$this->assembleAssignStaticField( + $field->getClassName(), + $field->getName(), + $field->getValue(), + $field->getDescriptor() + ) + ); + } + + $staticInitializerOperations[] = \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_return + ); + + // Define for initialize static fields. + $this->methods + ->add( + (new Method( + (new MethodAccessFlag()) + ->enableStatic() + ->make(), + $className, + '', + $descriptor + )) + ->setConstantPool($this->getConstantPool()) + ->setConstantPoolFinder($this->getConstantPoolFinder()) + ->setAttributes( + (new Attributes()) + ->add( + (new Code()) + ->setConstantPool($this->getConstantPool()) + ->setConstantPoolFinder($this->getConstantPoolFinder()) + ->setCode($staticInitializerOperations) + ->beginPreparation() + ) + ->toArray() + ) + ); + return $this; + } +} diff --git a/src/Compiler/Lang/Assembler/Traits/StoreManageable.php b/src/Compiler/Lang/Assembler/Traits/StoreManageable.php new file mode 100644 index 00000000..5675d134 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Traits/StoreManageable.php @@ -0,0 +1,31 @@ +store = $store; + return $this; + } + + public function getStore(): ?Store + { + return $this->store; + } +} diff --git a/src/Compiler/Lang/Assembler/Traits/StreamManageable.php b/src/Compiler/Lang/Assembler/Traits/StreamManageable.php new file mode 100644 index 00000000..d18046a7 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Traits/StreamManageable.php @@ -0,0 +1,32 @@ +streamReader = $streamReader; + return $this; + } + + public function getStreamReader(): StreamReaderInterface + { + return $this->streamReader; + } +} diff --git a/src/Compiler/Lang/Assembler/Traits/StructureAccessorsLocatorManageable.php b/src/Compiler/Lang/Assembler/Traits/StructureAccessorsLocatorManageable.php new file mode 100644 index 00000000..93bdfa90 --- /dev/null +++ b/src/Compiler/Lang/Assembler/Traits/StructureAccessorsLocatorManageable.php @@ -0,0 +1,32 @@ +structureAccessorsLocator = $structureAccessorsLocator; + return $this; + } + + public function getStructureAccessorsLocator(): StructureAccessorsLocator + { + return $this->structureAccessorsLocator; + } +} diff --git a/src/Compiler/Lang/Assembler/Traits/Validatable.php b/src/Compiler/Lang/Assembler/Traits/Validatable.php new file mode 100644 index 00000000..269b7e0b --- /dev/null +++ b/src/Compiler/Lang/Assembler/Traits/Validatable.php @@ -0,0 +1,21 @@ + $operation) { + if (!($operation instanceof Operation)) { + throw new AssembleStructureException( + 'The index ' . $index . ' of entries is not instantiated by ' . Operation::class + ); + } + } + return $this; + } +} diff --git a/src/Compiler/Lang/PackageAssembler.php b/src/Compiler/Lang/PackageAssembler.php new file mode 100644 index 00000000..5f323f3d --- /dev/null +++ b/src/Compiler/Lang/PackageAssembler.php @@ -0,0 +1,96 @@ +stream = $stream; + + try { + $this->abstractSyntaxTree = (new ParserFactory()) + ->create( + ParserFactory::PREFER_PHP7 + ) + ->parse( + $stream->getCode() + ); + } catch (Error $error) { + throw new AssembleStructureException( + 'Failed to coordinate a class file. (reason: ' . $error->getMessage() . ')' + ); + } + } + + public function assemble(): self + { + PHPStandardClass::factory() + ->setStreamReader($this->stream) + ->assemble(); + + $modules = $this->extractNodes( + $this->abstractSyntaxTree, + NodeExtractorEnum::EXTRACT_MODULES + ); + $outsides = $this->extractNodes( + $this->abstractSyntaxTree, + NodeExtractorEnum::EXTRACT_OUTSIDES + ); + + $entryPointConstantPool = new ConstantPool(); + $entryPointConstantPoolFinder = new ConstantPoolFinder( + $entryPointConstantPool + ); + + $structureAccessorsLocator = new StructureAccessorsLocator( + new Classes($this->abstractSyntaxTree), + new Functions($this->abstractSyntaxTree) + ); + + $entryPointClassAssembler = EntryPointClassAssembler::factory(...$outsides) + ->setConstantPool($entryPointConstantPool) + ->setConstantPoolFinder($entryPointConstantPoolFinder) + ->setStructureAccessorsLocator($structureAccessorsLocator) + ->setStreamReader($this->stream); + + StatementProcessor::factory() + ->setStreamReader($this->stream) + ->setEntryPointClassAssembler($entryPointClassAssembler) + ->setStructureAccessorsLocator($structureAccessorsLocator) + ->execute($modules); + + // Assemble an entrypoint class. + $entryPointClassAssembler + ->assemble(); + + return $this; + } +} diff --git a/src/Compiler/Lang/Stream/AbstractStream.php b/src/Compiler/Lang/Stream/AbstractStream.php new file mode 100644 index 00000000..5ba1de91 --- /dev/null +++ b/src/Compiler/Lang/Stream/AbstractStream.php @@ -0,0 +1,59 @@ +source; + } + + public function getFilePath(): string + { + if ($this->filePath === null) { + throw new AssembleStructureException( + 'File path is not specified.' + ); + } + return $this->filePath; + } + + public function setDistributeDirectory(string $path): StreamReaderInterface + { + $this->distributeDirectory = $path; + return $this; + } + + /** + * @return resource + */ + public function getDistributeStreamByClassPath(string $classPath) + { + if (!isset($this->distributeDirectory)) { + throw new AssembleStructureException( + 'Distribution directory is not set. ' . + 'You must to set distribution directory with the `setDistributeDirectory` method on StreamInterface.' + ); + } + + [$namespace, $className] = Formatter::getNamespaceAndClassName($classPath); + $path = $this->distributeDirectory . '/' . implode('/', $namespace); + if (!is_dir($path)) { + mkdir($path, 0777, true); + } + return fopen( + $path . '/' . $className . '.class', + 'w+' + ); + } +} diff --git a/src/Compiler/Lang/Stream/FileStream.php b/src/Compiler/Lang/Stream/FileStream.php new file mode 100644 index 00000000..77c7e3cb --- /dev/null +++ b/src/Compiler/Lang/Stream/FileStream.php @@ -0,0 +1,12 @@ +filePath = realpath($source); + $this->source = file_get_contents($source); + } +} diff --git a/src/Compiler/Lang/Stream/InlineCodeStream.php b/src/Compiler/Lang/Stream/InlineCodeStream.php new file mode 100644 index 00000000..51c76035 --- /dev/null +++ b/src/Compiler/Lang/Stream/InlineCodeStream.php @@ -0,0 +1,11 @@ +source = $source; + } +} diff --git a/src/Compiler/Lang/Stream/StreamReaderInterface.php b/src/Compiler/Lang/Stream/StreamReaderInterface.php new file mode 100644 index 00000000..04d77b4e --- /dev/null +++ b/src/Compiler/Lang/Stream/StreamReaderInterface.php @@ -0,0 +1,19 @@ +getInvoker() + ->construct(...$arguments) + ->getJavaClass(); + } +} diff --git a/src/Core/Extended/Classifiable.php b/src/Core/Extended/Classifiable.php new file mode 100644 index 00000000..3207ea16 --- /dev/null +++ b/src/Core/Extended/Classifiable.php @@ -0,0 +1,13 @@ +debugTraces[] = $log; + return $this; + } + + public function debug(): void + { + $isEnabledTrace = $this->options['operations']['enable_trace'] ?? GlobalOptions::get('operations.enable_trace') ?? Runtime::OPERATIONS_ENABLE_TRACE; + if (!$isEnabledTrace) { + throw new DebugTraceIsDisabledException( + 'Debug trace is disabled. If you want to show debug trace then enable to `enable_trace` option.' + ); + } + $cpInfo = $this->getConstantPool(); + foreach ($this->debugTraces as $debugTraces) { + printf("[method]\n"); + printf(Formatter::beatifyMethodFromConstantPool($debugTraces['method'], $this->getConstantPool()) . "\n"); + printf("\n"); + printf("[code]\n"); + + $codeCounter = 0; + printf( + "%s\n", + implode( + "\n", + array_map( + function ($codes) use (&$codeCounter, &$debugTraces) { + return implode( + ' ', + array_map( + function ($code) use (&$codeCounter, &$debugTraces) { + $isMnemonic = in_array($codeCounter, $debugTraces['mnemonic_indexes']); + $codeCounter++; + return ($isMnemonic ? "\e[1m\e[35m" : '') . "<0x{$code}>" . ($isMnemonic ? "\e[m" : ''); + }, + $codes + ) + ); + }, + array_chunk(str_split(bin2hex($debugTraces['raw_code']), 2), 20) + ) + ) + ); + printf("\n"); + printf("[executed]\n"); + + printf( + "% 8s | %-6.6s | %-20.20s | %-10.10s | %-15.15s\n", + 'PC', + 'OPCODE', + 'MNEMONIC', + 'OPERANDS', + 'LOCAL STORAGE' + ); + + $line = sprintf( + "%8s+%8s+%22s+%12s+%17s\n", + '---------', + '--------', + '----------------------', + '------------', + '-----------------' + ); + + printf($line); + + foreach ($debugTraces['executed'] as [$opcode, $mnemonic, $localStorage, $stacks, $pointer]) { + printf( + "% 8s | 0x%02X | %-20.20s | %-10.10s | %-15.15s\n", + (int) $pointer, + $opcode, + // Remove prefix + ltrim($mnemonic, '_'), + count($stacks), + count($localStorage) + ); + } + + printf($line); + printf("\n"); + } + } +} diff --git a/src/Core/Extended/Finalizable.php b/src/Core/Extended/Finalizable.php new file mode 100644 index 00000000..14d7ae88 --- /dev/null +++ b/src/Core/Extended/Finalizable.php @@ -0,0 +1,16 @@ +debugTool)) { + return; + } + $this->debugTool->getLogger()->info( + 'Spent time: ' . (microtime(true) - $this->startTime) . ' sec.' + ); + } +} diff --git a/src/Core/Extended/InvokerProvidable.php b/src/Core/Extended/InvokerProvidable.php new file mode 100644 index 00000000..7b609e2e --- /dev/null +++ b/src/Core/Extended/InvokerProvidable.php @@ -0,0 +1,18 @@ +invoker; + } +} diff --git a/src/Core/Extended/OptionExtendable.php b/src/Core/Extended/OptionExtendable.php new file mode 100644 index 00000000..0915c129 --- /dev/null +++ b/src/Core/Extended/OptionExtendable.php @@ -0,0 +1,19 @@ +options[$key])) { + return $this->options[$key]; + } + return $this->options; + } +} diff --git a/src/Core/JVM/Accessor.php b/src/Core/JVM/Accessor.php new file mode 100644 index 00000000..2161f188 --- /dev/null +++ b/src/Core/JVM/Accessor.php @@ -0,0 +1,46 @@ +methodAccessor = new $targetedMethodAccessorClass($invoker, $methods, $options); + $this->fieldAccessor = new $targetedFieldAccessorClass($invoker, $fields); + } + + public function getFields(): FieldInterface + { + return $this->fieldAccessor; + } + + public function getMethods(): InvokerInterface + { + return $this->methodAccessor; + } +} diff --git a/src/Core/JVM/AccessorInterface.php b/src/Core/JVM/AccessorInterface.php new file mode 100644 index 00000000..2f9d4517 --- /dev/null +++ b/src/Core/JVM/AccessorInterface.php @@ -0,0 +1,13 @@ +reader = $reader; + for ($i = 0; $i < $entries; $i++) { + // not implemented, read only + $entry = (new AttributeInfo($reader)) + ->setConstantPool($constantPool) + ->setDebugTool($debugTool); + $entry->execute(); + + $this->entries[] = $entry; + } + } + + /** + * @return AttributeInfo[] + */ + public function getEntries(): array + { + return $this->entries; + } + + /** + * @param int $offset + * @return bool + */ + public function offsetExists($offset) + { + return isset($this->entries[$offset]); + } + + /** + * @param int $offset + * @return AttributeInfo + */ + public function offsetGet($offset) + { + return $this->entries[$offset]; + } + + /** + * @return int + */ + public function count() + { + return count($this->entries); + } + + /** + * @throws ReadOnlyException + */ + public function offsetSet($offset, $value) + { + throw new ReadOnlyException('You cannot rewrite datum. The Attribute Pool is read-only.'); + } + + /** + * @throws ReadOnlyException + */ + public function offsetUnset($offset) + { + throw new ReadOnlyException('You cannot rewrite datum. The Attribute Pool is read-only.'); + } + + /** + * @return \ArrayIterator + */ + public function getIterator() + { + return new \ArrayIterator($this->entries); + } +} diff --git a/src/Core/JVM/Cache/Cache.php b/src/Core/JVM/Cache/Cache.php new file mode 100644 index 00000000..4d75809d --- /dev/null +++ b/src/Core/JVM/Cache/Cache.php @@ -0,0 +1,19 @@ +items[$key])) { + return $this->items[$key]; + } + return $this->items[$key] = $pushFunction(...$parameters); + } +} diff --git a/src/Core/JVM/Cache/HeapCache.php b/src/Core/JVM/Cache/HeapCache.php new file mode 100644 index 00000000..d2936199 --- /dev/null +++ b/src/Core/JVM/Cache/HeapCache.php @@ -0,0 +1,7 @@ +reader = $reader; + + for ($i = 1; $i < $entries; $i++) { + $this->entries[$i] = $this->read( + $reader->getReader()->readUnsignedByte() + ); + + // execute + $this->entries[$i]->execute(); + + // Java's Long and Double problem. + if ($this->entries[$i] instanceof LongInfo || + $this->entries[$i] instanceof DoubleInfo) { + $i++; + } + } + } + + /** + * @return StructureInterface[] + */ + public function getEntries(): array + { + return $this->entries; + } + + /** + * @throws ReadEntryException + */ + private function read(int $entryTag): ?StructureInterface + { + switch ($entryTag) { + case ConstantPoolTag::CONSTANT_Class: + return new ClassInfo($this->reader); + case ConstantPoolTag::CONSTANT_Fieldref: + return new FieldrefInfo($this->reader); + case ConstantPoolTag::CONSTANT_Methodref: + return new MethodrefInfo($this->reader); + case ConstantPoolTag::CONSTANT_String: + return new StringInfo($this->reader); + case ConstantPoolTag::CONSTANT_Integer: + return new IntegerInfo($this->reader); + case ConstantPoolTag::CONSTANT_Float: + return new FloatInfo($this->reader); + case ConstantPoolTag::CONSTANT_Long: + return new LongInfo($this->reader); + case ConstantPoolTag::CONSTANT_Double: + return new DoubleInfo($this->reader); + case ConstantPoolTag::CONSTANT_NameAndType: + return new NameAndTypeInfo($this->reader); + case ConstantPoolTag::CONSTANT_Utf8: + return new Utf8Info($this->reader); + case ConstantPoolTag::CONSTANT_InterfaceMethodref: + return new InterfaceMethodrefInfo($this->reader); + case ConstantPoolTag::CONSTANT_InvokeDynamic: + return new InvokeDynamicInfo($this->reader); + case ConstantPoolTag::CONSTANT_MethodHandle: + return new MethodHandleInfo($this->reader); + case ConstantPoolTag::CONSTANT_MethodType: + return new MethodTypeInfo($this->reader); + case ConstantPoolTag::CONSTANT_Module: + case ConstantPoolTag::CONSTANT_Package: + throw new ReadEntryException('Entry tag ' . sprintf('0x%04X', $entryTag) . ' is not implemented.'); + } + throw new ReadEntryException('Entry tag ' . sprintf('0x%04X', $entryTag) . ' is not defined.'); + } + + /** + * @return bool + */ + public function offsetExists($offset) + { + return isset($this->entries[$offset]); + } + + /** + * @throws RuntimeException + * @return StructureInterface + */ + public function offsetGet($offset) + { + if ($this->entries[$offset] instanceof FreezableInterface) { + $this->entries[$offset]->freeze(); + } + if (!array_key_exists($offset, $this->entries)) { + throw new RuntimeException( + 'Cannot refer to an entry on the Constant Pool (index: ' . $offset . ')' + ); + } + return $this->entries[$offset]; + } + + /** + * @return int + */ + public function count() + { + return count($this->entries); + } + + /** + * @throws ReadOnlyException + */ + public function offsetSet($offset, $value) + { + throw new ReadOnlyException('You cannot rewrite datum. The Constant Pool is read-only.'); + } + + /** + * @throws ReadOnlyException + */ + public function offsetUnset($offset) + { + throw new ReadOnlyException('You cannot rewrite datum. The Constant Pool is read-only.'); + } + + /** + * @return \ArrayIterator + */ + public function getIterator() + { + return new \ArrayIterator($this->entries); + } +} diff --git a/src/Core/JVM/Extended/DynamicAccessorProvidable.php b/src/Core/JVM/Extended/DynamicAccessorProvidable.php new file mode 100644 index 00000000..5fa9354d --- /dev/null +++ b/src/Core/JVM/Extended/DynamicAccessorProvidable.php @@ -0,0 +1,18 @@ +dynamicAccessor; + } +} diff --git a/src/Core/JVM/Extended/JavaClassProvidable.php b/src/Core/JVM/Extended/JavaClassProvidable.php new file mode 100644 index 00000000..03a9c229 --- /dev/null +++ b/src/Core/JVM/Extended/JavaClassProvidable.php @@ -0,0 +1,19 @@ +javaClass); + } +} diff --git a/src/Core/JVM/Extended/ProviderProvidable.php b/src/Core/JVM/Extended/ProviderProvidable.php new file mode 100644 index 00000000..cc9a7235 --- /dev/null +++ b/src/Core/JVM/Extended/ProviderProvidable.php @@ -0,0 +1,26 @@ +providers[$providerName])) { + throw new IllegalJavaClassException($providerName . ' not provided.'); + } + + return $this->providers[$providerName]; + } +} diff --git a/src/Core/JVM/Extended/StaticAccessorProvidable.php b/src/Core/JVM/Extended/StaticAccessorProvidable.php new file mode 100644 index 00000000..fd8a45ed --- /dev/null +++ b/src/Core/JVM/Extended/StaticAccessorProvidable.php @@ -0,0 +1,22 @@ +staticAccessor + ->getMethods() + ->callStaticInitializerIfNotInstantiated(); + + return $this->staticAccessor; + } +} diff --git a/src/Core/JVM/Field/FieldGettable.php b/src/Core/JVM/Field/FieldGettable.php new file mode 100644 index 00000000..ee1a34f1 --- /dev/null +++ b/src/Core/JVM/Field/FieldGettable.php @@ -0,0 +1,25 @@ +javaClassInvoker + ->getStatic() + ->getMethods() + ->callStaticInitializerIfNotInstantiated(); + + if (!array_key_exists($name, $this->fields)) { + throw new NoSuchFieldException('Tried to get undefined field ' . $name); + } + + return $this->fields[$name]; + } +} diff --git a/src/Core/JVM/Field/FieldInterface.php b/src/Core/JVM/Field/FieldInterface.php new file mode 100644 index 00000000..1ded368d --- /dev/null +++ b/src/Core/JVM/Field/FieldInterface.php @@ -0,0 +1,16 @@ +fields; + } +} diff --git a/src/Core/JVM/Field/FieldSettable.php b/src/Core/JVM/Field/FieldSettable.php new file mode 100644 index 00000000..a0cee5a7 --- /dev/null +++ b/src/Core/JVM/Field/FieldSettable.php @@ -0,0 +1,12 @@ +fields[$name] = $value; + return $this; + } +} diff --git a/src/Core/JVM/Field/JavaDynamicField.php b/src/Core/JVM/Field/JavaDynamicField.php new file mode 100644 index 00000000..c4726339 --- /dev/null +++ b/src/Core/JVM/Field/JavaDynamicField.php @@ -0,0 +1,28 @@ +javaClassInvoker = $javaClassInvoker; + $this->fields = $fields; + } +} diff --git a/src/Core/JVM/Field/JavaStaticField.php b/src/Core/JVM/Field/JavaStaticField.php new file mode 100644 index 00000000..2c006b01 --- /dev/null +++ b/src/Core/JVM/Field/JavaStaticField.php @@ -0,0 +1,28 @@ +javaClassInvoker = $javaClassInvoker; + $this->fields = $fields; + } +} diff --git a/src/Core/JVM/Field/PHPDynamicField.php b/src/Core/JVM/Field/PHPDynamicField.php new file mode 100644 index 00000000..05c2aec4 --- /dev/null +++ b/src/Core/JVM/Field/PHPDynamicField.php @@ -0,0 +1,58 @@ +javaClassInvoker = $javaClassInvoker; + $this->fields = $fields; + } + + /** + * @throws NoSuchFieldException + */ + public function get(string $name) + { + $this->javaClassInvoker + ->getStatic() + ->getMethods() + ->callStaticInitializerIfNotInstantiated(); + + if (!array_key_exists($name, $this->fields)) { + throw new NoSuchFieldException('Tried to get undefined field ' . $name); + } + return $this->fields[$name]->getValue( + $this->javaClassInvoker->getClassObject() + ); + } + + public function set(string $name, $value) + { + $this->fields[$name]->setValue( + $this->javaClassInvoker->getClassObject(), + $value + ); + return $this; + } +} diff --git a/src/Core/JVM/Field/PHPStaticField.php b/src/Core/JVM/Field/PHPStaticField.php new file mode 100644 index 00000000..6e120293 --- /dev/null +++ b/src/Core/JVM/Field/PHPStaticField.php @@ -0,0 +1,52 @@ +javaClassInvoker = $javaClassInvoker; + $this->fields = $fields; + } + + /** + * @throws NoSuchFieldException + */ + public function get(string $name) + { + $this->javaClassInvoker + ->getStatic() + ->getMethods() + ->callStaticInitializerIfNotInstantiated(); + + if (!array_key_exists($name, $this->fields)) { + throw new NoSuchFieldException('Tried to get undefined field ' . $name); + } + return $this->fields[$name]->getValue(); + } + + public function set(string $name, $value) + { + $this->fields[$name]->setValue( + null, + $value + ); + return $this; + } +} diff --git a/src/Core/JVM/FieldPool.php b/src/Core/JVM/FieldPool.php new file mode 100644 index 00000000..93c868e9 --- /dev/null +++ b/src/Core/JVM/FieldPool.php @@ -0,0 +1,94 @@ +reader = $reader; + for ($i = 0; $i < $entries; $i++) { + $this->entries[$i] = new FieldInfo($reader); + $this->entries[$i]->setConstantPool($constantPool); + $this->entries[$i]->setDebugTool($debugTool); + $this->entries[$i]->execute(); + } + } + + /** + * @return FieldInfo[] + */ + public function getEntries() + { + return $this->entries; + } + + /** + * @param int $offset + * @return bool + */ + public function offsetExists($offset) + { + return isset($this->entries[$offset]); + } + + /** + * @param int $offset + * @return FieldInfo + */ + public function offsetGet($offset) + { + return $this->entries[$offset]; + } + + /** + * @return int + */ + public function count() + { + return count($this->entries); + } + + /** + * @throws ReadOnlyException + */ + public function offsetSet($offset, $value) + { + throw new ReadOnlyException('You cannot rewrite datum. The Field Pool is read-only.'); + } + + /** + * @throws ReadOnlyException + */ + public function offsetUnset($offset) + { + throw new ReadOnlyException('You cannot rewrite datum. The Field Pool is read-only.'); + } + + /** + * @return \ArrayIterator + */ + public function getIterator() + { + return new \ArrayIterator($this->entries); + } +} diff --git a/src/Core/JVM/InterfacePool.php b/src/Core/JVM/InterfacePool.php new file mode 100644 index 00000000..322d0c1b --- /dev/null +++ b/src/Core/JVM/InterfacePool.php @@ -0,0 +1,90 @@ +reader = $reader; + for ($i = 0; $i < $entries; $i++) { + $this->entries[$i] = $reader->getReader()->readUnsignedShort(); + } + } + + /** + * @return int[] + */ + public function getEntries() + { + return $this->entries; + } + + /** + * @param int $offset + * @return bool + */ + public function offsetExists($offset) + { + return isset($this->entries[$offset]); + } + + /** + * @param int $offset + * @return int + */ + public function offsetGet($offset) + { + return $this->entries[$offset]; + } + + /** + * @return int + */ + public function count() + { + return count($this->entries); + } + + /** + * @throws ReadOnlyException + */ + public function offsetSet($offset, $value) + { + throw new ReadOnlyException('You cannot rewrite datum. The Interface Pool is read-only.'); + } + + /** + * @throws ReadOnlyException + */ + public function offsetUnset($offset) + { + throw new ReadOnlyException('You cannot rewrite datum. The Interface Pool is read-only.'); + } + + /** + * @return \ArrayIterator + */ + public function getIterator() + { + return new \ArrayIterator($this->entries); + } +} diff --git a/src/Core/JVM/Invoker/Extended/ArgumentsStringifyable.php b/src/Core/JVM/Invoker/Extended/ArgumentsStringifyable.php new file mode 100644 index 00000000..2d1eb1a7 --- /dev/null +++ b/src/Core/JVM/Invoker/Extended/ArgumentsStringifyable.php @@ -0,0 +1,21 @@ +javaClassInvoker + ->getStatic() + ->getMethods() + ->callStaticInitializerIfNotInstantiated(); + + $operationCache = new OperationCache(); + $this->debugTool->getLogger()->debug('Call method: ' . $name); + + $currentConstantPool = $this->javaClassInvoker + ->getJavaClass() + ->getConstantPool(); + + // Wrap _Char + foreach ($arguments as &$argument) { + if (is_string($argument) && strlen($argument) === 1) { + $argument = new Char_($argument); + } + } + + $constantPool = $currentConstantPool->getEntries(); + $convertedPassedArguments = $this->stringifyArguments(...$arguments); + + /** + * @var MethodInfo $method + */ + $method = $operationCache->fetchOrPush( + "{$name}.{$convertedPassedArguments}", + function () use ($name, $arguments) { + return $this->findMethod( + $name, + ...$arguments + ); + } + ); + + $currentConstantPool = $method->getConstantPool() ?? $currentConstantPool; + + $methodBeautified = Formatter::beatifyMethodFromConstantPool( + $method, + $currentConstantPool + ); + + try { + /** + * @var CodeAttribute $codeAttribute + */ + $codeAttribute = AttributionResolver::resolve( + $method->getAttributes(), + CodeAttribute::class + ); + } catch (UnableToFindAttributionException $e) { + $this->debugTool->getLogger()->info('No code attribution: ' . $methodBeautified); + throw new NoSuchCodeAttributeException($methodBeautified); + } + + $handle = fopen( + $this->options['operations']['temporary_code_stream'] ?? + GlobalOptions::get('operations.temporary_code_stream') ?? + Runtime::OPERATIONS_TEMPORARY_CODE_STREAM, + 'r+' + ); + + fwrite($handle, $codeAttribute->getCode()); + rewind($handle); + + // Debug code attribution with HEX + $isEnabledTrace = $this->options['operations']['enable_trace'] ?? GlobalOptions::get('operations.enable_trace') ?? Runtime::OPERATIONS_ENABLE_TRACE; + $debugTraces = []; + if ($isEnabledTrace) { + $debugTraces['raw_code'] = $codeAttribute->getCode(); + $debugTraces['method'] = $method; + $debugTraces['mnemonic_indexes'] = []; + $debugTraces['executed'] = []; + } + + $reader = new BinaryReader($handle); + + if ($this->isDynamic()) { + array_unshift( + $arguments, + $this->javaClassInvoker->getJavaClass() + ); + } + + $stacks = []; + $mnemonicMap = new OpCode(); + $executedCounter = 0; + $executionTime = microtime(true); + $maxExecutionTime = ($this->options['max_execution_time'] ?? GlobalOptions::get('max_execution_time') ?? Runtime::MAX_EXECUTION_TIME); + + $this->debugTool->getLogger()->info('Start operations: ' . $methodBeautified); + + $arguments = array_map( + function ($argument) { + return TypeResolver::convertPHPTypeToJavaType($argument); + }, + $arguments + ); + + $localStorage = []; + foreach ($arguments as $argument) { + $localStorage[] = $argument; + if ($argument instanceof Double_ || $argument instanceof Long_) { + // Double and Long have a problem of skipping the next storage. + $localStorage[] = null; + } + } + + $dependencyInjectionProvider = (new DependencyInjectionProvider()) + ->add( + 'ConstantPool', + $currentConstantPool + ) + ->add( + 'JavaClass', + $this->javaClassInvoker->getJavaClass() + ) + ->add( + 'JavaClassInvoker', + $this->javaClassInvoker + ) + ->add( + 'Attributes', + $method->getAttributes() + ) + ->add( + 'Options', + $this->options + ); + + while ($reader->getOffset() < $codeAttribute->getOpCodeLength()) { + $dependencyInjectionProvider + ->add('OperandStacks', $stacks) + ->add('LocalStorages', $localStorage); + + if (++$executedCounter > ($this->options['max_stack_exceeded'] ?? GlobalOptions::get('max_stack_exceeded') ?? Runtime::MAX_STACK_EXCEEDED)) { + throw new RuntimeException( + 'Max stack exceeded. PHPJava has been stopped by safety guard.' . + ' Maybe Java class has illegal program counter, stacks, or OpCode.' + ); + } + $exceededTime = microtime(true) - $executionTime; + if ($exceededTime > $maxExecutionTime) { + throw new RuntimeException( + 'Maximum execution time of ' . $maxExecutionTime . ' seconds exceeded. PHPJava has been stopped by safety guard.' . + ' Maybe Java class has illegal program counter, stacks, or OpCode.' + ); + } + $opcode = $reader->readUnsignedByte(); + $mnemonic = $mnemonicMap->getName($opcode); + + if ($mnemonic === null) { + throw new UndefinedOpCodeException( + 'Call to undefined OpCode ' . sprintf('0x%X', $opcode) . '.' + ); + } + $pointer = $reader->getOffset() - 1; + + $fullName = Runtime::MNEMONIC_NAMESPACE . '\\' . $mnemonic; + + if (!class_exists($fullName)) { + throw new UnsupportedOperationException( + sprintf( + '%s(0x%02X) operation does not supported.', + ltrim($mnemonic, '_'), + $opcode + ) + ); + } + + if ($isEnabledTrace) { + $debugTraces['executed'][] = [$opcode, $mnemonic, $localStorage, $stacks, $pointer]; + $debugTraces['mnemonic_indexes'][] = $pointer; + } + + /** + * @var Accumulator|ConstantPool|OperationCodeInterface $executor + */ + $executor = new $fullName(); + + $executor + ->setConstantPool($currentConstantPool) + ->setParameters( + $method, + $this->javaClassInvoker, + $reader, + $localStorage, + $stacks, + $pointer, + $dependencyInjectionProvider + ) + ->setDebugTool( + $this->debugTool + ); + + $executor->beforeExecute(); + + $beforeTrigger = $this->options['operations']['injections']['before'] ?? GlobalOptions::get('operations.injections.before'); + if (is_callable($beforeTrigger)) { + // Bind class + \Closure::bind($beforeTrigger, $this); + + $beforeTrigger($executor); + } + + // Run executor + $executor->execute(); + + $afterTrigger = $this->options['operations']['injections']['after'] ?? GlobalOptions::get('operations.injections.after'); + if (is_callable($afterTrigger)) { + // Bind class + \Closure::bind($beforeTrigger, $this); + + $afterTrigger($executor); + } + + if ($this->debugTool->getLogLevel() <= Logger::DEBUG) { + $this->debugTool->getLogger()->debug( + vsprintf( + 'OpCode: 0x%02X %-15.15s Stacks: %-4.4s PC: %-8.8s Used Memory: %-8.8s Used Memory Peak: %-8.8s', + [ + $opcode, + ltrim($mnemonic, '_'), + count($stacks), + $pointer, + Metric::bytes(memory_get_usage())->format(), + Metric::bytes(memory_get_peak_usage())->format(), + ] + ), + [$name] + ); + + $this->debugTool->getLogger()->debug( + vsprintf( + 'Consumed Operand Stacks: %s', + [ + Formatter::beatifyOperandStackItems( + $executor->getPoppedOperandStacks() + ), + ] + ), + [$name] + ); + + $this->debugTool->getLogger()->debug( + vsprintf( + 'Pushed Operand Stacks: %s', + [ + Formatter::beatifyOperandStackItems( + $executor->getPushedOperandStacks() + ), + ] + ), + [$name] + ); + } + + $executor->afterExecute(); + + /** + * Return processing as following: + * - areturn (Return an object) + * - ireturn (Return integer object) + * - dreturn (Return double obejct) + * - freturn (Return float object) + * - lreturn (Return long object) + * - return (Return void obejct). + */ + if ($executor->returnValue() !== null) { + if ($isEnabledTrace) { + $this->javaClassInvoker->getJavaClass()->appendDebug($debugTraces); + } + $this->debugTool->getLogger()->info('Finish operations: ' . $methodBeautified); + + // return values + return $executor + ->returnValue(); + } + } + + if ($isEnabledTrace) { + $this->javaClassInvoker->getJavaClass()->appendDebug($debugTraces); + } + $this->debugTool->getLogger()->info('Finish operations: ' . $methodBeautified); + return null; + } +} diff --git a/src/Core/JVM/Invoker/Extended/JavaMethodFindable.php b/src/Core/JVM/Invoker/Extended/JavaMethodFindable.php new file mode 100644 index 00000000..2c955e9c --- /dev/null +++ b/src/Core/JVM/Invoker/Extended/JavaMethodFindable.php @@ -0,0 +1,80 @@ +isDynamic() + ? SuperClassResolver::resolveDynamicMethods($name, $this->javaClassInvoker->getJavaClass()) + : SuperClassResolver::resolveStaticMethods($name, $this->javaClassInvoker->getJavaClass()); + + $methodReferences = array_merge( + $this->methods[$name] ?? [], + $superClassMethods[$name] ?? [] + ); + + if (empty($methodReferences)) { + throw new NoSuchMethodException( + 'Call to undefined method ' . $name . '.' + ); + } + + $convertedPassedArguments = $this->stringifyArguments(...$arguments); + + $this->debugTool->getLogger()->debug('Passed descriptor is ' . ($convertedPassedArguments ?: '(none)')); + + $method = null; + + foreach ($methodReferences as $methodReference) { + $constantPool = $currentConstantPool = $methodReference->getConstantPool(); + $formattedArguments = Formatter::parseSignature( + $constantPool[$methodReference->getDescriptorIndex()]->getString() + )['arguments']; + + // does not strict mode can be PHP types + if (!($this->options['strict'] ?? GlobalOptions::get('strict') ?? Runtime::STRICT)) { + $formattedArguments = Formatter::signatureConvertToAmbiguousForPHP($formattedArguments); + } + + /** + * @var MethodInfo $methodReference + */ + $methodSignature = Formatter::buildArgumentsSignature($formattedArguments); + + $this->debugTool->getLogger()->debug('Find descriptor for ' . ($methodSignature ?: '(none)')); + + if (!($this->options['validation']['method']['arguments_count_only'] ?? GlobalOptions::get('validation.method.arguments_count_only') ?? Runtime::VALIDATION_METHOD_ARGUMENTS_COUNT_ONLY)) { + if (TypeResolver::compare($this->javaClassInvoker->getJavaClass(), $methodSignature, $convertedPassedArguments)) { + return $methodReference; + } + } + if (($this->options['validation']['method']['arguments_count_only'] ?? GlobalOptions::get('validation.method.arguments_count_only') ?? Runtime::VALIDATION_METHOD_ARGUMENTS_COUNT_ONLY) === true) { + $size = count($formattedArguments); + $passedArgumentsSize = count($arguments); + + if ($size === $passedArgumentsSize) { + return $methodReference; + } + } + } + + throw new NoSuchMethodException('Call to undefined method ' . $name . '.'); + } +} diff --git a/src/Core/JVM/Invoker/Extended/JavaMethodSpecifiable.php b/src/Core/JVM/Invoker/Extended/JavaMethodSpecifiable.php new file mode 100644 index 00000000..4f069778 --- /dev/null +++ b/src/Core/JVM/Invoker/Extended/JavaMethodSpecifiable.php @@ -0,0 +1,21 @@ +findMethod( + $methodName, + ...$arguments + ); + + return new JavaMethodSpecifics( + $methodInfo + ); + } +} diff --git a/src/Core/JVM/Invoker/Extended/MethodListable.php b/src/Core/JVM/Invoker/Extended/MethodListable.php new file mode 100644 index 00000000..8cf4c845 --- /dev/null +++ b/src/Core/JVM/Invoker/Extended/MethodListable.php @@ -0,0 +1,16 @@ +methods; + } +} diff --git a/src/Core/JVM/Invoker/Extended/PHPMethodAnnotationAffectable.php b/src/Core/JVM/Invoker/Extended/PHPMethodAnnotationAffectable.php new file mode 100644 index 00000000..14c0925b --- /dev/null +++ b/src/Core/JVM/Invoker/Extended/PHPMethodAnnotationAffectable.php @@ -0,0 +1,31 @@ +findMethod($name); + $annotations = []; + if ($phpDocument = $method->getDocComment()) { + $documentBlock = \phpDocumentor\Reflection\DocBlockFactory::createInstance() + ->create($phpDocument); + + foreach ($documentBlock->getTags() as $tag) { + /** + * @var \phpDocumentor\Reflection\DocBlock\Tag $tag + */ + $name = $tag->getName(); + if (!isset($annotations[$name])) { + $annotations[$name] = []; + } + $annotations[$name][] = $tag; + } + } + return $annotations; + } +} diff --git a/src/Core/JVM/Invoker/Extended/PHPMethodCallable.php b/src/Core/JVM/Invoker/Extended/PHPMethodCallable.php new file mode 100644 index 00000000..807789ed --- /dev/null +++ b/src/Core/JVM/Invoker/Extended/PHPMethodCallable.php @@ -0,0 +1,67 @@ +javaClassInvoker + ->getStatic() + ->getMethods() + ->callStaticInitializerIfNotInstantiated(); + + $realPHPMethodName = MethodNameResolver::resolve($name); + + /** + * @var \ReflectionMethod $method + */ + $method = $this->findMethod($name); + + $suffix = ($realPHPMethodName !== $name ? ' (Actual calling is the ' . $realPHPMethodName . ' method)' : ''); + $this->debugTool->getLogger()->debug( + 'Call method: ' . $name . $suffix + ); + + if ($this->isDynamic() && MethodNameResolver::isConstructorMethod($name)) { + $this->javaClassInvoker->construct(...$arguments); + return $this->javaClassInvoker->getJavaClass(); + } + + $classObject = $this->javaClassInvoker->getClassObject(); + if ($this->isDynamic() && $classObject === null) { + throw new RuntimeException( + 'Failed to call the method because the given JavaClass does not have ClassObject.' + ); + } + + $executed = $method + ->invokeArgs( + $this->isDynamic() + ? $classObject + : null, + $arguments + ); + + $this->debugTool->getLogger()->debug( + 'Finish operation: ' . $name . $suffix + ); + + return $executed; + } +} diff --git a/src/Core/JVM/Invoker/Extended/PHPMethodFindable.php b/src/Core/JVM/Invoker/Extended/PHPMethodFindable.php new file mode 100644 index 00000000..87123a13 --- /dev/null +++ b/src/Core/JVM/Invoker/Extended/PHPMethodFindable.php @@ -0,0 +1,19 @@ +methods[$name])) { + throw new NoSuchMethodException( + 'Call to undefined method ' . $name . ' on ' . $this->javaClassInvoker->getJavaClass()->getClassName() . '.' + ); + } + + return $this->methods[$name][0]; + } +} diff --git a/src/Core/JVM/Invoker/Extended/PHPMethodSpecifiable.php b/src/Core/JVM/Invoker/Extended/PHPMethodSpecifiable.php new file mode 100644 index 00000000..519f6581 --- /dev/null +++ b/src/Core/JVM/Invoker/Extended/PHPMethodSpecifiable.php @@ -0,0 +1,18 @@ +findMethod( + $methodName + ) + ); + } +} diff --git a/src/Core/JVM/Invoker/Extended/SpecialMethodCallable.php b/src/Core/JVM/Invoker/Extended/SpecialMethodCallable.php new file mode 100644 index 00000000..8699d9dc --- /dev/null +++ b/src/Core/JVM/Invoker/Extended/SpecialMethodCallable.php @@ -0,0 +1,15 @@ +call($name, ...$arguments); + } +} diff --git a/src/Core/JVM/Invoker/Extended/Specifics/JavaMethodSpecifics.php b/src/Core/JVM/Invoker/Extended/Specifics/JavaMethodSpecifics.php new file mode 100644 index 00000000..c0dab5b9 --- /dev/null +++ b/src/Core/JVM/Invoker/Extended/Specifics/JavaMethodSpecifics.php @@ -0,0 +1,79 @@ +methodInfo = $methodInfo; + } + + public function isAbstract(): bool + { + return ($this->methodInfo->getAccessFlag() & MethodAccessFlag::ACC_ABSTRACT) !== 0; + } + + public function isStatic(): bool + { + return ($this->methodInfo->getAccessFlag() & MethodAccessFlag::ACC_STATIC) !== 0; + } + + public function isFinal(): bool + { + return ($this->methodInfo->getAccessFlag() & MethodAccessFlag::ACC_FINAL) !== 0; + } + + public function isPublic(): bool + { + return ($this->methodInfo->getAccessFlag() & MethodAccessFlag::ACC_PUBLIC) !== 0; + } + + public function isPrivate(): bool + { + return ($this->methodInfo->getAccessFlag() & MethodAccessFlag::ACC_PRIVATE) !== 0; + } + + public function isProtected(): bool + { + return ($this->methodInfo->getAccessFlag() & MethodAccessFlag::ACC_PROTECTED) !== 0; + } + + public function isBridge(): bool + { + return ($this->methodInfo->getAccessFlag() & MethodAccessFlag::ACC_BRIDGE) !== 0; + } + + public function isNative(): bool + { + return ($this->methodInfo->getAccessFlag() & MethodAccessFlag::ACC_NATIVE) !== 0; + } + + public function isStrict(): bool + { + return ($this->methodInfo->getAccessFlag() & MethodAccessFlag::ACC_STRICT) !== 0; + } + + public function isSynchronized(): bool + { + return ($this->methodInfo->getAccessFlag() & MethodAccessFlag::ACC_SYNCHRONIZED) !== 0; + } + + public function isSynthetic(): bool + { + return ($this->methodInfo->getAccessFlag() & MethodAccessFlag::ACC_SYNTHETIC) !== 0; + } + + public function isVarargs(): bool + { + return ($this->methodInfo->getAccessFlag() & MethodAccessFlag::ACC_VARARGS) !== 0; + } +} diff --git a/src/Core/JVM/Invoker/Extended/Specifics/MethodSpecificsInterface.php b/src/Core/JVM/Invoker/Extended/Specifics/MethodSpecificsInterface.php new file mode 100644 index 00000000..adcee773 --- /dev/null +++ b/src/Core/JVM/Invoker/Extended/Specifics/MethodSpecificsInterface.php @@ -0,0 +1,30 @@ +methodInfo = $methodInfo; + } + + public function isAbstract(): bool + { + return $this->methodInfo->isAbstract(); + } + + public function isStatic(): bool + { + return $this->methodInfo->isStatic(); + } + + public function isFinal(): bool + { + return $this->methodInfo->isFinal(); + } + + public function isPublic(): bool + { + return $this->methodInfo->isPublic(); + } + + public function isPrivate(): bool + { + return $this->methodInfo->isPrivate(); + } + + public function isProtected(): bool + { + return $this->methodInfo->isProtected(); + } + + public function isBridge(): bool + { + return false; + } + + public function isNative(): bool + { + return false; + } + + public function isStrict(): bool + { + return false; + } + + public function isSynchronized(): bool + { + return false; + } + + public function isSynthetic(): bool + { + return false; + } + + public function isVarargs(): bool + { + return false; + } +} diff --git a/src/Core/JVM/Invoker/Extended/StaticConstructorInitializable.php b/src/Core/JVM/Invoker/Extended/StaticConstructorInitializable.php new file mode 100644 index 00000000..e803ef36 --- /dev/null +++ b/src/Core/JVM/Invoker/Extended/StaticConstructorInitializable.php @@ -0,0 +1,30 @@ +isInstantiatedStaticInitializer) { + return $this; + } + $this->isInstantiatedStaticInitializer = true; + if ($this->javaClassInvoker->getStatic()->getMethods()->has('')) { + $this->javaClassInvoker + ->getStatic() + ->getMethods() + ->call( + '' + ); + } + return $this; + } +} diff --git a/src/Core/JVM/Invoker/InvokerInterface.php b/src/Core/JVM/Invoker/InvokerInterface.php new file mode 100644 index 00000000..9cd204e7 --- /dev/null +++ b/src/Core/JVM/Invoker/InvokerInterface.php @@ -0,0 +1,31 @@ +javaClassInvoker = $javaClassInvoker; + $this->methods = $methods; + $this->options = $options; + $this->debugTool = new DebugTool( + str_replace('/', '.', $javaClassInvoker->getJavaClass()->getClassName()), + $this->options + ); + } + + public function isDynamic(): bool + { + return false; + } + + public function has(string $name): bool + { + return count($this->methods[$name] ?? []) > 0; + } +} diff --git a/src/Core/JVM/Invoker/JavaClassStaticMethodInvoker.php b/src/Core/JVM/Invoker/JavaClassStaticMethodInvoker.php new file mode 100644 index 00000000..1e06c63d --- /dev/null +++ b/src/Core/JVM/Invoker/JavaClassStaticMethodInvoker.php @@ -0,0 +1,7 @@ +javaClassInvoker = $javaClassInvoker; + $this->methods = $methods; + $this->options = $options; + $this->debugTool = new DebugTool( + ltrim( + str_replace( + [Runtime::PHP_PACKAGES_NAMESPACE, '\\'], + ['', '.'], + $javaClassInvoker->getJavaClass()->getClassName() + ), + '.' + ), + $this->options + ); + } + + public function isDynamic(): bool + { + return false; + } + + public function has(string $name): bool + { + return count($this->methods[$name] ?? []) > 0; + } +} diff --git a/src/Core/JVM/Invoker/PHPClassStaticMethodInvoker.php b/src/Core/JVM/Invoker/PHPClassStaticMethodInvoker.php new file mode 100644 index 00000000..07e6f6c3 --- /dev/null +++ b/src/Core/JVM/Invoker/PHPClassStaticMethodInvoker.php @@ -0,0 +1,7 @@ +javaClass = $javaClass; + $this->options = $options; + $cpInfo = $this->javaClass->getConstantPool(); + + foreach ($this->javaClass->getDefinedMethods() as $methodInfo) { + /** + * @var MethodInfo $methodInfo + */ + $methodName = $cpInfo[$methodInfo->getNameIndex()]->getString(); + + if (($methodInfo->getAccessFlag() & MethodAccessFlag::ACC_STATIC) !== 0) { + $this->staticMethods[$methodName][] = $methodInfo; + } else { + $this->dynamicMethods[$methodName][] = $methodInfo; + } + } + + foreach ($this->javaClass->getDefinedFields() as $fieldInfo) { + /** + * @var FieldInfo $fieldInfo + */ + $fieldName = $cpInfo[$fieldInfo->getNameIndex()]->getString(); + + if (($fieldInfo->getAccessFlag() & FieldAccessFlag::ACC_STATIC) !== 0) { + $this->staticFields[$fieldName] = $fieldInfo; + } else { + $this->dynamicFields[$fieldName] = $fieldInfo; + } + } + + $this->dynamicAccessor = new Accessor( + $this, + JavaClassDynamicMethodInvoker::class, + JavaDynamicField::class, + $this->dynamicMethods, + Normalizer::normalizeFields( + $this->dynamicFields, + $this->javaClass + ), + $this->options + ); + + $this->staticAccessor = new Accessor( + $this, + JavaClassStaticMethodInvoker::class, + JavaStaticField::class, + $this->staticMethods, + Normalizer::normalizeFields( + $this->staticFields, + $this->javaClass + ), + $this->options + ); + } + + public function construct(...$arguments): ClassInvokerInterface + { + $this->dynamicAccessor = new Accessor( + $this, + JavaClassDynamicMethodInvoker::class, + JavaDynamicField::class, + $this->dynamicMethods, + Normalizer::normalizeFields( + $this->dynamicFields, + $this->javaClass + ), + $this->options + ); + + $this->getDynamic()->getMethods()->call( + '', + ...$arguments + ); + + return $this; + } + + /** + * @return JavaClassInterface + */ + public function getClassObject() + { + return $this->javaClass; + } +} diff --git a/src/Core/JVM/MethodPool.php b/src/Core/JVM/MethodPool.php new file mode 100644 index 00000000..3295ad15 --- /dev/null +++ b/src/Core/JVM/MethodPool.php @@ -0,0 +1,94 @@ +reader = $reader; + for ($i = 0; $i < $entries; $i++) { + $this->entries[$i] = new MethodInfo($reader); + $this->entries[$i]->setConstantPool($constantPool); + $this->entries[$i]->setDebugTool($debugTool); + $this->entries[$i]->execute(); + } + } + + /** + * @return MethodInfo[] + */ + public function getEntries() + { + return $this->entries; + } + + /** + * @param int $offset + * @return bool + */ + public function offsetExists($offset) + { + return isset($this->entries[$offset]); + } + + /** + * @param int $offset + * @return MethodInfo + */ + public function offsetGet($offset) + { + return $this->entries[$offset]; + } + + /** + * @return int + */ + public function count() + { + return count($this->entries); + } + + /** + * @throws ReadOnlyException + */ + public function offsetSet($offset, $value) + { + throw new ReadOnlyException('You cannot rewrite datum. The Interface Pool is read-only.'); + } + + /** + * @throws ReadOnlyException + */ + public function offsetUnset($offset) + { + throw new ReadOnlyException('You cannot rewrite datum. The Interface Pool is read-only.'); + } + + /** + * @return \ArrayIterator + */ + public function getIterator() + { + return new \ArrayIterator($this->entries); + } +} diff --git a/src/Core/JVM/PHPClassInvoker.php b/src/Core/JVM/PHPClassInvoker.php new file mode 100644 index 00000000..e036fbbc --- /dev/null +++ b/src/Core/JVM/PHPClassInvoker.php @@ -0,0 +1,144 @@ +javaClass = $javaClass; + $this->options = $options; + + foreach ($this->javaClass->getDefinedMethods() as $methodName => $methodList) { + foreach ($methodList as $methodInfo) { + /** + * @var \ReflectionMethod $methodInfo + */ + + // Private methods become accessible + $methodInfo->setAccessible(true); + + // Categorise by method accessibility. + if ($methodInfo->isStatic()) { + $this->staticMethods[$methodName][] = $methodInfo; + } else { + $this->dynamicMethods[$methodName][] = $methodInfo; + } + } + } + + foreach ($this->javaClass->getDefinedFields() as $fieldName => $fieldInfo) { + /** + * @var \ReflectionProperty $fieldInfo + */ + + // Private fields become accessible + $fieldInfo->setAccessible(true); + + // Categorise by field accessibility. + if ($fieldInfo->isStatic()) { + $this->staticFields[$fieldName] = $fieldInfo; + } else { + $this->dynamicFields[$fieldName] = $fieldInfo; + } + } + + $this->dynamicAccessor = new Accessor( + $this, + PHPClassDynamicMethodInvoker::class, + PHPDynamicField::class, + $this->dynamicMethods, + $this->dynamicFields, + $this->options + ); + + $this->staticAccessor = new Accessor( + $this, + PHPClassStaticMethodInvoker::class, + PHPStaticField::class, + $this->staticMethods, + $this->staticFields, + $this->options + ); + } + + public function construct(...$arguments): ClassInvokerInterface + { + $this->dynamicAccessor = new Accessor( + $this, + PHPClassDynamicMethodInvoker::class, + PHPDynamicField::class, + $this->dynamicMethods, + $this->dynamicFields, + $this->options + ); + + $className = $this->javaClass->getClassName(); + $this->classObject = new $className( + ...$arguments + ); + + if (method_exists($this->classObject, 'setJavaClass')) { + $this->classObject->setJavaClass( + $this->getJavaClass() + ); + } + + return $this; + } + + public function getClassObject() + { + // TODO: add dynamically validation + return $this->classObject; + } +} diff --git a/src/Core/JVM/Parameters/GlobalOptions.php b/src/Core/JVM/Parameters/GlobalOptions.php new file mode 100644 index 00000000..0f7ef567 --- /dev/null +++ b/src/Core/JVM/Parameters/GlobalOptions.php @@ -0,0 +1,55 @@ + &$value) { + if (isset($merged[$key]) && + is_array($value) && + is_array($merged[$key]) + ) { + $merged[$key] = static::merge( + $merged[$key], + $value + ); + continue; + } + $merged[$key] = $value; + } + return $merged; + } +} diff --git a/src/Core/JVM/Parameters/Runtime.php b/src/Core/JVM/Parameters/Runtime.php new file mode 100644 index 00000000..2e7de54b --- /dev/null +++ b/src/Core/JVM/Parameters/Runtime.php @@ -0,0 +1,58 @@ + 'String_', + 'Object' => 'Object_', + 'Void' => 'Void_', + ]; + + const CLASS_INITIALIZER_CLASS_MAPS = [ + '__construct' => '', + '__staticConstruct' => '', + ]; + + const PHP_PACKAGES_NAMESPACE = 'PHPJava\\Packages'; + const MNEMONIC_NAMESPACE = 'PHPJava\\Kernel\\Mnemonics'; + + const EMULATOR_MNEMONIC_NAMESPACE = 'PHPJava\\Compiler\\Emulator\\Mnemonics'; + const BUILD_PACKAGE_NAMESPACE = 'PHPJava\\Compiler\\Lang\\Assembler\\Bundler\\Packages\\'; + const PHP_STANDARD_CLASS_NAME = 'PHPRuntime.PHPStandard'; + const PHP_STANDARD_CLASS_METHOD_PREFIX = 'PHP_STANDARD@'; + const PHP_ENTRY_POINT_CLASS_NAME = '__ENTRYPOINT__'; + const PHP_COMPILER_JDK_VERSION = '8'; + + const PREFIX_STATIC = 'static_'; + const PREFIX_DEFAULT = '__default_'; +} diff --git a/src/Core/JVM/Stream/BinaryReader.php b/src/Core/JVM/Stream/BinaryReader.php new file mode 100644 index 00000000..5dab248c --- /dev/null +++ b/src/Core/JVM/Stream/BinaryReader.php @@ -0,0 +1,105 @@ +handle = $handle; + } + + /** + * @throws BinaryReaderException + */ + final public function read(int $bytes = 1): string + { + $this->offset += $bytes; + if ($bytes === 0) { + return ''; + } + $read = fread($this->handle, $bytes); + if (strlen($read) !== $bytes) { + throw new BinaryReaderException( + 'Read binary from stream is incorrect. that expected length is ' . + $bytes . + ', but actual length is ' . strlen($read) . '.' + ); + } + return $read; + } + + public function readByte(): int + { + return current(unpack('c', $this->read(1))); + } + + public function readUnsignedByte(): int + { + return current(unpack('C', $this->read(1))); + } + + public function readUnsignedInt(): int + { + return current(unpack('N', $this->read(4))); + } + + public function readUnsignedShort(): int + { + return current(unpack('n', $this->read(2))); + } + + public function readInt(): int + { + $bytes = array_values(unpack('c4', $this->read(4))); + return ($bytes[0] << 24) | ($bytes[1] << 16) | ($bytes[2] << 8) | $bytes[3]; + } + + public function readShort(): int + { + $short = $this->readUnsignedShort(); + return (($short & 0x8000) > 0) ? ($short - 0xFFFF - 1) : $short; + } + + public function readUnsignedLong(): int + { + return current(unpack('J', $this->read(8))); + } + + public function readLong(): int + { + return hexdec(bin2hex($this->read(8))); + } + + public function seek(int $bytes): void + { + $this->offset += $bytes; + fseek($this->handle, $bytes, SEEK_CUR); + } + + public function setOffset(int $pointer): void + { + $this->offset = $pointer; + fseek($this->handle, $pointer, SEEK_SET); + } + + public function getOffset(): int + { + return $this->offset; + } +} diff --git a/src/Core/JVM/Stream/BinaryWriter.php b/src/Core/JVM/Stream/BinaryWriter.php new file mode 100644 index 00000000..c3ae353f --- /dev/null +++ b/src/Core/JVM/Stream/BinaryWriter.php @@ -0,0 +1,110 @@ +handle = $handle; + } + + public function write(string $binary) + { + fwrite($this->handle, $binary); + return $this; + } + + public function writeByte(int $value): self + { + return $this->write(pack('c', $value)); + } + + public function writeUnsignedByte(int $value): self + { + return $this->write(pack('C', $value)); + } + + public function writeInt(int $value): self + { + if ($value < 0) { + $value += 0x800000; + } + return $this->writeUnsignedInt($value); + } + + public function writeUnsignedInt(int $value): self + { + return $this->write(pack('N', $value)); + } + + public function writeShort(int $value): self + { + return $this->writeUnsignedShort($value); + } + + public function writeUnsignedShort(int $value): self + { + return $this->write(pack('n', $value)); + } + + public function writeLong(int $value): self + { + throw new CompilerException( + 'Write a long size value to a binary file is not implemented yet.' + ); + return $this; + } + + public function writeUnsignedLong(int $value): self + { + return $this->write(pack('J', $value)); + } + + public function isWritable(): bool + { + $meta = stream_get_meta_data($this->handle); + if (!isset($meta['mode'])) { + return false; + } + + foreach (['w', 'r+', 'a', 'c', 'x'] as $mode) { + if (strstr($meta['mode'], $mode) !== false) { + return true; + } + } + return false; + } + + public function enableBuffer(bool $isEnabled): self + { + $this->buffer = $isEnabled; + return $this; + } + + public function getStreamContents() + { + rewind($this->handle); + return stream_get_contents($this->handle); + } +} diff --git a/src/Core/JVM/Stream/ReflectionClassReader.php b/src/Core/JVM/Stream/ReflectionClassReader.php new file mode 100644 index 00000000..d11301ec --- /dev/null +++ b/src/Core/JVM/Stream/ReflectionClassReader.php @@ -0,0 +1,61 @@ +handle = $handle; + } + + /** + * @return \ReflectionProperty[] + */ + public function getFields() + { + $fields = []; + foreach ($this->handle->getProperties() as $field) { + $fieldName = static::removePrefixes($field->getName()); + $fields[Runtime::CLASS_INITIALIZER_CLASS_MAPS[$fieldName] ?? $fieldName] = $field; + } + + return $fields; + } + + /** + * @return \ReflectionMethod[][] + */ + public function getMethods() + { + $methods = []; + foreach ($this->handle->getMethods() as $method) { + $methodName = static::removePrefixes($method->getName()); + $methods[Runtime::CLASS_INITIALIZER_CLASS_MAPS[$methodName] ?? $methodName][] = $method; + } + + return $methods; + } + + private static function removePrefixes(string $name): string + { + return str_replace( + [ + Runtime::PREFIX_STATIC, + Runtime::PREFIX_DEFAULT, + ], + '', + $name + ); + } +} diff --git a/src/Core/JVM/Stream/StreamReaderInterface.php b/src/Core/JVM/Stream/StreamReaderInterface.php new file mode 100644 index 00000000..150fbc7f --- /dev/null +++ b/src/Core/JVM/Stream/StreamReaderInterface.php @@ -0,0 +1,7 @@ +magicByte = (int) $magicByte; + } + + public function isValid(): bool + { + return static::CAFEBABE === $this->magicByte ?? 0; + } + + public static function getMagicByte(): int + { + return static::CAFEBABE; + } +} diff --git a/src/Core/JVM/Validations/ValidatorInterface.php b/src/Core/JVM/Validations/ValidatorInterface.php new file mode 100644 index 00000000..676467fd --- /dev/null +++ b/src/Core/JVM/Validations/ValidatorInterface.php @@ -0,0 +1,8 @@ +startTime = microtime(true); + $this->jarFile = $jarFile; + $archive = new \ZipArchive(); + $archive->open($jarFile); + $this->expandedHArchive = $archive; + $this->options = $options; + $this->debugTool = new DebugTool( + basename($jarFile), + $this->options + ); + + $this->debugTool->getLogger()->info('Start jar emulation'); + + $this->manifestData['main-class'] = $options['entrypoint'] ?? Runtime::ENTRYPOINT; + + // Add resolving path + ClassResolver::add( + [ + [ClassResolver::RESOURCE_TYPE_FILE, dirname($jarFile)], + [ClassResolver::RESOURCE_TYPE_FILE, getcwd()], + [ClassResolver::RESOURCE_TYPE_JAR, $this], + ] + ); + + $this->debugTool->getLogger()->debug('Extracting jar files: ' . $this->expandedHArchive->numFiles); + for ($i = 0; $i < $this->expandedHArchive->numFiles; $i++) { + $name = $archive->getNameIndex($i); + if ($name[strlen($name) - 1] === '/') { + continue; + } + $fileName = preg_replace('/\.class$/', '', $name); + $this->files[$fileName] = $archive->getFromIndex($i); + } + + if (!isset($this->files[static::MANIFEST_FILE_NAME])) { + throw new FileNotFoundException('Failed to load Manifest.mf'); + } + + foreach (explode("\n", $this->files[static::MANIFEST_FILE_NAME]) as $attribute) { + $attribute = str_replace(["\r", "\n"], '', $attribute); + if (empty($attribute)) { + continue; + } + if (strpos($attribute, ':') === false) { + continue; + } + [$name, $value] = explode(':', $attribute); + $this->manifestData[strtolower($name)] = trim($value); + } + + $this->files = array_filter( + $this->files, + function ($fileName) { + return $fileName !== static::MANIFEST_FILE_NAME; + }, + ARRAY_FILTER_USE_KEY + ); + + foreach ($this->files as $className => $code) { + if (in_array($className, static::IGNORE_FILES)) { + continue; + } + $classPath = str_replace('/', '.', $className); + $this->classes[$classPath] = new JavaInlineClassDeferredLoader( + [$className, $code], + $this->options + ); + } + + $currentDirectory = getcwd(); + foreach ($this->getClassPaths() as $classPath) { + $resolvePath = $classPath[0] === '/' ? $classPath : ($currentDirectory . '/' . $classPath); + $realpath = realpath($resolvePath); + if ($realpath === false) { + throw new FileNotFoundException($classPath . ' does not exist.'); + } + + $value = $realpath; + + switch ($fileType = FileTypeResolver::resolve($resolvePath)) { + case ClassResolver::RESOURCE_TYPE_CLASS: + $value = new JavaCompiledClass( + new Stream\Reader\FileReader($value), + $this->options + ); + break; + case ClassResolver::RESOURCE_TYPE_JAR: + $value = new JavaArchive($value, $this->options); + break; + case ClassResolver::RESOURCE_TYPE_FILE: + break; + } + ClassResolver::add($fileType, $value); + } + + $this->debugTool->getLogger()->info('End of jar'); + } + + public function __destruct() + { + $this->debugTool->getLogger()->info( + 'Spent time: ' . (microtime(true) - $this->startTime) . ' sec.' + ); + } + + /** + * @throws ClassNotFoundException + * @throws UndefinedEntrypointException + */ + public function execute(...$arguments) + { + $this->debugTool->getLogger()->info('Call to entrypoint: ' . $this->getEntryPointName()); + if ($this->getEntryPointName() === null) { + throw new UndefinedEntrypointException('No entrypoint.'); + } + return $this + ->getClassByName($this->getEntryPointName()) + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + static::DEFAULT_ENTRYPOINT_NAME, + ...$arguments + ); + } + + /** + * @return mixed[] + */ + public function __debugInfo() + { + return [ + 'version' => $this->getVersion(), + 'createdBy' => $this->getCreatedBy(), + 'entryPointName' => $this->getEntryPointName(), + 'file' => $this->jarFile, + 'classes' => $this->getClasses(), + 'classPaths' => $this->getClassPaths(), + ]; + } + + public function getVersion(): ?string + { + return $this->manifestData['manifest-version'] ?? null; + } + + public function getCreatedBy(): ?string + { + return $this->manifestData['created-by'] ?? null; + } + + public function getEntryPointName(): ?string + { + return $this->manifestData['main-class'] ?? null; + } + + /** + * @return string[] + */ + public function getClassPaths(): array + { + $classPaths = []; + foreach (explode(' ', $this->manifestData['class-path'] ?? '') as $path) { + if (empty($path)) { + continue; + } + $classPaths[] = $path; + } + return $classPaths; + } + + /** + * @return JavaClassInterface[] + */ + public function getClasses(): array + { + return $this->classes; + } + + public function getClassByName(string $name): JavaClassInterface + { + $name = str_replace('/', '.', $name); + if (!isset($this->classes[$name])) { + throw new ClassNotFoundException($name . ' does not exist on ' . $this->jarFile . '.'); + } + return $this->classes[$name]; + } +} diff --git a/src/Core/JavaClass.php b/src/Core/JavaClass.php new file mode 100644 index 00000000..c1f459a4 --- /dev/null +++ b/src/Core/JavaClass.php @@ -0,0 +1,174 @@ +genericClass = $genericClass; + } + + /** + * @param $methodName + * @param $arguments + */ + public function __call($methodName, $arguments) + { + return $this->genericClass->{$methodName}(...$arguments); + } + + public function isCompiledClass(): bool + { + return $this->genericClass instanceof JavaCompiledClass; + } + + public function isSimpleClass(): bool + { + return $this->genericClass instanceof JavaSimpleClass; + } + + /** + * @return JavaClass + */ + public static function of(JavaClassInterface $javaClass) + { + if ($javaClass instanceof JavaClass) { + return $javaClass; + } + return new JavaClass($javaClass); + } + + public function is(string $className): bool + { + $className = Formatter::convertPHPNamespacesToJava($className); + + // get parents + $extendedClasses = $this->genericClass->getDefinedExtendedClasses(); + $interfaceClasses = $this->genericClass->getDefinedInterfaceClasses(); + + return in_array($className, $extendedClasses, true) || + in_array($className, $interfaceClasses, true); + } + + public function __toString(): string + { + return (string) $this + ->genericClass + ->getInvoker() + ->getDynamic() + ->getMethods() + ->call( + 'toString' + ); + } + + /** + * @throws ClassNotFoundException + * @throws \PHPJava\Exceptions\ReadEntryException + * @throws \PHPJava\Exceptions\UnknownVersionException + * @throws \PHPJava\Exceptions\ValidatorException + */ + public static function load(string $classPath, array $options = [], bool $enableInstantiated = true): JavaClass + { + static $loaded = []; + $classPath = str_replace('/', '.', Formatter::convertPHPNamespacesToJava($classPath)); + + if (isset($loaded[$classPath]) && !$enableInstantiated) { + return $loaded[$classPath]; + } + + [$type, ] = Formatter::convertJavaNamespaceToPHP( + $classPath + ); + + // Add resolving path. + ClassResolver::add( + [ + [ClassResolver::RESOURCE_TYPE_FILE, getcwd()], + ] + ); + + $instance = null; + if ($type === Formatter::BUILT_IN_PACKAGE) { + $instance = new JavaSimpleClass( + new PackageReader( + $classPath + ), + $options + ); + } else { + foreach (ClassResolver::getClassPaths() as [$resourceType, $value]) { + try { + switch ($resourceType) { + case ClassResolver::RESOURCE_TYPE_JAR: + /** + * @var JavaArchive $value + */ + $instance = $value->getClassByName($classPath); + break; + case ClassResolver::RESOURCE_TYPE_FILE: + $path = realpath( + $value . '/' . ltrim(str_replace('.', '/', $classPath) . '.class', '/') + ); + if ($path === false) { + break; + } + $instance = new JavaCompiledClass( + new FileReader($path), + $options + ); + break; + } + if ($instance !== null) { + break; + } + } catch (ClassNotFoundException $e) { + // do nothing + } + } + } + + if ($instance === null) { + throw new ClassNotFoundException('Class ' . $classPath . ' does not exist.'); + } + + return $loaded[$classPath] = static::of($instance); + } + + /** + * @return JavaClassDeferredLoader + */ + public static function deferred(string $classPath, array $options = []) + { + return new JavaClassDeferredLoader( + $classPath, + $options + ); + } +} diff --git a/src/Core/JavaClassInterface.php b/src/Core/JavaClassInterface.php new file mode 100644 index 00000000..e5d70bfa --- /dev/null +++ b/src/Core/JavaClassInterface.php @@ -0,0 +1,29 @@ + null, + 'major' => null, + ]; + + /** + * @var InterfacePool + */ + private $interfacePool; + + /** + * @var FieldPool + */ + private $fieldPool; + + /** + * @var MethodPool + */ + private $methodPool; + + /** + * @var AttributePool + */ + private $attributePool; + + /** + * @var int + */ + private $accessFlag = 0; + + /** + * @var int + */ + private $thisClass = 0; + + /** + * @var int + */ + private $superClassIndex = 0; + + /** + * @var null|Utf8Info + */ + private $className; + + /** + * @var JavaClassInvoker + */ + private $invoker; + + /** + * @var PHPJava\Kernel\Structures\_Classes[] + */ + private $innerClasses = []; + + /** + * @var mixed + */ + private $superClass; + + /** + * @var float + */ + private $startTime = 0.0; + + /** + * @throws ValidatorException + * @throws \PHPJava\Exceptions\ReadEntryException + * @throws \PHPJava\Exceptions\UnknownVersionException + */ + public function __construct(ReaderInterface $reader, array $options = []) + { + $this->startTime = microtime(true); + + // Validate Java file + if (!(new MagicByte($reader->getReader()->readUnsignedInt()))->isValid()) { + throw new ValidatorException($reader . ' has broken or not Java class.'); + } + + // options + $this->options = $options; + + ClassResolver::add([ + [ClassResolver::RESOURCE_TYPE_FILE, dirname($reader->getFileName())], + [ClassResolver::RESOURCE_TYPE_FILE, getcwd()], + ]); + + // Debug tool + $this->debugTool = new DebugTool( + $reader->getJavaPathName(), + $options + ); + + $this->debugTool->getLogger()->info('Start class emulation'); + + // read minor version + $this->versions['minor'] = $reader->getReader()->readUnsignedShort(); + + $this->debugTool->getLogger()->info('Minor version: ' . $this->versions['minor']); + + // read major version + $this->versions['major'] = $reader->getReader()->readUnsignedShort(); + + $this->debugTool->getLogger()->info('Major version: ' . $this->versions['major']); + + $this->debugTool->getLogger()->info('JDK version: ' . SDKVersionResolver::resolve($this->versions['major'] . '.' . $this->versions['minor'])); + + // read constant pool size + $this->constantPool = new ConstantPool( + $reader, + $reader->getReader()->readUnsignedShort() + ); + + $this->debugTool->getLogger()->info('Constant Pools: ' . count($this->constantPool)); + + // read access flag + $this->accessFlag = $reader->getReader()->readUnsignedShort(); + + // read this class + $this->thisClass = $reader->getReader()->readUnsignedShort(); + + $this->className = $this->constantPool[$this->constantPool[$this->thisClass]->getClassIndex()]; + + // read super class + $this->superClassIndex = $reader->getReader()->readUnsignedShort(); + + $cpInfo = $this->getConstantPool(); + + $this->superClass = JavaClass::load( + $cpInfo[$cpInfo[$this->superClassIndex]->getClassIndex()]->getString(), + $this->options + ); + + $this->debugTool->getLogger()->info( + 'Load super class: ' . $this->superClass->getClassName() + ); + + // read interfaces + $this->interfacePool = new InterfacePool( + $reader, + $reader->getReader()->readUnsignedShort(), + $this->constantPool, + $this->debugTool + ); + + $this->debugTool->getLogger()->info('Extracted interfaces: ' . count($this->interfacePool)); + + // read fields + $this->fieldPool = new FieldPool( + $reader, + $reader->getReader()->readUnsignedShort(), + $this->constantPool, + $this->debugTool + ); + + $this->debugTool->getLogger()->info('Extracted fields: ' . count($this->fieldPool)); + + // read methods + $this->methodPool = new MethodPool( + $reader, + $reader->getReader()->readUnsignedShort(), + $this->constantPool, + $this->debugTool + ); + + $this->debugTool->getLogger()->info('Extracted methods: ' . count($this->methodPool)); + + // read Attributes + $this->attributePool = new AttributePool( + $reader, + $reader->getReader()->readUnsignedShort(), + $this->constantPool, + $this->debugTool + ); + + $this->debugTool->getLogger()->info('Extracted attributes: ' . count($this->attributePool)); + + $this->debugTool->getLogger()->info('Load inner classes'); + + $innerClasses = []; + foreach ($this->attributePool as $entry) { + /** + * @var AttributeInfo $entry + */ + if ($entry->getAttributeData() instanceof InnerClassesAttribute) { + /** + * @var InnerClassesAttribute $attributeData + */ + $attributeData = $entry->getAttributeData(); + foreach ($attributeData->getClasses() as $innerClassInfo) { + /** + * @var InnerClasses $innerClassInfo + */ + $info = $this->constantPool[$innerClassInfo->getInnerClassInfoIndex()]; + $className = $this->constantPool[$info->getClassIndex()]->getString(); + + $innerClasses[] = [ + JavaClass::deferred( + $className, + $this->options + ), + $innerClassInfo, + $this->constantPool, + ]; + } + } + } + + $this->innerClasses = $innerClasses; + + $this->debugTool->getLogger()->info('Loaded inner classes'); + + $this->invoker = new JavaClassInvoker( + $this, + $options + ); + + $this->debugTool->getLogger()->info('End of Class'); + } + + public function __debugInfo() + { + $superClass = $this->getSuperClass(); + return [ + 'JDKVersion' => SDKVersionResolver::resolve($this->versions['major'] . '.' . $this->versions['minor']), + 'name' => str_replace('/', '.', $this->getClassName()), + 'super' => str_replace('/', '.', $this->getSuperClass() ? $this->getSuperClass()->getClassName() : null), + 'methods' => array_map( + function (MethodInfo $method) { + return Formatter::beatifyMethodFromConstantPool( + $method, + $this->constantPool + ); + }, + $this->methodPool->getEntries() + ), + ]; + } + + public function getClassName(bool $shortName = false): string + { + $className = $this->className->getString(); + if ($shortName === true) { + $split = explode('$', $className); + return $split[count($split) - 1]; + } + return $className; + } + + public function getPackageName(): ?string + { + $className = dirname($this->className->getString()); + if ($className === '') { + return null; + } + return str_replace('/', '.', $className); + } + + /** + * @return PHPJava\Core\JVM\FieldInfo[] + */ + public function getDefinedFields(): array + { + return $this->fieldPool->getEntries(); + } + + /** + * @return PHPJava\Core\JVM\MethodInfo[] + */ + public function getDefinedMethods(): array + { + return $this->methodPool->getEntries(); + } + + public function getDefinedExtendedClasses(): array + { + $parents = []; + $currentClassObject = $this; + while ($parentClass = $currentClassObject->getSuperClass()) { + $parents[] = Formatter::convertPHPNamespacesToJava($parentClass->getClassName()); + $currentClassObject = $parentClass; + } + $parents[] = Formatter::convertPHPNamespacesToJava($this->getClassName()); + return $parents; + } + + public function getDefinedInnerClasses(): array + { + return $this->innerClasses; + } + + public function getDefinedInterfaceClasses(): array + { + return $this->interfacePool->getEntries(); + } + + public function getInvoker(): ClassInvokerInterface + { + return $this->invoker; + } + + public function getSuperClass() + { + return $this->superClass; + } + + /** + * @return PHPJava\Core\JVM\_AttributeInfo[] + */ + public function getAttributes(): array + { + return $this->attributePool->getEntries(); + } +} diff --git a/src/Core/JavaGenericClassInterface.php b/src/Core/JavaGenericClassInterface.php new file mode 100644 index 00000000..0601c12b --- /dev/null +++ b/src/Core/JavaGenericClassInterface.php @@ -0,0 +1,30 @@ + null, + 'major' => null, + ]; + + /** + * @var int + */ + private $accessFlag = 0; + + /** + * @var null|Utf8Info + */ + private $className; + + /** + * @var float + */ + private $startTime = 0.0; + + /** + * @var PackageReader + */ + private $reader; + + private $superClass; + + /** + * @throws ValidatorException + * @throws \PHPJava\Exceptions\ReadEntryException + * @throws \PHPJava\Exceptions\UnknownVersionException + */ + public function __construct(ReaderInterface $reader, array $options = []) + { + $this->accessFlag = ClassAccessFlag::ACC_PUBLIC; + $this->startTime = microtime(true); + $this->reader = $reader; + + // options + $this->options = $options; + + ClassResolver::add([ + [ClassResolver::RESOURCE_TYPE_FILE, dirname($reader->getFileName())], + [ClassResolver::RESOURCE_TYPE_FILE, getcwd()], + ]); + + // Debug tool + $this->debugTool = new DebugTool( + $reader->getJavaPathName(), + $options + ); + + $this->invoker = new PHPClassInvoker( + $this, + $this->options + ); + } + + public function __debugInfo() + { + return [ + 'name' => $this->getClassName(), + ]; + } + + public function getClassName(bool $shortName = false): string + { + return $this->reader->getFileName(); + } + + public function getPackageName(): ?string + { + } + + /** + * @return PHPJava\Kernel\Structures\_Classes[] + */ + public function getDefinedInnerClasses(): array + { + return []; + } + + /** + * @return PHPJava\Core\JVM\FieldInfo[] + */ + public function getDefinedFields(): array + { + /** + * @var ReflectionClassReader $reader + */ + $reader = $this->reader->getReader(); + return $reader->getFields(); + } + + /** + * @return PHPJava\Core\JVM\MethodInfo[] + */ + public function getDefinedMethods(): array + { + /** + * @var ReflectionClassReader $reader + */ + $reader = $this->reader->getReader(); + return $reader->getMethods(); + } + + public function getDefinedExtendedClasses(): array + { + $className = $this->getClassName(); + return array_map( + function ($value) { + return Formatter::convertPHPNamespacesToJava( + $value + ); + }, + array_merge( + array_values( + class_parents($className) + ), + [$className] + ) + ); + } + + public function getDefinedInterfaceClasses(): array + { + $className = $this->getClassName(); + return array_map( + function ($value) { + return Formatter::convertPHPNamespacesToJava( + $value + ); + }, + array_values( + class_implements($className) + ) + ); + } + + /** + * @return JavaClass + */ + public function getSuperClass() + { + return $this->superClass; + } + + /** + * @return PHPJava\Core\JVM\_AttributeInfo[] + */ + public function getAttributes(): array + { + return []; + } +} diff --git a/src/Core/PHPJava.php b/src/Core/PHPJava.php new file mode 100644 index 00000000..2fa44d97 --- /dev/null +++ b/src/Core/PHPJava.php @@ -0,0 +1,28 @@ +handle = fopen($file, 'r'); + $this->binaryReader = new BinaryReader($this->handle); + } + + public function getReader(): StreamReaderInterface + { + return $this->binaryReader; + } + + public function getJavaPathName(): string + { + return preg_replace( + '/\.class$/', + '', + basename($this->getFileName()) + ); + } + + public function getFileName(): string + { + return stream_get_meta_data($this->handle)['uri']; + } + + public function __toString(): string + { + return $this->getFileName(); + } +} diff --git a/src/Core/Stream/Reader/InlineReader.php b/src/Core/Stream/Reader/InlineReader.php new file mode 100644 index 00000000..777d7d97 --- /dev/null +++ b/src/Core/Stream/Reader/InlineReader.php @@ -0,0 +1,53 @@ +fileName = $fileName; + $this->handle = fopen('php://memory', 'rw'); + fwrite($this->handle, $code); + rewind($this->handle); + $this->binaryReader = new BinaryReader($this->handle); + } + + public function getReader(): StreamReaderInterface + { + return $this->binaryReader; + } + + public function getJavaPathName(): string + { + return str_replace('/', '.', $this->getFileName()); + } + + public function getFileName(): string + { + return $this->fileName; + } + + public function __toString(): string + { + return $this->getFileName(); + } +} diff --git a/src/Core/Stream/Reader/PackageReader.php b/src/Core/Stream/Reader/PackageReader.php new file mode 100644 index 00000000..2c5669e3 --- /dev/null +++ b/src/Core/Stream/Reader/PackageReader.php @@ -0,0 +1,45 @@ +classPath] = Formatter::convertJavaNamespaceToPHP($classPath); + $this->reflectionClassReader = new ReflectionClassReader( + new \ReflectionClass($this->classPath) + ); + } + + public function getReader(): StreamReaderInterface + { + return $this->reflectionClassReader; + } + + public function getJavaPathName(): string + { + return Formatter::convertJavaNamespaceToPHP($this->classPath)[1]; + } + + public function getFileName(): string + { + return basename($this->classPath); + } + + public function __toString(): string + { + return $this->getFileName(); + } +} diff --git a/src/Core/Stream/Reader/ReaderInterface.php b/src/Core/Stream/Reader/ReaderInterface.php new file mode 100644 index 00000000..b700773c --- /dev/null +++ b/src/Core/Stream/Reader/ReaderInterface.php @@ -0,0 +1,20 @@ +elementValue = new ElementValue($this->reader); + $this->elementValue->setConstantPool($this->getConstantPool()); + $this->elementValue->setDebugTool($this->getDebugTool()); + $this->elementValue->execute(); + } +} diff --git a/src/Kernel/Attributes/AttributeInfo.php b/src/Kernel/Attributes/AttributeInfo.php new file mode 100644 index 00000000..86459f8c --- /dev/null +++ b/src/Kernel/Attributes/AttributeInfo.php @@ -0,0 +1,81 @@ +attributeNameIndex = $this->readUnsignedShort(); + $this->attributeLength = $this->readUnsignedInt(); + $cpInfo = $this->getConstantPool(); + $currentOffset = $this->getOffset(); + + $attributeName = $cpInfo[$this->attributeNameIndex]->getString(); + + if ($loadAttributes !== null && !in_array($attributeName, $loadAttributes, true)) { + $this->read($this->attributeLength); + $this->getDebugTool()->getLogger()->debug('Skip to load an attribute: ' . $attributeName); + return; + } + + $classAttributeName = '\\PHPJava\\Kernel\\Attributes\\' . $attributeName . 'Attribute'; + $this->getDebugTool()->getLogger()->debug('Load an attribute: ' . $attributeName); + $this->attributeData = new $classAttributeName($this->reader); + $this->attributeData->setConstantPool($this->getConstantPool()); + $this->attributeData->setDebugTool($this->getDebugTool()); + $this->attributeData->setAttributeReference($this); + $this->attributeData->execute(); + if ($this->attributeLength != ($actual = $this->getOffset() - $currentOffset)) { + throw new ValidatorException( + 'Invalid attribute counter. expect number is ' . + $this->attributeLength . + ', but actual number is ' . $actual . '.' + ); + } + } + + public function getAttributeData(): ?AttributeInterface + { + return $this->attributeData; + } + + public function getAttributeNameIndex(): int + { + return $this->attributeNameIndex; + } + + public function getAttributeLength(): int + { + return $this->attributeLength; + } +} diff --git a/src/Kernel/Attributes/AttributeInterface.php b/src/Kernel/Attributes/AttributeInterface.php new file mode 100644 index 00000000..b136d7db --- /dev/null +++ b/src/Kernel/Attributes/AttributeInterface.php @@ -0,0 +1,8 @@ +readUnsignedShort(); + + for ($i = 0; $i < $numBootstrapMethods; $i++) { + $bootstrapMethod = new BootstrapMethod($this->reader); + $bootstrapMethod + ->setDebugTool($this->getDebugTool()) + ->setConstantPool($this->getConstantPool()) + ->execute(); + $this->bootstrapMethods[] = $bootstrapMethod; + } + } + + /** + * @return BootstrapMethod[] + */ + public function getBootstrapMethods(): array + { + return $this->bootstrapMethods; + } +} diff --git a/src/Kernel/Attributes/CodeAttribute.php b/src/Kernel/Attributes/CodeAttribute.php new file mode 100644 index 00000000..d6675ea8 --- /dev/null +++ b/src/Kernel/Attributes/CodeAttribute.php @@ -0,0 +1,112 @@ +maxStack = $this->readUnsignedShort(); + $this->maxLocals = $this->readUnsignedShort(); + $this->codeLength = $this->readUnsignedInt(); + + // read Mnemonics + $this->code = []; + for ($i = 0; $i < $this->codeLength; $i++) { + $this->code[$i] = $this->readUnsignedByte(); + $this->rawCode .= chr($this->code[$i]); + } + + // read exception table + $this->exceptionTableLength = $this->readUnsignedShort(); + for ($i = 0; $i < $this->exceptionTableLength; $i++) { + $exceptionTable = new \PHPJava\Kernel\Structures\ExceptionTable($this->reader); + $exceptionTable->setConstantPool($this->getConstantPool()); + $exceptionTable->setDebugTool($this->getDebugTool()); + $exceptionTable->execute(); + $this->exceptionTables[] = $exceptionTable; + } + + $this->attributeCount = $this->readUnsignedShort(); + for ($i = 0; $i < $this->attributeCount; $i++) { + $attributeInfo = new \PHPJava\Kernel\Attributes\AttributeInfo($this->reader); + $attributeInfo->setConstantPool($this->getConstantPool()); + $attributeInfo->setDebugTool($this->getDebugTool()); + $attributeInfo->execute(); + $this->attributeInfo[] = $attributeInfo; + } + } + + /** + * @return \PHPJava\Kernel\Structures\ExceptionTable[] + */ + public function getExceptionTables(): array + { + return $this->exceptionTables; + } + + public function getCode(): string + { + return $this->rawCode; + } + + public function getOpCodes(): int + { + return $this->code; + } + + public function getOpCodeLength(): int + { + return (int) $this->codeLength; + } +} diff --git a/src/Kernel/Attributes/ConstantValueAttribute.php b/src/Kernel/Attributes/ConstantValueAttribute.php new file mode 100644 index 00000000..f81bc5fb --- /dev/null +++ b/src/Kernel/Attributes/ConstantValueAttribute.php @@ -0,0 +1,21 @@ +constantValueIndex = $this->readUnsignedShort(); + } +} diff --git a/src/Kernel/Attributes/DeprecatedAttribute.php b/src/Kernel/Attributes/DeprecatedAttribute.php new file mode 100644 index 00000000..7a8f40c6 --- /dev/null +++ b/src/Kernel/Attributes/DeprecatedAttribute.php @@ -0,0 +1,15 @@ +classIndex = $this->readUnsignedShort(); + $this->methodIndex = $this->readUnsignedShort(); + } +} diff --git a/src/Kernel/Attributes/ExceptionsAttribute.php b/src/Kernel/Attributes/ExceptionsAttribute.php new file mode 100644 index 00000000..ccd2fb38 --- /dev/null +++ b/src/Kernel/Attributes/ExceptionsAttribute.php @@ -0,0 +1,29 @@ +numberOfExceptions = $this->readUnsignedShort(); + for ($i = 0; $i < $this->numberOfExceptions; $i++) { + $this->exceptionIndexTable[] = $this->readUnsignedShort(); + } + } +} diff --git a/src/Kernel/Attributes/InnerClassesAttribute.php b/src/Kernel/Attributes/InnerClassesAttribute.php new file mode 100644 index 00000000..28ecd266 --- /dev/null +++ b/src/Kernel/Attributes/InnerClassesAttribute.php @@ -0,0 +1,43 @@ +numberOfClasses = $this->readUnsignedShort(); + for ($i = 0; $i < $this->numberOfClasses; $i++) { + $class = new InnerClasses($this->reader); + $class->setConstantPool($this->getConstantPool()) + ->setDebugTool($this->getDebugTool()); + $class->execute(); + $this->classes[] = $class; + } + } + + /** + * @return InnerClasses[] + */ + public function getClasses(): array + { + return $this->classes; + } +} diff --git a/src/Kernel/Attributes/LineNumberTableAttribute.php b/src/Kernel/Attributes/LineNumberTableAttribute.php new file mode 100644 index 00000000..4186004c --- /dev/null +++ b/src/Kernel/Attributes/LineNumberTableAttribute.php @@ -0,0 +1,41 @@ +lineNumberTableLength = $this->readUnsignedShort(); + for ($i = 0; $i < $this->lineNumberTableLength; $i++) { + $lineNumberTable = new \PHPJava\Kernel\Structures\LineNumberTable($this->reader); + $lineNumberTable->setConstantPool($this->getConstantPool()); + $lineNumberTable->setDebugTool($this->getDebugTool()); + $lineNumberTable->execute(); + $this->lineNumberTables[] = $lineNumberTable; + } + } + + /** + * @return \PHPJava\Kernel\Structures\LineNumberTable[] + */ + public function getLineNumberTables(): array + { + return $this->lineNumberTables; + } +} diff --git a/src/Kernel/Attributes/LocalVariableTableAttribute.php b/src/Kernel/Attributes/LocalVariableTableAttribute.php new file mode 100644 index 00000000..5b27bcaf --- /dev/null +++ b/src/Kernel/Attributes/LocalVariableTableAttribute.php @@ -0,0 +1,33 @@ +localVariableTableLength = $this->readUnsignedShort(); + for ($i = 0; $i < $this->localVariableTableLength; $i++) { + $localVariableTable = new LocalVariableTable($this->reader); + $localVariableTable->execute(); + $this->localVariableTables[] = $localVariableTable; + } + } +} diff --git a/src/Kernel/Attributes/LocalVariableTypeTableAttribute.php b/src/Kernel/Attributes/LocalVariableTypeTableAttribute.php new file mode 100644 index 00000000..8a41feb2 --- /dev/null +++ b/src/Kernel/Attributes/LocalVariableTypeTableAttribute.php @@ -0,0 +1,33 @@ +localVariableTypeTableLength = $this->readUnsignedShort(); + $this->localVariableTypeTable = []; + for ($i = 0; $i < $this->localVariableTypeTableLength; $i++) { + $this->localVariableTypeTable[$i] = new LocalVariableTypeTable($this->reader); + $this->localVariableTypeTable[$i]->execute(); + } + } +} diff --git a/src/Kernel/Attributes/MethodParametersAttribute.php b/src/Kernel/Attributes/MethodParametersAttribute.php new file mode 100644 index 00000000..5222511a --- /dev/null +++ b/src/Kernel/Attributes/MethodParametersAttribute.php @@ -0,0 +1,35 @@ +readUnsignedByte(); + for ($i = 0; $i < $parametersCount; $i++) { + $this->parameters[] = [ + 'name_index' => $this->readUnsignedShort(), + 'access_flags' => $this->readUnsignedShort(), + ]; + } + } + + /** + * @return int[][] + */ + public function getParameters(): array + { + return $this->parameters; + } +} diff --git a/src/Kernel/Attributes/NestMembersAttribute.php b/src/Kernel/Attributes/NestMembersAttribute.php new file mode 100644 index 00000000..2667969b --- /dev/null +++ b/src/Kernel/Attributes/NestMembersAttribute.php @@ -0,0 +1,34 @@ +readUnsignedShort(); + $cp = $this->getConstantPool(); + + for ($i = 0; $i < $numberOfClasses; $i++) { + $this->classes[] = $cp[$this->readUnsignedShort()]; + } + } + + /** + * @var \PHPJava\Core\JVM\StructureInterface[] + */ + public function getClasses(): array + { + return $this->classes; + } +} diff --git a/src/Kernel/Attributes/RuntimeInvisibleAnnotationsAttribute.php b/src/Kernel/Attributes/RuntimeInvisibleAnnotationsAttribute.php new file mode 100644 index 00000000..9f5a6665 --- /dev/null +++ b/src/Kernel/Attributes/RuntimeInvisibleAnnotationsAttribute.php @@ -0,0 +1,35 @@ +numAnnotations = $this->readUnsignedShort(); + for ($i = 0; $i < $this->numAnnotations; $i++) { + $annotation = new Annotation($this->reader); + $annotation->setConstantPool($this->getConstantPool()); + $annotation->setDebugTool($this->getDebugTool()); + $annotation->execute(); + $this->annotations[] = $annotation; + } + } +} diff --git a/src/Kernel/Attributes/RuntimeInvisibleParameterAnnotationsAttribute.php b/src/Kernel/Attributes/RuntimeInvisibleParameterAnnotationsAttribute.php new file mode 100644 index 00000000..843d344d --- /dev/null +++ b/src/Kernel/Attributes/RuntimeInvisibleParameterAnnotationsAttribute.php @@ -0,0 +1,36 @@ +numParameters = $this->readUnsignedByte(); + + for ($i = 0; $i < $this->numParameters; $i++) { + $annotation = new ParameterAnnotation($this->reader); + $annotation->setConstantPool($this->getConstantPool()); + $annotation->setDebugTool($this->getDebugTool()); + $annotation->execute(); + $this->annotations[] = $annotation; + } + } +} diff --git a/src/Kernel/Attributes/RuntimeVisibleAnnotationsAttribute.php b/src/Kernel/Attributes/RuntimeVisibleAnnotationsAttribute.php new file mode 100644 index 00000000..44d639ec --- /dev/null +++ b/src/Kernel/Attributes/RuntimeVisibleAnnotationsAttribute.php @@ -0,0 +1,35 @@ +numAnnotations = $this->readUnsignedShort(); + for ($i = 0; $i < $this->numAnnotations; $i++) { + $annotation = new Annotation($this->reader); + $annotation->setConstantPool($this->getConstantPool()); + $annotation->setDebugTool($this->getDebugTool()); + $annotation->execute(); + $this->annotations[] = $annotation; + } + } +} diff --git a/src/Kernel/Attributes/RuntimeVisibleParameterAnnotationsAttribute.php b/src/Kernel/Attributes/RuntimeVisibleParameterAnnotationsAttribute.php new file mode 100644 index 00000000..4a7d98cd --- /dev/null +++ b/src/Kernel/Attributes/RuntimeVisibleParameterAnnotationsAttribute.php @@ -0,0 +1,18 @@ +signatureIndex = $this->readUnsignedShort(); + } + + public function getSignatureIndex(): int + { + return $this->signatureIndex; + } +} diff --git a/src/Kernel/Attributes/SourceDebugExtensionAttribute.php b/src/Kernel/Attributes/SourceDebugExtensionAttribute.php new file mode 100644 index 00000000..1d12399e --- /dev/null +++ b/src/Kernel/Attributes/SourceDebugExtensionAttribute.php @@ -0,0 +1,25 @@ +debugExtension = $this->read( + $this + ->getAttributeReference() + ->getAttributeLength() + ); + } +} diff --git a/src/Kernel/Attributes/SourceFileAttribute.php b/src/Kernel/Attributes/SourceFileAttribute.php new file mode 100644 index 00000000..59a8d77e --- /dev/null +++ b/src/Kernel/Attributes/SourceFileAttribute.php @@ -0,0 +1,26 @@ +sourceFileIndex = $this->readUnsignedShort(); + } + + public function getSourceFileIndex(): int + { + return $this->sourceFileIndex; + } +} diff --git a/src/Kernel/Attributes/StackMapTableAttribute.php b/src/Kernel/Attributes/StackMapTableAttribute.php new file mode 100644 index 00000000..0b2f3f7e --- /dev/null +++ b/src/Kernel/Attributes/StackMapTableAttribute.php @@ -0,0 +1,41 @@ +numberOfEntries = $this->readUnsignedShort(); + for ($i = 0; $i < $this->numberOfEntries; $i++) { + $stackMapFrame = new \PHPJava\Kernel\Structures\StackMapFrameInfo($this->reader); + $stackMapFrame->setConstantPool($this->getConstantPool()); + $stackMapFrame->setDebugTool($this->getDebugTool()); + $stackMapFrame->execute(); + $this->stackMapFrames[] = $stackMapFrame; + } + } + + /** + * @return \PHPJava\Kernel\Structures\StackMapFrameInfo[] + */ + public function getStackMapFrames(): array + { + return $this->stackMapFrames; + } +} diff --git a/src/Kernel/Attributes/SyntheticAttribute.php b/src/Kernel/Attributes/SyntheticAttribute.php new file mode 100644 index 00000000..cfad8e7a --- /dev/null +++ b/src/Kernel/Attributes/SyntheticAttribute.php @@ -0,0 +1,18 @@ +method = $method; + $this->attributes = $method->getAttributes(); + $this->javaClassInvoker = $javaClassInvoker; + $this->javaClass = $javaClassInvoker->getJavaClass(); + $this->options = $this->javaClass->getOptions(); + $this->reader = $reader; + $this->localStorage = &$localStorage; + $this->stacks = &$stacks; + $this->pointer = $pointer; + $this->dependencyInjectionProvider = $dependencyInjectionProvider; + return $this; + } + + final public function read(int $bytes = 1): string + { + return $this->reader->read($bytes); + } + + public function readByte(): int + { + return $this->reader->readByte(); + } + + public function readUnsignedByte(): int + { + return $this->reader->readUnsignedByte(); + } + + public function readUnsignedInt(): int + { + return $this->reader->readUnsignedInt(); + } + + public function readUnsignedShort(): int + { + return $this->reader->readUnsignedShort(); + } + + public function readInt(): int + { + return $this->reader->readInt(); + } + + public function readShort(): int + { + return $this->reader->readShort(); + } + + public function readUnsignedLong(): int + { + return $this->reader->readUnsignedLong(); + } + + public function readLong(): int + { + return current(unpack('q', $this->read(8))); + } + + public function seek(int $bytes): void + { + $this->reader->seek($bytes); + } + + public function setOffset(int $pointer): void + { + $this->reader->setOffset($pointer); + } + + public function getOffset(): int + { + return $this->reader->getOffset(); + } + + public function pushToOperandStack($value): void + { + $this->pushedOperandStacks[] = $this->stacks[] = $value; + } + + public function pushToOperandStackByReference(&$value): void + { + $this->pushedOperandStacks[] = $this->stacks[] = &$value; + } + + public function popFromOperandStack() + { + if (empty($this->stacks)) { + throw new IllegalOperationException('Cannot pop an item from stack.'); + } + return $this->poppedOperandStacks[] = array_pop($this->stacks); + } + + public function getCurrentStackIndex(): int + { + return count($this->stacks) - 1; + } + + public function popStack(): self + { + $this->poppedOperandStacks[] = array_pop($this->stacks); + return $this; + } + + public function getStacks() + { + return $this->stacks; + } + + public function setLocalStorage($index, $value): void + { + $this->localStorage[(int) $index] = $value; + } + + public function getLocalStorage($index) + { + if (!isset($this->localStorage[(int) $index])) { + $this->localStorage[(int) $index] = null; + } + return $this->localStorage[(int) $index]; + } + + public function getLocalStorages(): array + { + return $this->localStorage; + } + + public function getProgramCounter(): int + { + return $this->pointer; + } + + public function getOptions($key) + { + return $this->options[$key] ?? null; + } + + /** + * @return \PHPJava\Kernel\Attributes\AttributeInfo[] + */ + public function getAttributes(): array + { + return $this->attributes; + } +} diff --git a/src/Kernel/Core/AttributeReference.php b/src/Kernel/Core/AttributeReference.php new file mode 100644 index 00000000..85747232 --- /dev/null +++ b/src/Kernel/Core/AttributeReference.php @@ -0,0 +1,24 @@ +attributeInfoReference = $attributeInfo; + return $this; + } + + public function getAttributeReference(): AttributeInterface + { + return $this->attributeInfoReference; + } +} diff --git a/src/Kernel/Core/BinaryReader.php b/src/Kernel/Core/BinaryReader.php new file mode 100644 index 00000000..9c187ccf --- /dev/null +++ b/src/Kernel/Core/BinaryReader.php @@ -0,0 +1,78 @@ +reader = $reader; + } + + final public function read(int $bytes = 1): string + { + return $this->reader->getReader()->read($bytes); + } + + public function readByte(): int + { + return $this->reader->getReader()->readByte(); + } + + public function readUnsignedByte(): int + { + return $this->reader->getReader()->readUnsignedByte(); + } + + public function readUnsignedInt(): int + { + return $this->reader->getReader()->readUnsignedInt(); + } + + public function readUnsignedShort(): int + { + return $this->reader->getReader()->readUnsignedShort(); + } + + public function readInt(): int + { + return $this->reader->getReader()->readInt(); + } + + public function readShort(): int + { + return $this->reader->getReader()->readShort(); + } + + public function readUnsignedLong(): int + { + return $this->reader->getReader()->readUnsignedLong(); + } + + public function readLong(): int + { + return $this->reader->getReader()->readLong(); + } + + public function seek($bytes): void + { + $this->reader->getReader()->seek($bytes); + } + + public function setOffset($pointer): void + { + $this->reader->getReader()->setOffset($pointer); + } + + public function getOffset(): int + { + return $this->reader->getReader()->getOffset(); + } +} diff --git a/src/Kernel/Core/ConstantPool.php b/src/Kernel/Core/ConstantPool.php new file mode 100644 index 00000000..2846019d --- /dev/null +++ b/src/Kernel/Core/ConstantPool.php @@ -0,0 +1,22 @@ +constantPool = $constantPool; + return $this; + } + + public function getConstantPool(): \PHPJava\Core\JVM\ConstantPool + { + return $this->constantPool; + } +} diff --git a/src/Kernel/Core/DebugTool.php b/src/Kernel/Core/DebugTool.php new file mode 100644 index 00000000..64f96518 --- /dev/null +++ b/src/Kernel/Core/DebugTool.php @@ -0,0 +1,22 @@ +debugTool = $debugTool; + return $this; + } + + public function getDebugTool(): \PHPJava\Utilities\DebugTool + { + return $this->debugTool; + } +} diff --git a/src/Kernel/Core/DependencyInjector.php b/src/Kernel/Core/DependencyInjector.php new file mode 100644 index 00000000..241a3b6a --- /dev/null +++ b/src/Kernel/Core/DependencyInjector.php @@ -0,0 +1,87 @@ +getInfoAnnotateInjections($annotations), + $this->getNativeAnnotateInjections($annotations), + $this->getProviderAnnotateInjections($annotations) + ); + } + + /** + * @throws \PHPJava\Exceptions\ProviderException + * @return ( + * \PHPJava\Core\JVM\ConstantPool| + * \PHPJava\Core\JavaClass| + * \PHPJava\Core\JavaClassInvoker| + * \PHPJava\Kernel\Attributes\AttributeInfo| + * array + * )[] + */ + private function getInfoAnnotateInjections(array $annotations): array + { + // Native annotation will inject a dependency. + $injections = []; + foreach (($annotations['depended-info'] ?? []) as $info) { + switch (strtolower(trim((string) $info))) { + case 'signature': + $injections[] = $this->methodSignature; + break; + } + } + return $injections; + } + + /** + * @throws \PHPJava\Exceptions\ProviderException + * @return ( + * \PHPJava\Core\JVM\ConstantPool| + * \PHPJava\Core\JavaClass| + * \PHPJava\Core\JavaClassInvoker| + * \PHPJava\Kernel\Attributes\AttributeInfo| + * array + * )[] + */ + private function getNativeAnnotateInjections(array $annotations): array + { + // Native annotation will inject a dependency. + $injections = []; + foreach (($annotations['native'] ?? []) as $native) { + $injections[] = $this->dependencyInjectionProvider + ->get(trim((string) $native)); + } + return $injections; + } + + /** + * @throws \PHPJava\Exceptions\ProviderException + * @return \PHPJava\Kernel\Provider\ProviderInterface[] + */ + private function getProviderAnnotateInjections(array $annotations): array + { + $injections = []; + foreach (($annotations['provider'] ?? []) as $provider) { + $injections[] = $this->javaClassInvoker + ->getProvider( + trim($provider) + ); + } + return $injections; + } +} diff --git a/src/Kernel/Core/ExceptionTableInspectable.php b/src/Kernel/Core/ExceptionTableInspectable.php new file mode 100644 index 00000000..20f11e50 --- /dev/null +++ b/src/Kernel/Core/ExceptionTableInspectable.php @@ -0,0 +1,74 @@ +getAttributes(), + CodeAttribute::class + ); + + $expectedClass = Formatter::convertPHPNamespacesToJava( + get_class($e) + ); + + $cpInfo = $this->getConstantPool(); + + foreach ($codeAttribute->getExceptionTables() as $exception) { + /** + * @var ExceptionTable $exception + */ + if ($exception->getStartPc() > $this->getProgramCounter() || + $exception->getEndPc() < $this->getProgramCounter() + ) { + continue; + } + + // In finally statement. + if ($exception->getCatchType() === 0) { + $this->setOffset($exception->getHandlerPc()); + return true; + } + $catchClass = Formatter::convertPHPNamespacesToJava($cpInfo[$cpInfo[$exception->getCatchType()]->getClassIndex()]->getString()); + if ($catchClass === $expectedClass) { + $instance = JavaClass::load($catchClass) + ->getInvoker() + ->construct( + $e->getMessage(), + $e->getCode(), + $e + ); + $this->pushToOperandStack($instance); + $this->setOffset($exception->getHandlerPc()); + return true; + } + } + + throw new UncaughtException( + $expectedClass . ': ' . $e->getMessage(), + 0, + $e + ); + } +} diff --git a/src/Kernel/Filters/Normalizer.php b/src/Kernel/Filters/Normalizer.php new file mode 100644 index 00000000..5c346613 --- /dev/null +++ b/src/Kernel/Filters/Normalizer.php @@ -0,0 +1,122 @@ + &$value) { + $realType = $normalizeTypes[$key] ?? null; + if ($realType === null) { + throw new NormalizerException('Broken arguments parser.'); + } + /** + * @var Collection|Type $value + */ + if ($value instanceof Collection) { + $value = static::normalizeValues( + $value, + array_fill( + 0, + count($value), + $realType + ) + ); + continue; + } + if (!TypeResolver::isPrimitive($realType['type'])) { + // TODO: implements up-cast and down-cast + continue; + } + $initiateClass = $realType['type']; + if ($value instanceof $initiateClass) { + continue; + } + + /** + * @var Type $initiateClass + */ + $value = $initiateClass::get( + static::getPrimitiveValue($value) + ); + } + + return $values; + } + + public static function normalizeFields(array $fields, JavaClassInterface $fromJavaClass = null): array + { + $newFields = []; + foreach ($fields as $name => $value) { + /** + * @var FieldInfo $value + */ + $cp = $value->getConstantPool(); + $descriptor = Formatter::parseSignature($cp[$value->getDescriptorIndex()]->getString()); + [$type, $class, $dimensionsOfArray] = TypeResolver::getType($descriptor[0]); + + switch ($type) { + case TypeResolver::IS_PRIMITIVE: + $newFields[$name] = $class::get(); + break; + case TypeResolver::IS_CLASS: + $newFields[$name] = JavaClass::load('java.lang.Object'); + break; + case TypeResolver::IS_ARRAY: + $newFields[$name] = null; + break; + default: + throw new NormalizerException('Failed to normalize fields.'); + } + } + return $newFields; + } + + /** + * @throws \PHPJava\Exceptions\TypeException + * @return Type + */ + public static function normalizeReturnValue($value, array $signatureArray) + { + /** + * @var Type $typeClass + */ + [$type, $typeClass] = TypeResolver::getType($signatureArray); + + return $type === TypeResolver::IS_PRIMITIVE + ? $typeClass::get($value) + : $value; + } + + /** + * @param $value + * @return bool|float|int|string + */ + public static function getPrimitiveValue($value) + { + if ($value instanceof PrimitiveValueInterface) { + return TypeResolver::extractPrimitiveValueFromType($value); + } + return $value; + } +} diff --git a/src/Kernel/Frames/AppendFrame.php b/src/Kernel/Frames/AppendFrame.php new file mode 100644 index 00000000..c9a17e86 --- /dev/null +++ b/src/Kernel/Frames/AppendFrame.php @@ -0,0 +1,35 @@ +frameType = $this->readUnsignedByte(); + $this->offsetDelta = $this->readUnsignedShort(); + for ($i = 0, $s = $this->frameType - 251; $i < $s; $i++) { + $local = new \PHPJava\Kernel\Structures\VerificationTypeInfo($this->reader); + $local->execute(); + $this->locals[] = $local; + } + } +} diff --git a/src/Kernel/Frames/ChopFrame.php b/src/Kernel/Frames/ChopFrame.php new file mode 100644 index 00000000..321a650e --- /dev/null +++ b/src/Kernel/Frames/ChopFrame.php @@ -0,0 +1,25 @@ +frameType = $this->readUnsignedByte(); + $this->offsetDelta = $this->readUnsignedShort(); + } +} diff --git a/src/Kernel/Frames/FrameInterface.php b/src/Kernel/Frames/FrameInterface.php new file mode 100644 index 00000000..eb8b96e9 --- /dev/null +++ b/src/Kernel/Frames/FrameInterface.php @@ -0,0 +1,8 @@ +frameType = $this->readUnsignedByte(); + $this->offsetDelta = $this->readUnsignedShort(); + $this->numberOfLocals = $this->readUnsignedShort(); + for ($i = 0; $i < $this->numberOfLocals; $i++) { + $local = new \PHPJava\Kernel\Structures\VerificationTypeInfo($this->reader); + $local->execute(); + $this->locals = $local; + } + $this->numberOfStackItems = $this->readUnsignedShort(); + for ($i = 0; $i < $this->numberOfStackItems; $i++) { + $stack = new \PHPJava\Kernel\Structures\VerificationTypeInfo($this->reader); + $stack->execute(); + $this->stack[] = $stack; + } + } +} diff --git a/src/Kernel/Frames/SameFrame.php b/src/Kernel/Frames/SameFrame.php new file mode 100644 index 00000000..d7f2d3f5 --- /dev/null +++ b/src/Kernel/Frames/SameFrame.php @@ -0,0 +1,19 @@ +frameType = $this->readUnsignedByte(); + } +} diff --git a/src/Kernel/Frames/SameFrameExtended.php b/src/Kernel/Frames/SameFrameExtended.php new file mode 100644 index 00000000..2761db62 --- /dev/null +++ b/src/Kernel/Frames/SameFrameExtended.php @@ -0,0 +1,25 @@ +frameType = $this->readUnsignedByte(); + $this->offsetDelta = $this->readUnsignedShort(); + } +} diff --git a/src/Kernel/Frames/SameLocals1StackItemFrame.php b/src/Kernel/Frames/SameLocals1StackItemFrame.php new file mode 100644 index 00000000..848d45a8 --- /dev/null +++ b/src/Kernel/Frames/SameLocals1StackItemFrame.php @@ -0,0 +1,27 @@ +frameType = $this->readUnsignedByte(); + $stack = new \PHPJava\Kernel\Structures\VerificationTypeInfo($this->reader); + $stack->execute(); + $this->stack[] = $stack; + } +} diff --git a/src/Kernel/Frames/SameLocals1StackItemFrameExtended.php b/src/Kernel/Frames/SameLocals1StackItemFrameExtended.php new file mode 100644 index 00000000..548289d9 --- /dev/null +++ b/src/Kernel/Frames/SameLocals1StackItemFrameExtended.php @@ -0,0 +1,33 @@ +frameType = $this->readUnsignedByte(); + $this->offsetDelta = $this->readUnsignedShort(); + $local = new \PHPJava\Kernel\Structures\VerificationTypeInfo($this->reader); + $local->execute(); + $this->locals[] = $local; + } +} diff --git a/src/Kernel/Internal/ClassDeferredLoader.php b/src/Kernel/Internal/ClassDeferredLoader.php new file mode 100644 index 00000000..a9f25646 --- /dev/null +++ b/src/Kernel/Internal/ClassDeferredLoader.php @@ -0,0 +1,49 @@ +initializeIfNotInitiated())->__debugInfo(); + } + + /** + * @throws \PHPJava\Exceptions\ReadEntryException + * @throws \PHPJava\Exceptions\UnknownVersionException + * @throws \PHPJava\Exceptions\ValidatorException + */ + public function __call(string $name, $arguments) + { + return ($this->initializeIfNotInitiated())->{$name}(...$arguments); + } + + /** + * @throws \PHPJava\Exceptions\ReadEntryException + * @throws \PHPJava\Exceptions\UnknownVersionException + * @throws \PHPJava\Exceptions\ValidatorException + */ + public function __invoke(...$arguments): JavaClassInterface + { + return ($this->initializeIfNotInitiated())(...$arguments); + } + + /** + * @throws ClassDeferredLoaderException + */ + protected function initializeIfNotInitiated(): JavaClass + { + throw new ClassDeferredLoaderException('Failed to load a non-specified class.'); + } +} diff --git a/src/Kernel/Internal/InstanceDeferredLoader.php b/src/Kernel/Internal/InstanceDeferredLoader.php new file mode 100644 index 00000000..929f6610 --- /dev/null +++ b/src/Kernel/Internal/InstanceDeferredLoader.php @@ -0,0 +1,41 @@ +className = $className; + } + + public function getClassName(): string + { + return $this->className; + } + + public function getInstance(): object + { + return $this->instance; + } +} diff --git a/src/Kernel/Internal/JavaClassDeferredLoader.php b/src/Kernel/Internal/JavaClassDeferredLoader.php new file mode 100644 index 00000000..a865a152 --- /dev/null +++ b/src/Kernel/Internal/JavaClassDeferredLoader.php @@ -0,0 +1,50 @@ +loadingClassName = $loadingClassName; + $this->options = $options; + } + + /** + * @throws \PHPJava\Exceptions\ReadEntryException + * @throws \PHPJava\Exceptions\UnknownVersionException + * @throws \PHPJava\Exceptions\ValidatorException + * @throws \PHPJava\Packages\java\lang\ClassNotFoundException + */ + protected function initializeIfNotInitiated(): JavaClass + { + if (isset($this->javaClass)) { + return $this->javaClass; + } + return $this->javaClass = JavaClass::load( + $this->loadingClassName, + $this->options + ); + } +} diff --git a/src/Kernel/Internal/JavaInlineClassDeferredLoader.php b/src/Kernel/Internal/JavaInlineClassDeferredLoader.php new file mode 100644 index 00000000..1615b641 --- /dev/null +++ b/src/Kernel/Internal/JavaInlineClassDeferredLoader.php @@ -0,0 +1,51 @@ +arguments = $arguments; + $this->options = $options; + } + + /** + * @throws \PHPJava\Exceptions\ReadEntryException + * @throws \PHPJava\Exceptions\UnknownVersionException + * @throws \PHPJava\Exceptions\ValidatorException + */ + protected function initializeIfNotInitiated(): JavaClass + { + if (isset($this->javaClass)) { + return $this->javaClass; + } + return $this->javaClass = new JavaClass( + new JavaCompiledClass( + new InlineReader(...$this->arguments), + $this->options + ) + ); + } +} diff --git a/src/Kernel/Internal/Lambda.php b/src/Kernel/Internal/Lambda.php new file mode 100644 index 00000000..7a36e8d0 --- /dev/null +++ b/src/Kernel/Internal/Lambda.php @@ -0,0 +1,76 @@ +name = $name; + $this->descriptor = $descriptor; + $this->class = $class; + + $this->methodEntity = [ + $name, + $invokedName, + $javaClass, + ]; + + $this->classObject = JavaClass::load( + $class, + $javaClass->getOptions() + ); + } + + /** + * @param $name + * @param $arguments + */ + public function __call($name, $arguments) + { + return $this->classObject->{$name}(...$arguments); + } + + public function __invoke(string $name, ...$arguments) + { + [$actualMethodName, $entityMethodName, $referredClassObject] = $this->methodEntity; + + return $referredClassObject + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + $actualMethodName, + ...$arguments + ); + } +} diff --git a/src/Kernel/Maps/ClassAccessFlag.php b/src/Kernel/Maps/ClassAccessFlag.php new file mode 100644 index 00000000..6e1d3537 --- /dev/null +++ b/src/Kernel/Maps/ClassAccessFlag.php @@ -0,0 +1,16 @@ +getConstants(), true)) !== false) { + return $key; + } + } catch (\ReflectionException $e) { + } + return null; + } + + public function getValue(string $name) + { + try { + $reflectionClass = new \ReflectionClass($this); + return $reflectionClass->getConstant($name); + } catch (\ReflectionException $e) { + } + + return null; + } + + /** + * @return string[] + */ + public function getValues(): array + { + try { + $reflectionClass = new \ReflectionClass($this); + return $reflectionClass->getConstants(); + } catch (\ReflectionException $e) { + } + return []; + } +} diff --git a/src/Kernel/Maps/MethodAccessFlag.php b/src/Kernel/Maps/MethodAccessFlag.php new file mode 100644 index 00000000..5972b539 --- /dev/null +++ b/src/Kernel/Maps/MethodAccessFlag.php @@ -0,0 +1,19 @@ +operands ?? null; + } + + /** + * Execute. + */ + public function execute(): void + { + $this->beforeExecute(); + $this->isExecuted = true; + } + + public function returnValue() + { + return $this->returnValue; + } + + public function isExecuted(): bool + { + return $this->isExecuted; + } + + public function isStackingOperation(): bool + { + return $this->isStackingOperation; + } + + public function getName(): string + { + return ltrim( + preg_replace( + '/.+\\\\([^$]+)$/', + '$1', + get_class($this) + ), + '_' + ); + } + + public function getCode(): int + { + return (new \ReflectionClassConstant( + OpCode::class, + preg_replace( + '/.+\\\\([^$]+)$/', + '$1', + get_class($this) + ) + ))->getValue(); + } + + /** + * Prepare an execution. + */ + public function beforeExecute(): void + { + } + + /** + * After treatment an execution. + */ + public function afterExecute(): void + { + // Clear references. + $this->poppedOperandStacks = []; + $this->pushedOperandStacks = []; + } + + public function getPoppedOperandStacks(): array + { + if (!$this->isExecuted) { + throw new OperationException( + __METHOD__ . ' cannot call before executing an operation because PHPJava cannot understands that popping several operand stacks beforehand.' + ); + } + return $this->poppedOperandStacks; + } + + public function getPushedOperandStacks(): array + { + if (!$this->isExecuted) { + throw new OperationException( + __METHOD__ . ' cannot call before executing an operation because PHPJava cannot understands that pushing several operand stacks beforehand.' + ); + } + return $this->pushedOperandStacks; + } +} diff --git a/src/Kernel/Mnemonics/Operands.php b/src/Kernel/Mnemonics/Operands.php new file mode 100644 index 00000000..c0a02414 --- /dev/null +++ b/src/Kernel/Mnemonics/Operands.php @@ -0,0 +1,59 @@ +operands[$alias] = [$value, $operandNames]; + } + } + + public function count() + { + return count($this->getInfo()); + } + + public function getInfo(): array + { + $operands = []; + foreach ($this->operands as [, $operandParameters]) { + ArrayTool::concat($operands, ...$operandParameters); + } + return $operands; + } + + public function getElements(): array + { + return $this->operands; + } + + public function offsetExists($offset) + { + return isset($this->operands[$offset]); + } + + public function offsetGet($offset) + { + if (!$this->offsetExists($offset)) { + throw new OperationException('Does not exist an operand.'); + } + return $this->operands[$offset][0]; + } + + public function offsetSet($offset, $value) + { + throw new OperationException('Operands class is not supported overwrite.'); + } + + public function offsetUnset($offset) + { + throw new OperationException('Operands class is not supported unset an element.'); + } +} diff --git a/src/Kernel/Mnemonics/OperationCodeInterface.php b/src/Kernel/Mnemonics/OperationCodeInterface.php new file mode 100644 index 00000000..acbba296 --- /dev/null +++ b/src/Kernel/Mnemonics/OperationCodeInterface.php @@ -0,0 +1,28 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + /** + * load onto the stack a reference from an array. + */ + public function execute(): void + { + parent::execute(); + $index = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $arrayref = $this->popFromOperandStack(); + + $this->pushToOperandStack( + $arrayref[$index] + ); + } +} diff --git a/src/Kernel/Mnemonics/_aastore.php b/src/Kernel/Mnemonics/_aastore.php new file mode 100644 index 00000000..d0dadba9 --- /dev/null +++ b/src/Kernel/Mnemonics/_aastore.php @@ -0,0 +1,29 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + /** + * store into a reference in an array. + */ + public function execute(): void + { + parent::execute(); + $value = $this->popFromOperandStack(); + $index = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $arrayref = $this->popFromOperandStack(); + $arrayref[$index] = $value; + } +} diff --git a/src/Kernel/Mnemonics/_aconst_null.php b/src/Kernel/Mnemonics/_aconst_null.php new file mode 100644 index 00000000..6e1bd7d3 --- /dev/null +++ b/src/Kernel/Mnemonics/_aconst_null.php @@ -0,0 +1,26 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + /** + * store into a reference in an array. + */ + public function execute(): void + { + parent::execute(); + $this->pushToOperandStack(null); + } +} diff --git a/src/Kernel/Mnemonics/_aload.php b/src/Kernel/Mnemonics/_aload.php new file mode 100644 index 00000000..80344f35 --- /dev/null +++ b/src/Kernel/Mnemonics/_aload.php @@ -0,0 +1,31 @@ +operands !== null) { + return $this->operands; + } + $index = $this->readByte(); + + return $this->operands = new Operands( + ['index', $index, ['index']] + ); + } + + /** + * load a reference onto the stack from a local variable #index. + */ + public function execute(): void + { + parent::execute(); + $index = $this->getOperands()['index']; + $this->pushToOperandStack($this->getLocalStorage($index)); + } +} diff --git a/src/Kernel/Mnemonics/_aload_0.php b/src/Kernel/Mnemonics/_aload_0.php new file mode 100644 index 00000000..14afe04a --- /dev/null +++ b/src/Kernel/Mnemonics/_aload_0.php @@ -0,0 +1,27 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + /** + * load a reference onto the stack from local variable 0. + */ + public function execute(): void + { + parent::execute(); + $index = 0; + $this->pushToOperandStack($this->getLocalStorage($index)); + } +} diff --git a/src/Kernel/Mnemonics/_aload_1.php b/src/Kernel/Mnemonics/_aload_1.php new file mode 100644 index 00000000..e2b4dd7a --- /dev/null +++ b/src/Kernel/Mnemonics/_aload_1.php @@ -0,0 +1,27 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + /** + * load a reference onto the stack from local variable 1. + */ + public function execute(): void + { + parent::execute(); + $index = 1; + $this->pushToOperandStack($this->getLocalStorage($index)); + } +} diff --git a/src/Kernel/Mnemonics/_aload_2.php b/src/Kernel/Mnemonics/_aload_2.php new file mode 100644 index 00000000..93dac352 --- /dev/null +++ b/src/Kernel/Mnemonics/_aload_2.php @@ -0,0 +1,27 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + /** + * load a reference onto the stack from local variable 2. + */ + public function execute(): void + { + parent::execute(); + $index = 2; + $this->pushToOperandStack($this->getLocalStorage($index)); + } +} diff --git a/src/Kernel/Mnemonics/_aload_3.php b/src/Kernel/Mnemonics/_aload_3.php new file mode 100644 index 00000000..436d7d07 --- /dev/null +++ b/src/Kernel/Mnemonics/_aload_3.php @@ -0,0 +1,27 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + /** + * load a reference onto the stack from local variable 3. + */ + public function execute(): void + { + parent::execute(); + $index = 3; + $this->pushToOperandStack($this->getLocalStorage($index)); + } +} diff --git a/src/Kernel/Mnemonics/_anewarray.php b/src/Kernel/Mnemonics/_anewarray.php new file mode 100644 index 00000000..f38e6c87 --- /dev/null +++ b/src/Kernel/Mnemonics/_anewarray.php @@ -0,0 +1,53 @@ +operands !== null) { + return $this->operands; + } + $indexbyte = $this->readUnsignedShort(); + + return $this->operands = new Operands( + ['indexbyte', $indexbyte, ['indexbyte1', 'indexbyte2']] + ); + } + + /** + * create a new array of references of length count and component + * type identified by the class reference index (indexbyte1 << 8 + indexbyte2) + * in the constant pool. + */ + public function execute(): void + { + parent::execute(); + // Current class index for Constant Pool + $this->getOperands()['indexbyte']; + + // Get an array size + $count = Normalizer::getPrimitiveValue( + $this->popFromOperandStack() + ); + + // Make an array with object. + $array = new \ArrayIterator( + array_fill( + 0, + $count, + null + ) + ); + + $this->pushToOperandStackByReference( + $array + ); + } +} diff --git a/src/Kernel/Mnemonics/_areturn.php b/src/Kernel/Mnemonics/_areturn.php new file mode 100644 index 00000000..8c1f6205 --- /dev/null +++ b/src/Kernel/Mnemonics/_areturn.php @@ -0,0 +1,21 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->returnValue = $this->popFromOperandStack(); + } +} diff --git a/src/Kernel/Mnemonics/_arraylength.php b/src/Kernel/Mnemonics/_arraylength.php new file mode 100644 index 00000000..6e6b0d33 --- /dev/null +++ b/src/Kernel/Mnemonics/_arraylength.php @@ -0,0 +1,26 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $arrayref = $this->popFromOperandStack(); + $this->pushToOperandStack( + count($arrayref) + ); + } +} diff --git a/src/Kernel/Mnemonics/_astore.php b/src/Kernel/Mnemonics/_astore.php new file mode 100644 index 00000000..ae769fb7 --- /dev/null +++ b/src/Kernel/Mnemonics/_astore.php @@ -0,0 +1,26 @@ +operands !== null) { + return $this->operands; + } + $index = $this->readUnsignedByte(); + + return $this->operands = new Operands( + ['index', $index, ['index']] + ); + } + + public function execute(): void + { + parent::execute(); + $index = $this->getOperands()['index']; + $this->setLocalStorage($index, $this->popFromOperandStack()); + } +} diff --git a/src/Kernel/Mnemonics/_astore_0.php b/src/Kernel/Mnemonics/_astore_0.php new file mode 100644 index 00000000..fae11214 --- /dev/null +++ b/src/Kernel/Mnemonics/_astore_0.php @@ -0,0 +1,21 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->setLocalStorage(0, $this->popFromOperandStack()); + } +} diff --git a/src/Kernel/Mnemonics/_astore_1.php b/src/Kernel/Mnemonics/_astore_1.php new file mode 100644 index 00000000..040990f0 --- /dev/null +++ b/src/Kernel/Mnemonics/_astore_1.php @@ -0,0 +1,21 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->setLocalStorage(1, $this->popFromOperandStack()); + } +} diff --git a/src/Kernel/Mnemonics/_astore_2.php b/src/Kernel/Mnemonics/_astore_2.php new file mode 100644 index 00000000..e212650b --- /dev/null +++ b/src/Kernel/Mnemonics/_astore_2.php @@ -0,0 +1,21 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->setLocalStorage(2, $this->popFromOperandStack()); + } +} diff --git a/src/Kernel/Mnemonics/_astore_3.php b/src/Kernel/Mnemonics/_astore_3.php new file mode 100644 index 00000000..098db742 --- /dev/null +++ b/src/Kernel/Mnemonics/_astore_3.php @@ -0,0 +1,21 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->setLocalStorage(3, $this->popFromOperandStack()); + } +} diff --git a/src/Kernel/Mnemonics/_athrow.php b/src/Kernel/Mnemonics/_athrow.php new file mode 100644 index 00000000..f6d8de1d --- /dev/null +++ b/src/Kernel/Mnemonics/_athrow.php @@ -0,0 +1,44 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + /** + * @throws UncaughtException + */ + public function execute(): void + { + parent::execute(); + $cpInfo = $this->getConstantPool(); + + /** + * @var JavaClass $objectref + */ + $objectref = $this->popFromOperandStack(); + + try { + $this->inspectExceptionTable($objectref->getInvoker()->getClassObject()); + } catch (\Exception $e) { + throw new UncaughtException( + "Unable to catch {$objectref->getClassName()} exception. " . + 'PHPJava has stopped operations. ' . + 'You may be running broken Java class. ' + ); + } + } +} diff --git a/src/Kernel/Mnemonics/_baload.php b/src/Kernel/Mnemonics/_baload.php new file mode 100644 index 00000000..c2a71206 --- /dev/null +++ b/src/Kernel/Mnemonics/_baload.php @@ -0,0 +1,30 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $index = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $arrayref = $this->popFromOperandStack(); + + $this->pushToOperandStack( + $arrayref[$index] + ); + } +} diff --git a/src/Kernel/Mnemonics/_bastore.php b/src/Kernel/Mnemonics/_bastore.php new file mode 100644 index 00000000..806cfe74 --- /dev/null +++ b/src/Kernel/Mnemonics/_bastore.php @@ -0,0 +1,48 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value = $this->popFromOperandStack(); + $index = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + /** + * @var Collection $arrayref + */ + $arrayref = $this->popFromOperandStack(); + if ($arrayref->getType() === Boolean_::class) { + $value = Boolean_::get( + Normalizer::getPrimitiveValue( + $value + ) + ); + } else { + $value = Byte_::get( + Normalizer::getPrimitiveValue( + $value + ) + ); + } + + // The value is a ref. + $arrayref[$index] = $value; + } +} diff --git a/src/Kernel/Mnemonics/_bipush.php b/src/Kernel/Mnemonics/_bipush.php new file mode 100644 index 00000000..2931c05c --- /dev/null +++ b/src/Kernel/Mnemonics/_bipush.php @@ -0,0 +1,29 @@ +operands !== null) { + return $this->operands; + } + $byte = $this->readByte(); + + return $this->operands = new Operands( + ['byte', $byte, ['byte']] + ); + } + + public function execute(): void + { + parent::execute(); + $this->pushToOperandStack(Int_::get($this->getOperands()['byte'])); + } +} diff --git a/src/Kernel/Mnemonics/_caload.php b/src/Kernel/Mnemonics/_caload.php new file mode 100644 index 00000000..bab89c58 --- /dev/null +++ b/src/Kernel/Mnemonics/_caload.php @@ -0,0 +1,30 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $index = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $arrayref = $this->popFromOperandStack(); + + $this->pushToOperandStack( + $arrayref[$index] + ); + } +} diff --git a/src/Kernel/Mnemonics/_castore.php b/src/Kernel/Mnemonics/_castore.php new file mode 100644 index 00000000..628fd27b --- /dev/null +++ b/src/Kernel/Mnemonics/_castore.php @@ -0,0 +1,34 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $index = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + /** + * @var Type $arrayref + */ + $arrayref = $this->popFromOperandStack(); + + // The value is a ref. + $arrayref[$index] = Char_::get($value); + } +} diff --git a/src/Kernel/Mnemonics/_checkcast.php b/src/Kernel/Mnemonics/_checkcast.php new file mode 100644 index 00000000..3a2f3ca5 --- /dev/null +++ b/src/Kernel/Mnemonics/_checkcast.php @@ -0,0 +1,54 @@ +operands !== null) { + return $this->operands; + } + $indexbyte = $this->readUnsignedShort(); + + return $this->operands = new Operands( + ['indexbyte', $indexbyte, ['indexbyte1', 'indexbyte2']] + ); + } + + /** + * @throws ClassCastException + */ + public function execute(): void + { + parent::execute(); + $cp = $this->getConstantPool(); + $index = $this->getOperands()['indexbyte']; + $objectref = $this->popFromOperandStack(); + + if ($objectref === null) { + return; + } + + $castTo = $cp[$cp[$index]->getClassIndex()]->getString(); + + $fromObjectClass = Formatter::convertPHPNamespacesToJava(get_class($objectref)); + + [$classes, $interfaces] = TypeResolver::getExtendedClasses( + 'L' . str_replace('/', '.', $castTo) + )[0] ?? [[], []]; + + if (in_array($fromObjectClass, $classes, true)) { + return; + } + + throw new ClassCastException( + 'class \\' . get_class($objectref) . ' cannot be cast to class ' . Formatter::convertJavaNamespaceToPHP($castTo)[1] + ); + } +} diff --git a/src/Kernel/Mnemonics/_d2f.php b/src/Kernel/Mnemonics/_d2f.php new file mode 100644 index 00000000..4914a48d --- /dev/null +++ b/src/Kernel/Mnemonics/_d2f.php @@ -0,0 +1,30 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value = Normalizer::getPrimitiveValue( + $this->popFromOperandStack() + ); + + $this->pushToOperandStack(Float_::get($value)); + } +} diff --git a/src/Kernel/Mnemonics/_d2i.php b/src/Kernel/Mnemonics/_d2i.php new file mode 100644 index 00000000..9b216946 --- /dev/null +++ b/src/Kernel/Mnemonics/_d2i.php @@ -0,0 +1,30 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value = Normalizer::getPrimitiveValue( + $this->popFromOperandStack() + ); + + $this->pushToOperandStack(Int_::get($value)); + } +} diff --git a/src/Kernel/Mnemonics/_d2l.php b/src/Kernel/Mnemonics/_d2l.php new file mode 100644 index 00000000..aa2ff83b --- /dev/null +++ b/src/Kernel/Mnemonics/_d2l.php @@ -0,0 +1,30 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value = Normalizer::getPrimitiveValue( + $this->popFromOperandStack() + ); + + $this->pushToOperandStack(Long_::get($value)); + } +} diff --git a/src/Kernel/Mnemonics/_dadd.php b/src/Kernel/Mnemonics/_dadd.php new file mode 100644 index 00000000..20b32226 --- /dev/null +++ b/src/Kernel/Mnemonics/_dadd.php @@ -0,0 +1,33 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value2 = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $value1 = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + $result = (string) BigDecimal::of($value1) + ->plus(BigDecimal::of($value2)); + + $this->pushToOperandStack(Double_::get($result)); + } +} diff --git a/src/Kernel/Mnemonics/_daload.php b/src/Kernel/Mnemonics/_daload.php new file mode 100644 index 00000000..28eb2a4d --- /dev/null +++ b/src/Kernel/Mnemonics/_daload.php @@ -0,0 +1,33 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + /** + * load a double from an array. + */ + public function execute(): void + { + parent::execute(); + $index = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $arrayref = $this->popFromOperandStack(); + + $this->pushToOperandStack( + $arrayref[$index] + ); + } +} diff --git a/src/Kernel/Mnemonics/_dastore.php b/src/Kernel/Mnemonics/_dastore.php new file mode 100644 index 00000000..fa8cd9ff --- /dev/null +++ b/src/Kernel/Mnemonics/_dastore.php @@ -0,0 +1,37 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + /** + * store a double into an array. + */ + public function execute(): void + { + parent::execute(); + $value = $this->popFromOperandStack(); + $index = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + /** + * @var Type $arrayref + */ + $arrayref = $this->popFromOperandStack(); + + // The value is a ref. + $arrayref[$index] = Double_::get($value); + } +} diff --git a/src/Kernel/Mnemonics/_dcmpg.php b/src/Kernel/Mnemonics/_dcmpg.php new file mode 100644 index 00000000..ef0934f7 --- /dev/null +++ b/src/Kernel/Mnemonics/_dcmpg.php @@ -0,0 +1,45 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $rightOperand = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $leftOperand = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + if ($leftOperand > $rightOperand) { + $this->pushToOperandStack( + Int_::get(1) + ); + return; + } + + if ($leftOperand < $rightOperand) { + $this->pushToOperandStack( + Int_::get(-1) + ); + return; + } + + $this->pushToOperandStack( + Int_::get(0) + ); + } +} diff --git a/src/Kernel/Mnemonics/_dcmpl.php b/src/Kernel/Mnemonics/_dcmpl.php new file mode 100644 index 00000000..e093f4c3 --- /dev/null +++ b/src/Kernel/Mnemonics/_dcmpl.php @@ -0,0 +1,45 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $rightOperand = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $leftOperand = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + if ($leftOperand > $rightOperand) { + $this->pushToOperandStack( + Int_::get(1) + ); + return; + } + + if ($leftOperand < $rightOperand) { + $this->pushToOperandStack( + Int_::get(-1) + ); + return; + } + + $this->pushToOperandStack( + Int_::get(0) + ); + } +} diff --git a/src/Kernel/Mnemonics/_dconst_0.php b/src/Kernel/Mnemonics/_dconst_0.php new file mode 100644 index 00000000..18ef21a9 --- /dev/null +++ b/src/Kernel/Mnemonics/_dconst_0.php @@ -0,0 +1,27 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->pushToOperandStack( + Double_::get(0) + ); + } +} diff --git a/src/Kernel/Mnemonics/_dconst_1.php b/src/Kernel/Mnemonics/_dconst_1.php new file mode 100644 index 00000000..da8864ba --- /dev/null +++ b/src/Kernel/Mnemonics/_dconst_1.php @@ -0,0 +1,27 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->pushToOperandStack( + Double_::get(1) + ); + } +} diff --git a/src/Kernel/Mnemonics/_ddiv.php b/src/Kernel/Mnemonics/_ddiv.php new file mode 100644 index 00000000..b2a6eb4c --- /dev/null +++ b/src/Kernel/Mnemonics/_ddiv.php @@ -0,0 +1,33 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value2 = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $value1 = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + $result = (string) BigDecimal::of($value1) + ->minus(BigDecimal::of($value2)); + + $this->pushToOperandStack(Double_::get($result)); + } +} diff --git a/src/Kernel/Mnemonics/_dload.php b/src/Kernel/Mnemonics/_dload.php new file mode 100644 index 00000000..9c49dfba --- /dev/null +++ b/src/Kernel/Mnemonics/_dload.php @@ -0,0 +1,37 @@ +operands !== null) { + return $this->operands; + } + $index = $this->readUnsignedByte(); + + return $this->operands = new Operands( + ['index', $index, ['index']] + ); + } + + /** + * load a double value from a local variable #index. + */ + public function execute(): void + { + parent::execute(); + $index = $this->getOperands()['index']; + $this->pushToOperandStack( + Double_::get( + $this->getLocalStorage($index) + ) + ); + } +} diff --git a/src/Kernel/Mnemonics/_dload_0.php b/src/Kernel/Mnemonics/_dload_0.php new file mode 100644 index 00000000..dfe7727c --- /dev/null +++ b/src/Kernel/Mnemonics/_dload_0.php @@ -0,0 +1,29 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->pushToOperandStack( + Double_::get( + $this->getLocalStorage(0) + ) + ); + } +} diff --git a/src/Kernel/Mnemonics/_dload_1.php b/src/Kernel/Mnemonics/_dload_1.php new file mode 100644 index 00000000..f291a0ed --- /dev/null +++ b/src/Kernel/Mnemonics/_dload_1.php @@ -0,0 +1,29 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->pushToOperandStack( + Double_::get( + $this->getLocalStorage(1) + ) + ); + } +} diff --git a/src/Kernel/Mnemonics/_dload_2.php b/src/Kernel/Mnemonics/_dload_2.php new file mode 100644 index 00000000..9f2c131a --- /dev/null +++ b/src/Kernel/Mnemonics/_dload_2.php @@ -0,0 +1,29 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->pushToOperandStack( + Double_::get( + $this->getLocalStorage(2) + ) + ); + } +} diff --git a/src/Kernel/Mnemonics/_dload_3.php b/src/Kernel/Mnemonics/_dload_3.php new file mode 100644 index 00000000..fd198abe --- /dev/null +++ b/src/Kernel/Mnemonics/_dload_3.php @@ -0,0 +1,29 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->pushToOperandStack( + Double_::get( + $this->getLocalStorage(3) + ) + ); + } +} diff --git a/src/Kernel/Mnemonics/_dmul.php b/src/Kernel/Mnemonics/_dmul.php new file mode 100644 index 00000000..eaa4d142 --- /dev/null +++ b/src/Kernel/Mnemonics/_dmul.php @@ -0,0 +1,33 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value2 = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $value1 = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + $result = (string) BigDecimal::of($value1) + ->multipliedBy(BigDecimal::of($value2)); + + $this->pushToOperandStack(Double_::get($result)); + } +} diff --git a/src/Kernel/Mnemonics/_dneg.php b/src/Kernel/Mnemonics/_dneg.php new file mode 100644 index 00000000..c72a0aea --- /dev/null +++ b/src/Kernel/Mnemonics/_dneg.php @@ -0,0 +1,34 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value = Normalizer::getPrimitiveValue( + $this->popFromOperandStack() + ); + + $result = (string) BigDecimal::of($value) + ->multipliedBy(BigDecimal::of(-1)); + + $this->pushToOperandStack(Double_::get($result)); + } +} diff --git a/src/Kernel/Mnemonics/_drem.php b/src/Kernel/Mnemonics/_drem.php new file mode 100644 index 00000000..2819dec3 --- /dev/null +++ b/src/Kernel/Mnemonics/_drem.php @@ -0,0 +1,33 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $rightOperand = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $leftOperand = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + $this->pushToOperandStack( + Double_::get( + $leftOperand % $rightOperand + ) + ); + } +} diff --git a/src/Kernel/Mnemonics/_dreturn.php b/src/Kernel/Mnemonics/_dreturn.php new file mode 100644 index 00000000..330d86f3 --- /dev/null +++ b/src/Kernel/Mnemonics/_dreturn.php @@ -0,0 +1,26 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value = $this->popFromOperandStack(); + $this->returnValue = ($value instanceof Double_) + ? $value + : Double_::get($value); + } +} diff --git a/src/Kernel/Mnemonics/_dstore.php b/src/Kernel/Mnemonics/_dstore.php new file mode 100644 index 00000000..69d67c32 --- /dev/null +++ b/src/Kernel/Mnemonics/_dstore.php @@ -0,0 +1,36 @@ +operands !== null) { + return $this->operands; + } + $index = $this->readUnsignedByte(); + + return $this->operands = new Operands( + ['index', $index, ['index']] + ); + } + + /** + * store a double value into a local variable #index. + */ + public function execute(): void + { + parent::execute(); + $index = $this->getOperands()['index']; + $value = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + $this->setLocalStorage( + $index, + $value + ); + } +} diff --git a/src/Kernel/Mnemonics/_dstore_0.php b/src/Kernel/Mnemonics/_dstore_0.php new file mode 100644 index 00000000..ad8187de --- /dev/null +++ b/src/Kernel/Mnemonics/_dstore_0.php @@ -0,0 +1,21 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->setLocalStorage(0, $this->popFromOperandStack()); + } +} diff --git a/src/Kernel/Mnemonics/_dstore_1.php b/src/Kernel/Mnemonics/_dstore_1.php new file mode 100644 index 00000000..205af1a8 --- /dev/null +++ b/src/Kernel/Mnemonics/_dstore_1.php @@ -0,0 +1,21 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->setLocalStorage(1, $this->popFromOperandStack()); + } +} diff --git a/src/Kernel/Mnemonics/_dstore_2.php b/src/Kernel/Mnemonics/_dstore_2.php new file mode 100644 index 00000000..2991588c --- /dev/null +++ b/src/Kernel/Mnemonics/_dstore_2.php @@ -0,0 +1,21 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->setLocalStorage(2, $this->popFromOperandStack()); + } +} diff --git a/src/Kernel/Mnemonics/_dstore_3.php b/src/Kernel/Mnemonics/_dstore_3.php new file mode 100644 index 00000000..44e559ad --- /dev/null +++ b/src/Kernel/Mnemonics/_dstore_3.php @@ -0,0 +1,21 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->setLocalStorage(3, $this->popFromOperandStack()); + } +} diff --git a/src/Kernel/Mnemonics/_dsub.php b/src/Kernel/Mnemonics/_dsub.php new file mode 100644 index 00000000..4f2f8217 --- /dev/null +++ b/src/Kernel/Mnemonics/_dsub.php @@ -0,0 +1,33 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value2 = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $value1 = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + $result = (string) BigDecimal::of($value1) + ->minus(BigDecimal::of($value2)); + + $this->pushToOperandStack(Double_::get($result)); + } +} diff --git a/src/Kernel/Mnemonics/_dup.php b/src/Kernel/Mnemonics/_dup.php new file mode 100644 index 00000000..007d9ba9 --- /dev/null +++ b/src/Kernel/Mnemonics/_dup.php @@ -0,0 +1,24 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $dup = $this->stacks[$this->getCurrentStackIndex()]; + $this->pushToOperandStackByReference($dup); + } +} diff --git a/src/Kernel/Mnemonics/_dup2.php b/src/Kernel/Mnemonics/_dup2.php new file mode 100644 index 00000000..70ed7f63 --- /dev/null +++ b/src/Kernel/Mnemonics/_dup2.php @@ -0,0 +1,23 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + throw new NotImplementedException(__CLASS__); + } +} diff --git a/src/Kernel/Mnemonics/_dup2_x1.php b/src/Kernel/Mnemonics/_dup2_x1.php new file mode 100644 index 00000000..d9d06fd3 --- /dev/null +++ b/src/Kernel/Mnemonics/_dup2_x1.php @@ -0,0 +1,23 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + throw new NotImplementedException(__CLASS__); + } +} diff --git a/src/Kernel/Mnemonics/_dup2_x2.php b/src/Kernel/Mnemonics/_dup2_x2.php new file mode 100644 index 00000000..e940c17f --- /dev/null +++ b/src/Kernel/Mnemonics/_dup2_x2.php @@ -0,0 +1,23 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + throw new NotImplementedException(__CLASS__); + } +} diff --git a/src/Kernel/Mnemonics/_dup_x1.php b/src/Kernel/Mnemonics/_dup_x1.php new file mode 100644 index 00000000..836ae3f9 --- /dev/null +++ b/src/Kernel/Mnemonics/_dup_x1.php @@ -0,0 +1,23 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + throw new NotImplementedException(__CLASS__); + } +} diff --git a/src/Kernel/Mnemonics/_dup_x2.php b/src/Kernel/Mnemonics/_dup_x2.php new file mode 100644 index 00000000..92c2a529 --- /dev/null +++ b/src/Kernel/Mnemonics/_dup_x2.php @@ -0,0 +1,23 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + throw new NotImplementedException(__CLASS__); + } +} diff --git a/src/Kernel/Mnemonics/_f2d.php b/src/Kernel/Mnemonics/_f2d.php new file mode 100644 index 00000000..90037ccb --- /dev/null +++ b/src/Kernel/Mnemonics/_f2d.php @@ -0,0 +1,30 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value = Normalizer::getPrimitiveValue( + $this->popFromOperandStack() + ); + + $this->pushToOperandStack(Double_::get($value)); + } +} diff --git a/src/Kernel/Mnemonics/_f2i.php b/src/Kernel/Mnemonics/_f2i.php new file mode 100644 index 00000000..54a9b86d --- /dev/null +++ b/src/Kernel/Mnemonics/_f2i.php @@ -0,0 +1,30 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value = Normalizer::getPrimitiveValue( + $this->popFromOperandStack() + ); + + $this->pushToOperandStack(Int_::get($value)); + } +} diff --git a/src/Kernel/Mnemonics/_f2l.php b/src/Kernel/Mnemonics/_f2l.php new file mode 100644 index 00000000..081e408c --- /dev/null +++ b/src/Kernel/Mnemonics/_f2l.php @@ -0,0 +1,30 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value = Normalizer::getPrimitiveValue( + $this->popFromOperandStack() + ); + + $this->pushToOperandStack(Long_::get($value)); + } +} diff --git a/src/Kernel/Mnemonics/_fadd.php b/src/Kernel/Mnemonics/_fadd.php new file mode 100644 index 00000000..d7a997a2 --- /dev/null +++ b/src/Kernel/Mnemonics/_fadd.php @@ -0,0 +1,29 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value2 = (float) Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $value1 = (float) Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + $this->pushToOperandStack(Float_::get($value1 + $value2)); + } +} diff --git a/src/Kernel/Mnemonics/_faload.php b/src/Kernel/Mnemonics/_faload.php new file mode 100644 index 00000000..9ce86f4e --- /dev/null +++ b/src/Kernel/Mnemonics/_faload.php @@ -0,0 +1,30 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $index = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $arrayref = $this->popFromOperandStack(); + + $this->pushToOperandStack( + $arrayref[$index] + ); + } +} diff --git a/src/Kernel/Mnemonics/_fastore.php b/src/Kernel/Mnemonics/_fastore.php new file mode 100644 index 00000000..cc9d1014 --- /dev/null +++ b/src/Kernel/Mnemonics/_fastore.php @@ -0,0 +1,34 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value = $this->popFromOperandStack(); + $index = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + /** + * @var Type $arrayref + */ + $arrayref = $this->popFromOperandStack(); + + // The value is a ref. + $arrayref[$index] = Float_::get($value); + } +} diff --git a/src/Kernel/Mnemonics/_fcmpg.php b/src/Kernel/Mnemonics/_fcmpg.php new file mode 100644 index 00000000..c6f87783 --- /dev/null +++ b/src/Kernel/Mnemonics/_fcmpg.php @@ -0,0 +1,45 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $rightOperand = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $leftOperand = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + if ($leftOperand > $rightOperand) { + $this->pushToOperandStack( + Int_::get(1) + ); + return; + } + + if ($leftOperand < $rightOperand) { + $this->pushToOperandStack( + Int_::get(-1) + ); + return; + } + + $this->pushToOperandStack( + Int_::get(0) + ); + } +} diff --git a/src/Kernel/Mnemonics/_fcmpl.php b/src/Kernel/Mnemonics/_fcmpl.php new file mode 100644 index 00000000..1d13ddb1 --- /dev/null +++ b/src/Kernel/Mnemonics/_fcmpl.php @@ -0,0 +1,45 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $rightOperand = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $leftOperand = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + if ($leftOperand > $rightOperand) { + $this->pushToOperandStack( + Int_::get(1) + ); + return; + } + + if ($leftOperand < $rightOperand) { + $this->pushToOperandStack( + Int_::get(-1) + ); + return; + } + + $this->pushToOperandStack( + Int_::get(0) + ); + } +} diff --git a/src/Kernel/Mnemonics/_fconst_0.php b/src/Kernel/Mnemonics/_fconst_0.php new file mode 100644 index 00000000..c89a0da4 --- /dev/null +++ b/src/Kernel/Mnemonics/_fconst_0.php @@ -0,0 +1,25 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->pushToOperandStack(Float_::get(0)); + } +} diff --git a/src/Kernel/Mnemonics/_fconst_1.php b/src/Kernel/Mnemonics/_fconst_1.php new file mode 100644 index 00000000..86c772b2 --- /dev/null +++ b/src/Kernel/Mnemonics/_fconst_1.php @@ -0,0 +1,25 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->pushToOperandStack(Float_::get(1)); + } +} diff --git a/src/Kernel/Mnemonics/_fconst_2.php b/src/Kernel/Mnemonics/_fconst_2.php new file mode 100644 index 00000000..976af742 --- /dev/null +++ b/src/Kernel/Mnemonics/_fconst_2.php @@ -0,0 +1,25 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->pushToOperandStack(Float_::get(2)); + } +} diff --git a/src/Kernel/Mnemonics/_fdiv.php b/src/Kernel/Mnemonics/_fdiv.php new file mode 100644 index 00000000..4e145a0f --- /dev/null +++ b/src/Kernel/Mnemonics/_fdiv.php @@ -0,0 +1,29 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value2 = (float) Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $value1 = (float) Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + $this->pushToOperandStack(Float_::get($value1 / $value2)); + } +} diff --git a/src/Kernel/Mnemonics/_fload.php b/src/Kernel/Mnemonics/_fload.php new file mode 100644 index 00000000..86c97b02 --- /dev/null +++ b/src/Kernel/Mnemonics/_fload.php @@ -0,0 +1,34 @@ +operands !== null) { + return $this->operands; + } + $index = $this->readUnsignedByte(); + + return $this->operands = new Operands( + ['index', $index, ['index']] + ); + } + + public function execute(): void + { + parent::execute(); + $index = $this->getOperands()['index']; + $this->pushToOperandStack( + Float_::get( + $this->getLocalStorage($index) + ) + ); + } +} diff --git a/src/Kernel/Mnemonics/_fload_0.php b/src/Kernel/Mnemonics/_fload_0.php new file mode 100644 index 00000000..0b0bfa89 --- /dev/null +++ b/src/Kernel/Mnemonics/_fload_0.php @@ -0,0 +1,29 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->pushToOperandStack( + Float_::get( + $this->getLocalStorage(0) + ) + ); + } +} diff --git a/src/Kernel/Mnemonics/_fload_1.php b/src/Kernel/Mnemonics/_fload_1.php new file mode 100644 index 00000000..8651486f --- /dev/null +++ b/src/Kernel/Mnemonics/_fload_1.php @@ -0,0 +1,29 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->pushToOperandStack( + Float_::get( + $this->getLocalStorage(1) + ) + ); + } +} diff --git a/src/Kernel/Mnemonics/_fload_2.php b/src/Kernel/Mnemonics/_fload_2.php new file mode 100644 index 00000000..a1d5f73d --- /dev/null +++ b/src/Kernel/Mnemonics/_fload_2.php @@ -0,0 +1,29 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->pushToOperandStack( + Float_::get( + $this->getLocalStorage(2) + ) + ); + } +} diff --git a/src/Kernel/Mnemonics/_fload_3.php b/src/Kernel/Mnemonics/_fload_3.php new file mode 100644 index 00000000..6dd38714 --- /dev/null +++ b/src/Kernel/Mnemonics/_fload_3.php @@ -0,0 +1,29 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->pushToOperandStack( + Float_::get( + $this->getLocalStorage(3) + ) + ); + } +} diff --git a/src/Kernel/Mnemonics/_fmul.php b/src/Kernel/Mnemonics/_fmul.php new file mode 100644 index 00000000..eb822e2c --- /dev/null +++ b/src/Kernel/Mnemonics/_fmul.php @@ -0,0 +1,29 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value2 = (float) Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $value1 = (float) Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + $this->pushToOperandStack(Float_::get($value1 * $value2)); + } +} diff --git a/src/Kernel/Mnemonics/_fneg.php b/src/Kernel/Mnemonics/_fneg.php new file mode 100644 index 00000000..548447da --- /dev/null +++ b/src/Kernel/Mnemonics/_fneg.php @@ -0,0 +1,30 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value = Normalizer::getPrimitiveValue( + $this->popFromOperandStack() + ); + + $this->pushToOperandStack(Float_::get($value * (float) -1)); + } +} diff --git a/src/Kernel/Mnemonics/_frem.php b/src/Kernel/Mnemonics/_frem.php new file mode 100644 index 00000000..4431e920 --- /dev/null +++ b/src/Kernel/Mnemonics/_frem.php @@ -0,0 +1,33 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $rightOperand = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $leftOperand = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + $this->pushToOperandStack( + Float_::get( + $leftOperand % $rightOperand + ) + ); + } +} diff --git a/src/Kernel/Mnemonics/_freturn.php b/src/Kernel/Mnemonics/_freturn.php new file mode 100644 index 00000000..db1edb41 --- /dev/null +++ b/src/Kernel/Mnemonics/_freturn.php @@ -0,0 +1,26 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value = $this->popFromOperandStack(); + $this->returnValue = ($value instanceof Float_) + ? $value + : Float_::get($value); + } +} diff --git a/src/Kernel/Mnemonics/_fstore.php b/src/Kernel/Mnemonics/_fstore.php new file mode 100644 index 00000000..22e3f70a --- /dev/null +++ b/src/Kernel/Mnemonics/_fstore.php @@ -0,0 +1,26 @@ +operands !== null) { + return $this->operands; + } + $index = $this->readUnsignedByte(); + + return $this->operands = new Operands( + ['index', $index, ['index']] + ); + } + + public function execute(): void + { + parent::execute(); + $index = $this->getOperands()['index']; + $this->setLocalStorage($index, $this->popFromOperandStack()); + } +} diff --git a/src/Kernel/Mnemonics/_fstore_0.php b/src/Kernel/Mnemonics/_fstore_0.php new file mode 100644 index 00000000..f0e1f2ff --- /dev/null +++ b/src/Kernel/Mnemonics/_fstore_0.php @@ -0,0 +1,21 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->setLocalStorage(0, $this->popFromOperandStack()); + } +} diff --git a/src/Kernel/Mnemonics/_fstore_1.php b/src/Kernel/Mnemonics/_fstore_1.php new file mode 100644 index 00000000..5fa6f4db --- /dev/null +++ b/src/Kernel/Mnemonics/_fstore_1.php @@ -0,0 +1,21 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->setLocalStorage(1, $this->popFromOperandStack()); + } +} diff --git a/src/Kernel/Mnemonics/_fstore_2.php b/src/Kernel/Mnemonics/_fstore_2.php new file mode 100644 index 00000000..92ca40ee --- /dev/null +++ b/src/Kernel/Mnemonics/_fstore_2.php @@ -0,0 +1,21 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->setLocalStorage(2, $this->popFromOperandStack()); + } +} diff --git a/src/Kernel/Mnemonics/_fstore_3.php b/src/Kernel/Mnemonics/_fstore_3.php new file mode 100644 index 00000000..74afb3ff --- /dev/null +++ b/src/Kernel/Mnemonics/_fstore_3.php @@ -0,0 +1,21 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->setLocalStorage(3, $this->popFromOperandStack()); + } +} diff --git a/src/Kernel/Mnemonics/_fsub.php b/src/Kernel/Mnemonics/_fsub.php new file mode 100644 index 00000000..2ff599da --- /dev/null +++ b/src/Kernel/Mnemonics/_fsub.php @@ -0,0 +1,29 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value2 = (float) Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $value1 = (float) Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + $this->pushToOperandStack(Float_::get($value1 - $value2)); + } +} diff --git a/src/Kernel/Mnemonics/_getfield.php b/src/Kernel/Mnemonics/_getfield.php new file mode 100644 index 00000000..36730d43 --- /dev/null +++ b/src/Kernel/Mnemonics/_getfield.php @@ -0,0 +1,48 @@ +operands !== null) { + return $this->operands; + } + $indexbyte = $this->readUnsignedShort(); + + return $this->operands = new Operands( + ['indexbyte', $indexbyte, ['indexbyte1', 'indexbyte2']] + ); + } + + public function execute(): void + { + parent::execute(); + $cpInfo = $this->getConstantPool(); + $cp = $cpInfo[$this->getOperands()['indexbyte']]; + $class = $cpInfo[$cp->getNameAndTypeIndex()]; + + $name = $cpInfo[$class->getNameIndex()]->getString(); + + /** + * @var JavaClassInterface $objectref + */ + $objectref = $this->popFromOperandStack(); + + $return = $objectref->getInvoker()->getDynamic()->getFields()->get($name); + + if ($return !== null) { + $this->pushToOperandStack($return); + return; + } + + throw new NoSuchFieldException('Tried to get undefined field ' . $name); + } +} diff --git a/src/Kernel/Mnemonics/_getstatic.php b/src/Kernel/Mnemonics/_getstatic.php new file mode 100644 index 00000000..e76b791a --- /dev/null +++ b/src/Kernel/Mnemonics/_getstatic.php @@ -0,0 +1,54 @@ +operands !== null) { + return $this->operands; + } + + $indexbyte = $this->readUnsignedShort(); + + return $this->operands = new Operands( + ['indexbyte', $indexbyte, ['indexbyte1', 'indexbyte2']] + ); + } + + public function execute(): void + { + parent::execute(); + $cpInfo = $this->getConstantPool(); + + $cp = $cpInfo[$this->getOperands()['indexbyte']]; + $class = $cpInfo[$cpInfo[$cp->getClassIndex()]->getClassIndex()]->getString(); + $signature = Formatter::parseSignature($cpInfo[$cpInfo[$cp->getNameAndTypeIndex()]->getDescriptorIndex()]->getString()); + $fieldName = $cpInfo[$cpInfo[$cp->getNameAndTypeIndex()]->getNameIndex()]->getString(); + + $classObject = JavaClass::load( + $class, + $this->javaClass->getOptions(), + false + ); + + /** + * @var JavaClassInterface $className + */ + $this->pushToOperandStack( + $classObject + ->getInvoker() + ->getStatic() + ->getFields() + ->get($fieldName) + ); + } +} diff --git a/src/Kernel/Mnemonics/_goto.php b/src/Kernel/Mnemonics/_goto.php new file mode 100644 index 00000000..cf36ad79 --- /dev/null +++ b/src/Kernel/Mnemonics/_goto.php @@ -0,0 +1,27 @@ +operands !== null) { + return $this->operands; + } + $branchbyte = $this->readShort(); + + return $this->operands = new Operands( + ['branchbyte', $branchbyte, ['branchbyte1', 'branchbyte2']] + ); + } + + public function execute(): void + { + parent::execute(); + $offset = $this->getOperands()['branchbyte']; + + $this->setOffset($this->getProgramCounter() + $offset); + } +} diff --git a/src/Kernel/Mnemonics/_goto_w.php b/src/Kernel/Mnemonics/_goto_w.php new file mode 100644 index 00000000..4a5f20eb --- /dev/null +++ b/src/Kernel/Mnemonics/_goto_w.php @@ -0,0 +1,23 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + throw new NotImplementedException(__CLASS__); + } +} diff --git a/src/Kernel/Mnemonics/_i2b.php b/src/Kernel/Mnemonics/_i2b.php new file mode 100644 index 00000000..f4a091c8 --- /dev/null +++ b/src/Kernel/Mnemonics/_i2b.php @@ -0,0 +1,30 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value = Normalizer::getPrimitiveValue( + $this->popFromOperandStack() + ); + + $this->pushToOperandStack(Byte_::get($value)); + } +} diff --git a/src/Kernel/Mnemonics/_i2c.php b/src/Kernel/Mnemonics/_i2c.php new file mode 100644 index 00000000..5abf5e2a --- /dev/null +++ b/src/Kernel/Mnemonics/_i2c.php @@ -0,0 +1,30 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value = Normalizer::getPrimitiveValue( + $this->popFromOperandStack() + ); + + $this->pushToOperandStack(Char_::get($value)); + } +} diff --git a/src/Kernel/Mnemonics/_i2d.php b/src/Kernel/Mnemonics/_i2d.php new file mode 100644 index 00000000..bdd26754 --- /dev/null +++ b/src/Kernel/Mnemonics/_i2d.php @@ -0,0 +1,30 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value = Normalizer::getPrimitiveValue( + $this->popFromOperandStack() + ); + + $this->pushToOperandStack(Double_::get($value)); + } +} diff --git a/src/Kernel/Mnemonics/_i2f.php b/src/Kernel/Mnemonics/_i2f.php new file mode 100644 index 00000000..97f95bc3 --- /dev/null +++ b/src/Kernel/Mnemonics/_i2f.php @@ -0,0 +1,30 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value = Normalizer::getPrimitiveValue( + $this->popFromOperandStack() + ); + + $this->pushToOperandStack(Float_::get($value)); + } +} diff --git a/src/Kernel/Mnemonics/_i2l.php b/src/Kernel/Mnemonics/_i2l.php new file mode 100644 index 00000000..2f986b17 --- /dev/null +++ b/src/Kernel/Mnemonics/_i2l.php @@ -0,0 +1,30 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value = Normalizer::getPrimitiveValue( + $this->popFromOperandStack() + ); + + $this->pushToOperandStack(Long_::get($value)); + } +} diff --git a/src/Kernel/Mnemonics/_i2s.php b/src/Kernel/Mnemonics/_i2s.php new file mode 100644 index 00000000..92ef6cf5 --- /dev/null +++ b/src/Kernel/Mnemonics/_i2s.php @@ -0,0 +1,30 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value = Normalizer::getPrimitiveValue( + $this->popFromOperandStack() + ); + + $this->pushToOperandStack(Short_::get($value)); + } +} diff --git a/src/Kernel/Mnemonics/_iadd.php b/src/Kernel/Mnemonics/_iadd.php new file mode 100644 index 00000000..62845930 --- /dev/null +++ b/src/Kernel/Mnemonics/_iadd.php @@ -0,0 +1,29 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value2 = (int) Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $value1 = (int) Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + $this->pushToOperandStack(Int_::get($value1 + $value2)); + } +} diff --git a/src/Kernel/Mnemonics/_iaload.php b/src/Kernel/Mnemonics/_iaload.php new file mode 100644 index 00000000..af203f7f --- /dev/null +++ b/src/Kernel/Mnemonics/_iaload.php @@ -0,0 +1,30 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $index = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $arrayref = $this->popFromOperandStack(); + + $this->pushToOperandStack( + $arrayref[$index] + ); + } +} diff --git a/src/Kernel/Mnemonics/_iand.php b/src/Kernel/Mnemonics/_iand.php new file mode 100644 index 00000000..6eb89270 --- /dev/null +++ b/src/Kernel/Mnemonics/_iand.php @@ -0,0 +1,29 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value2 = (int) Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $value1 = (int) Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + $this->pushToOperandStack(Int_::get($value1 & $value2)); + } +} diff --git a/src/Kernel/Mnemonics/_iastore.php b/src/Kernel/Mnemonics/_iastore.php new file mode 100644 index 00000000..323a0888 --- /dev/null +++ b/src/Kernel/Mnemonics/_iastore.php @@ -0,0 +1,34 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value = $this->popFromOperandStack(); + $index = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + /** + * @var Type $arrayref + */ + $arrayref = $this->popFromOperandStack(); + + // The value is a ref. + $arrayref[$index] = Int_::get($value); + } +} diff --git a/src/Kernel/Mnemonics/_iconst_0.php b/src/Kernel/Mnemonics/_iconst_0.php new file mode 100644 index 00000000..8f058f1e --- /dev/null +++ b/src/Kernel/Mnemonics/_iconst_0.php @@ -0,0 +1,25 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->pushToOperandStack(Int_::get(0)); + } +} diff --git a/src/Kernel/Mnemonics/_iconst_1.php b/src/Kernel/Mnemonics/_iconst_1.php new file mode 100644 index 00000000..db23774a --- /dev/null +++ b/src/Kernel/Mnemonics/_iconst_1.php @@ -0,0 +1,25 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->pushToOperandStack(Int_::get(1)); + } +} diff --git a/src/Kernel/Mnemonics/_iconst_2.php b/src/Kernel/Mnemonics/_iconst_2.php new file mode 100644 index 00000000..64437c24 --- /dev/null +++ b/src/Kernel/Mnemonics/_iconst_2.php @@ -0,0 +1,25 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->pushToOperandStack(Int_::get(2)); + } +} diff --git a/src/Kernel/Mnemonics/_iconst_3.php b/src/Kernel/Mnemonics/_iconst_3.php new file mode 100644 index 00000000..b385d82a --- /dev/null +++ b/src/Kernel/Mnemonics/_iconst_3.php @@ -0,0 +1,25 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->pushToOperandStack(Int_::get(3)); + } +} diff --git a/src/Kernel/Mnemonics/_iconst_4.php b/src/Kernel/Mnemonics/_iconst_4.php new file mode 100644 index 00000000..45bb0f72 --- /dev/null +++ b/src/Kernel/Mnemonics/_iconst_4.php @@ -0,0 +1,25 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->pushToOperandStack(Int_::get(4)); + } +} diff --git a/src/Kernel/Mnemonics/_iconst_5.php b/src/Kernel/Mnemonics/_iconst_5.php new file mode 100644 index 00000000..ee8a858c --- /dev/null +++ b/src/Kernel/Mnemonics/_iconst_5.php @@ -0,0 +1,25 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->pushToOperandStack(Int_::get(5)); + } +} diff --git a/src/Kernel/Mnemonics/_iconst_m1.php b/src/Kernel/Mnemonics/_iconst_m1.php new file mode 100644 index 00000000..de075f1a --- /dev/null +++ b/src/Kernel/Mnemonics/_iconst_m1.php @@ -0,0 +1,25 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->pushToOperandStack(Int_::get(-1)); + } +} diff --git a/src/Kernel/Mnemonics/_idiv.php b/src/Kernel/Mnemonics/_idiv.php new file mode 100644 index 00000000..0b54a6de --- /dev/null +++ b/src/Kernel/Mnemonics/_idiv.php @@ -0,0 +1,29 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value2 = (int) Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $value1 = (int) Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + $this->pushToOperandStack(Int_::get((int) ($value1 / $value2))); + } +} diff --git a/src/Kernel/Mnemonics/_if_acmpeq.php b/src/Kernel/Mnemonics/_if_acmpeq.php new file mode 100644 index 00000000..035c8780 --- /dev/null +++ b/src/Kernel/Mnemonics/_if_acmpeq.php @@ -0,0 +1,40 @@ +operands !== null) { + return $this->operands; + } + $branchbyte = $this->readShort(); + + return $this->operands = new Operands( + ['branchbyte', $branchbyte, ['branchbyte1', 'branchbyte2']] + ); + } + + public function execute(): void + { + parent::execute(); + $offset = $this->getOperands()['branchbyte']; + + /** + * @var JavaClass|StringInfo|Utf8Info $rightOperand + * @var JavaClass|StringInfo|Utf8Info $leftOperand + */ + $rightOperand = $this->popFromOperandStack(); + $leftOperand = $this->popFromOperandStack(); + + if ($leftOperand === $rightOperand) { + $this->setOffset($this->getProgramCounter() + $offset); + } + } +} diff --git a/src/Kernel/Mnemonics/_if_acmpne.php b/src/Kernel/Mnemonics/_if_acmpne.php new file mode 100644 index 00000000..25c3ee90 --- /dev/null +++ b/src/Kernel/Mnemonics/_if_acmpne.php @@ -0,0 +1,39 @@ +operands !== null) { + return $this->operands; + } + $branchbyte = $this->readShort(); + + return $this->operands = new Operands( + ['branchbyte', $branchbyte, ['branchbyte1', 'branchbyte2']] + ); + } + + public function execute(): void + { + parent::execute(); + $offset = $this->getOperands()['branchbyte']; + + /** + * @var _String|JavaClass|Utf8Info $rightOperand + * @var _String|JavaClass|Utf8Info $leftOperand + */ + $rightOperand = $this->popFromOperandStack(); + $leftOperand = $this->popFromOperandStack(); + + if ($leftOperand !== $rightOperand) { + $this->setOffset($this->getProgramCounter() + $offset); + } + } +} diff --git a/src/Kernel/Mnemonics/_if_icmpeq.php b/src/Kernel/Mnemonics/_if_icmpeq.php new file mode 100644 index 00000000..43a0b289 --- /dev/null +++ b/src/Kernel/Mnemonics/_if_icmpeq.php @@ -0,0 +1,34 @@ +operands !== null) { + return $this->operands; + } + $branchbyte = $this->readShort(); + + return $this->operands = new Operands( + ['branchbyte', $branchbyte, ['branchbyte1', 'branchbyte2']] + ); + } + + public function execute(): void + { + parent::execute(); + $offset = $this->getOperands()['branchbyte']; + + $rightOperand = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $leftOperand = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + if ($leftOperand == $rightOperand) { + $this->setOffset($this->getProgramCounter() + $offset); + } + } +} diff --git a/src/Kernel/Mnemonics/_if_icmpge.php b/src/Kernel/Mnemonics/_if_icmpge.php new file mode 100644 index 00000000..1e025a8b --- /dev/null +++ b/src/Kernel/Mnemonics/_if_icmpge.php @@ -0,0 +1,34 @@ +operands !== null) { + return $this->operands; + } + $branchbyte = $this->readShort(); + + return $this->operands = new Operands( + ['branchbyte', $branchbyte, ['branchbyte1', 'branchbyte2']] + ); + } + + public function execute(): void + { + parent::execute(); + $offset = $this->getOperands()['branchbyte']; + + $rightOperand = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $leftOperand = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + if ($leftOperand >= $rightOperand) { + $this->setOffset($this->getProgramCounter() + $offset); + } + } +} diff --git a/src/Kernel/Mnemonics/_if_icmpgt.php b/src/Kernel/Mnemonics/_if_icmpgt.php new file mode 100644 index 00000000..ff48e68a --- /dev/null +++ b/src/Kernel/Mnemonics/_if_icmpgt.php @@ -0,0 +1,34 @@ +operands !== null) { + return $this->operands; + } + $branchbyte = $this->readShort(); + + return $this->operands = new Operands( + ['branchbyte', $branchbyte, ['branchbyte1', 'branchbyte2']] + ); + } + + public function execute(): void + { + parent::execute(); + $offset = $this->getOperands()['branchbyte']; + + $rightOperand = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $leftOperand = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + if ($leftOperand > $rightOperand) { + $this->setOffset($this->getProgramCounter() + $offset); + } + } +} diff --git a/src/Kernel/Mnemonics/_if_icmple.php b/src/Kernel/Mnemonics/_if_icmple.php new file mode 100644 index 00000000..b3c26e4b --- /dev/null +++ b/src/Kernel/Mnemonics/_if_icmple.php @@ -0,0 +1,34 @@ +operands !== null) { + return $this->operands; + } + $branchbyte = $this->readShort(); + + return $this->operands = new Operands( + ['branchbyte', $branchbyte, ['branchbyte1', 'branchbyte2']] + ); + } + + public function execute(): void + { + parent::execute(); + $offset = $this->getOperands()['branchbyte']; + + $rightOperand = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $leftOperand = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + if ($leftOperand <= $rightOperand) { + $this->setOffset($this->getProgramCounter() + $offset); + } + } +} diff --git a/src/Kernel/Mnemonics/_if_icmplt.php b/src/Kernel/Mnemonics/_if_icmplt.php new file mode 100644 index 00000000..e43dcc0c --- /dev/null +++ b/src/Kernel/Mnemonics/_if_icmplt.php @@ -0,0 +1,32 @@ +operands !== null) { + return $this->operands; + } + $branchbyte = $this->readShort(); + + return $this->operands = new Operands( + ['branchbyte', $branchbyte, ['branchbyte1', 'branchbyte2']] + ); + } + + public function execute(): void + { + parent::execute(); + $offset = $this->getOperands()['branchbyte']; + + $rightOperand = $this->popFromOperandStack(); + $leftOperand = $this->popFromOperandStack(); + + if ($leftOperand < $rightOperand) { + $this->setOffset($this->getProgramCounter() + $offset); + } + } +} diff --git a/src/Kernel/Mnemonics/_if_icmpne.php b/src/Kernel/Mnemonics/_if_icmpne.php new file mode 100644 index 00000000..456c1732 --- /dev/null +++ b/src/Kernel/Mnemonics/_if_icmpne.php @@ -0,0 +1,32 @@ +operands !== null) { + return $this->operands; + } + $branchbyte = $this->readShort(); + + return $this->operands = new Operands( + ['branchbyte', $branchbyte, ['branchbyte1', 'branchbyte2']] + ); + } + + public function execute(): void + { + parent::execute(); + $offset = $this->getOperands()['branchbyte']; + + $rightOperand = $this->popFromOperandStack(); + $leftOperand = $this->popFromOperandStack(); + + if ($leftOperand != $rightOperand) { + $this->setOffset($this->getProgramCounter() + $offset); + } + } +} diff --git a/src/Kernel/Mnemonics/_ifeq.php b/src/Kernel/Mnemonics/_ifeq.php new file mode 100644 index 00000000..eb44b0be --- /dev/null +++ b/src/Kernel/Mnemonics/_ifeq.php @@ -0,0 +1,32 @@ +operands !== null) { + return $this->operands; + } + $branchbyte = $this->readShort(); + + return $this->operands = new Operands( + ['branchbyte', $branchbyte, ['branchbyte1', 'branchbyte2']] + ); + } + + public function execute(): void + { + parent::execute(); + $offset = $this->getOperands()['branchbyte']; + $operand = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + if ($operand == 0) { + $this->setOffset($this->getProgramCounter() + $offset); + } + } +} diff --git a/src/Kernel/Mnemonics/_ifge.php b/src/Kernel/Mnemonics/_ifge.php new file mode 100644 index 00000000..08575a03 --- /dev/null +++ b/src/Kernel/Mnemonics/_ifge.php @@ -0,0 +1,33 @@ +operands !== null) { + return $this->operands; + } + $branchbyte = $this->readShort(); + + return $this->operands = new Operands( + ['branchbyte', $branchbyte, ['branchbyte1', 'branchbyte2']] + ); + } + + public function execute(): void + { + parent::execute(); + $offset = $this->getOperands()['branchbyte']; + + $value = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + if ($value >= 0) { + $this->setOffset($this->getProgramCounter() + $offset); + } + } +} diff --git a/src/Kernel/Mnemonics/_ifgt.php b/src/Kernel/Mnemonics/_ifgt.php new file mode 100644 index 00000000..bf48b662 --- /dev/null +++ b/src/Kernel/Mnemonics/_ifgt.php @@ -0,0 +1,31 @@ +operands !== null) { + return $this->operands; + } + $branchbyte = $this->readShort(); + + return $this->operands = new Operands( + ['branchbyte', $branchbyte, ['branchbyte1', 'branchbyte2']] + ); + } + + public function execute(): void + { + parent::execute(); + $offset = $this->getOperands()['branchbyte']; + $value = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + if ($value > 0) { + $this->setOffset($this->getProgramCounter() + $offset); + } + } +} diff --git a/src/Kernel/Mnemonics/_ifle.php b/src/Kernel/Mnemonics/_ifle.php new file mode 100644 index 00000000..85360b32 --- /dev/null +++ b/src/Kernel/Mnemonics/_ifle.php @@ -0,0 +1,32 @@ +operands !== null) { + return $this->operands; + } + $branchbyte = $this->readShort(); + + return $this->operands = new Operands( + ['branchbyte', $branchbyte, ['branchbyte1', 'branchbyte2']] + ); + } + + public function execute(): void + { + parent::execute(); + $offset = $this->getOperands()['branchbyte']; + $value = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + if ($value <= 0) { + $this->setOffset($this->getProgramCounter() + $offset); + } + } +} diff --git a/src/Kernel/Mnemonics/_iflt.php b/src/Kernel/Mnemonics/_iflt.php new file mode 100644 index 00000000..d0b3ec82 --- /dev/null +++ b/src/Kernel/Mnemonics/_iflt.php @@ -0,0 +1,32 @@ +operands !== null) { + return $this->operands; + } + $branchbyte = $this->readShort(); + + return $this->operands = new Operands( + ['branchbyte', $branchbyte, ['branchbyte1', 'branchbyte2']] + ); + } + + public function execute(): void + { + parent::execute(); + $offset = $this->getOperands()['branchbyte']; + + $value = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + if ($value < 0) { + $this->setOffset($this->getProgramCounter() + $offset); + } + } +} diff --git a/src/Kernel/Mnemonics/_ifne.php b/src/Kernel/Mnemonics/_ifne.php new file mode 100644 index 00000000..bb3ca8df --- /dev/null +++ b/src/Kernel/Mnemonics/_ifne.php @@ -0,0 +1,33 @@ +operands !== null) { + return $this->operands; + } + $branchbyte = $this->readShort(); + + return $this->operands = new Operands( + ['branchbyte', $branchbyte, ['branchbyte1', 'branchbyte2']] + ); + } + + public function execute(): void + { + parent::execute(); + $offset = $this->getOperands()['branchbyte']; + + $operand = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + if ($operand != 0) { + $this->setOffset($this->getProgramCounter() + $offset); + } + } +} diff --git a/src/Kernel/Mnemonics/_ifnonnull.php b/src/Kernel/Mnemonics/_ifnonnull.php new file mode 100644 index 00000000..ac6968bf --- /dev/null +++ b/src/Kernel/Mnemonics/_ifnonnull.php @@ -0,0 +1,30 @@ +operands !== null) { + return $this->operands; + } + $branchbyte = $this->readShort(); + + return $this->operands = new Operands( + ['branchbyte', $branchbyte, ['branchbyte1', 'branchbyte2']] + ); + } + + public function execute(): void + { + parent::execute(); + $offset = $this->getOperands()['branchbyte']; + $operand = $this->popFromOperandStack(); + + if ($operand !== null) { + $this->setOffset($this->getProgramCounter() + $offset); + } + } +} diff --git a/src/Kernel/Mnemonics/_ifnull.php b/src/Kernel/Mnemonics/_ifnull.php new file mode 100644 index 00000000..c5753276 --- /dev/null +++ b/src/Kernel/Mnemonics/_ifnull.php @@ -0,0 +1,33 @@ +operands !== null) { + return $this->operands; + } + $branchbyte = $this->readShort(); + + return $this->operands = new Operands( + ['branchbyte', $branchbyte, ['branchbyte1', 'branchbyte2']] + ); + } + + public function execute(): void + { + parent::execute(); + $offset = $this->getOperands()['branchbyte']; + + $branch = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + if ($branch === null) { + $this->setOffset($this->getProgramCounter() + $offset); + } + } +} diff --git a/src/Kernel/Mnemonics/_iinc.php b/src/Kernel/Mnemonics/_iinc.php new file mode 100644 index 00000000..25b9d66d --- /dev/null +++ b/src/Kernel/Mnemonics/_iinc.php @@ -0,0 +1,35 @@ +operands !== null) { + return $this->operands; + } + $index = $this->readUnsignedByte(); + $const = $this->readByte(); + + return $this->operands = new Operands( + ['index', $index, ['index']], + ['const', $const, ['const']] + ); + } + + public function execute(): void + { + parent::execute(); + $index = $this->getOperands()['index']; + $const = $this->getOperands()['const']; + + $value = Normalizer::getPrimitiveValue($this->getLocalStorage($index)); + + $this->setLocalStorage($index, Int_::get($value + $const)); + } +} diff --git a/src/Kernel/Mnemonics/_iload.php b/src/Kernel/Mnemonics/_iload.php new file mode 100644 index 00000000..d63a55d1 --- /dev/null +++ b/src/Kernel/Mnemonics/_iload.php @@ -0,0 +1,35 @@ +operands !== null) { + return $this->operands; + } + $index = $this->readUnsignedByte(); + + return $this->operands = new Operands( + ['index', $index, ['index']] + ); + } + + public function execute(): void + { + parent::execute(); + $index = $this->getOperands()['index']; + + $this->pushToOperandStack( + Int_::get( + $this->getLocalStorage($index) + ) + ); + } +} diff --git a/src/Kernel/Mnemonics/_iload_0.php b/src/Kernel/Mnemonics/_iload_0.php new file mode 100644 index 00000000..c2894d47 --- /dev/null +++ b/src/Kernel/Mnemonics/_iload_0.php @@ -0,0 +1,29 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->pushToOperandStack( + Int_::get( + $this->getLocalStorage(0) + ) + ); + } +} diff --git a/src/Kernel/Mnemonics/_iload_1.php b/src/Kernel/Mnemonics/_iload_1.php new file mode 100644 index 00000000..52fa3711 --- /dev/null +++ b/src/Kernel/Mnemonics/_iload_1.php @@ -0,0 +1,29 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->pushToOperandStack( + Int_::get( + $this->getLocalStorage(1) + ) + ); + } +} diff --git a/src/Kernel/Mnemonics/_iload_2.php b/src/Kernel/Mnemonics/_iload_2.php new file mode 100644 index 00000000..ec53fbd1 --- /dev/null +++ b/src/Kernel/Mnemonics/_iload_2.php @@ -0,0 +1,29 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->pushToOperandStack( + Int_::get( + $this->getLocalStorage(2) + ) + ); + } +} diff --git a/src/Kernel/Mnemonics/_iload_3.php b/src/Kernel/Mnemonics/_iload_3.php new file mode 100644 index 00000000..9207700c --- /dev/null +++ b/src/Kernel/Mnemonics/_iload_3.php @@ -0,0 +1,29 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->pushToOperandStack( + Int_::get( + $this->getLocalStorage(3) + ) + ); + } +} diff --git a/src/Kernel/Mnemonics/_imul.php b/src/Kernel/Mnemonics/_imul.php new file mode 100644 index 00000000..33b589ec --- /dev/null +++ b/src/Kernel/Mnemonics/_imul.php @@ -0,0 +1,29 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value2 = (int) Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $value1 = (int) Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + $this->pushToOperandStack(Int_::get($value1 * $value2)); + } +} diff --git a/src/Kernel/Mnemonics/_ineg.php b/src/Kernel/Mnemonics/_ineg.php new file mode 100644 index 00000000..869525ae --- /dev/null +++ b/src/Kernel/Mnemonics/_ineg.php @@ -0,0 +1,28 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + $this->pushToOperandStack(Int_::get($value * -1)); + } +} diff --git a/src/Kernel/Mnemonics/_instanceof.php b/src/Kernel/Mnemonics/_instanceof.php new file mode 100644 index 00000000..f7b3a9ea --- /dev/null +++ b/src/Kernel/Mnemonics/_instanceof.php @@ -0,0 +1,51 @@ +operands !== null) { + return $this->operands; + } + $indexbyte = $this->readUnsignedShort(); + + return $this->operands = new Operands( + ['indexbyte', $indexbyte, ['indexbyte1', 'indexbyte2']] + ); + } + + public function execute(): void + { + parent::execute(); + $cp = $this->getConstantPool(); + + /** + * @var JavaClass $objectref + */ + $objectref = $this->popFromOperandStack(); + + $targetClass = $cp[$cp[$this->getOperands()['indexbyte']]->getClassIndex()]; + + [, $className] = Formatter::convertJavaNamespaceToPHP((string) $targetClass); + + if ($objectref->is($className)) { + $this->pushToOperandStack( + Int_::get(1) + ); + return; + } + + $this->pushToOperandStack( + Int_::get(0) + ); + } +} diff --git a/src/Kernel/Mnemonics/_invokedynamic.php b/src/Kernel/Mnemonics/_invokedynamic.php new file mode 100644 index 00000000..268fa1cc --- /dev/null +++ b/src/Kernel/Mnemonics/_invokedynamic.php @@ -0,0 +1,189 @@ +operands !== null) { + return $this->operands; + } + $indexbyte = $this->readUnsignedShort(); + + return $this->operands = new Operands( + ['indexbyte', $indexbyte, ['indexbyte1', 'indexbyte2']] + ); + } + + /** + * `invokedynamic` is a trial implementation. + * + * @throws NotImplementedException + * @throws \PHPJava\Exceptions\NormalizerException + * @throws \PHPJava\Exceptions\TypeException + * @throws \PHPJava\Exceptions\UnableToFindAttributionException + * @see https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-6.html#jvms-6.5.invokedynamic + */ + public function execute(): void + { + parent::execute(); + $cp = $this->getConstantPool(); + /** + * @var \PHPJava\Kernel\Structures\InvokeDynamicInfo $invokeDynamicStructure + */ + $invokeDynamicStructure = $cp[$this->getOperands()['indexbyte']]; + + // NOTE: The values of the third and fourth operand bytes must always be zero. + $thirdByte = $this->read(); + $forthByte = $this->read(); + + /** + * @var NameAndTypeInfo $nameAndTypeIndex + */ + $nameAndTypeIndex = $cp[$invokeDynamicStructure->getNameAndTypeIndex()]; + + $name = $cp[$nameAndTypeIndex->getNameIndex()]->getString(); + $signature = Formatter::parseSignature($cp[$nameAndTypeIndex->getDescriptorIndex()]->getString()); + + $arguments = []; + if (($length = $signature['arguments_count'] - 1) >= 0) { + $arguments = array_fill(0, $length, null); + for ($i = $length; $i >= 0; $i--) { + $arguments[$i] = $this->popFromOperandStack(); + } + + $arguments = Normalizer::normalizeValues( + $arguments, + $signature['arguments'] + ); + } + + /** + * @var BootstrapMethodsAttribute $bootstrapMethodAttribute + */ + $bootstrapMethodAttribute = AttributionResolver::resolve( + // NOTE: bootstrapMethods attribution is defined in JavaClass class. + $this->javaClass->getAttributes(), + BootstrapMethodsAttribute::class + ); + + /** + * @var BootstrapMethod $bootstrapMethod + */ + $bootstrapMethod = $bootstrapMethodAttribute->getBootstrapMethods()[$invokeDynamicStructure->getBootstrapMethodAttrIndex()]; + $bootstrapMethodArguments = $bootstrapMethod->getBootstrapArguments(); + + /** + * @var MethodHandleInfo $methodHandle + */ + $methodHandle = $cp[$bootstrapMethod->getBootstrapMethodRef()]; + + $result = null; + switch ($methodHandle->getReferenceKind()) { + case MethodHandleKind::REF_getField: + throw new NotImplementedException($methodHandle->getReferenceKind() . ' is not implemented in ' . __METHOD__); + case MethodHandleKind::REF_getStatic: + throw new NotImplementedException($methodHandle->getReferenceKind() . ' is not implemented in ' . __METHOD__); + case MethodHandleKind::REF_putField: + throw new NotImplementedException($methodHandle->getReferenceKind() . ' is not implemented in ' . __METHOD__); + case MethodHandleKind::REF_putStatic: + throw new NotImplementedException($methodHandle->getReferenceKind() . ' is not implemented in ' . __METHOD__); + case MethodHandleKind::REF_invokeVirtual: + throw new NotImplementedException($methodHandle->getReferenceKind() . ' is not implemented in ' . __METHOD__); + case MethodHandleKind::REF_invokeStatic: + $factoryClassName = $cp[$cp[$cp[$methodHandle->getReferenceIndex()]->getClassIndex()]->getClassIndex()]->getString(); + + $methodHandleLookup = new Lookup(); + + $methodHandleNameAndTypeConstant = $cp[$cp[$methodHandle->getReferenceIndex()]->getNameAndTypeIndex()]; + $methodHandledName = $cp[$methodHandleNameAndTypeConstant->getNameIndex()]->getString(); + $methodHandledDescriptor = $cp[$methodHandleNameAndTypeConstant->getDescriptorIndex()]->getString(); + + // NOTE: Must be a class name. + $methodHandleType = MethodType::methodType($signature[0]['type']); + + $arguments = array_merge( + [ + MethodHandles::lookup(), + $name, + $methodHandleType, + ], + $bootstrapMethodArguments, + $arguments + ); + + $invokerClass = JavaClass::load( + $factoryClassName, + $this->javaClass->getOptions(), + false + ); + $annotations = $this->getAnnotateInjections( + $invokerClass + ->getInvoker() + ->getStatic() + ->getMethods() + ->getAnnotations($methodHandledName) + ); + + if (!empty($annotations)) { + array_unshift($arguments, ...$annotations); + } + + $this->getDebugTool()->getLogger()->debug( + vsprintf( + 'Call a method: %s, parameters: %d', + [ + $methodHandledName, + count($arguments), + ] + ) + ); + + $result = $invokerClass + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + $methodHandledName, + ...$arguments + ); + break; + case MethodHandleKind::REF_invokeSpecial: + throw new NotImplementedException($methodHandle->getReferenceKind() . ' is not implemented in ' . __METHOD__); + case MethodHandleKind::REF_newInvokeSpecial: + throw new NotImplementedException($methodHandle->getReferenceKind() . ' is not implemented in ' . __METHOD__); + case MethodHandleKind::REF_invokeInterface: + throw new NotImplementedException($methodHandle->getReferenceKind() . ' is not implemented in ' . __METHOD__); + } + + if ($signature[0]['type'] !== Void_::class) { + $this->pushToOperandStack( + Normalizer::normalizeReturnValue( + $result, + $signature[0] + ) + ); + } + } +} diff --git a/src/Kernel/Mnemonics/_invokeinterface.php b/src/Kernel/Mnemonics/_invokeinterface.php new file mode 100644 index 00000000..bc21babc --- /dev/null +++ b/src/Kernel/Mnemonics/_invokeinterface.php @@ -0,0 +1,125 @@ +operands !== null) { + return $this->operands; + } + $indexbyte = $this->readUnsignedShort(); + $count = $this->readUnsignedByte(); + + // NOTE: The value of the fourth operand byte must always be zero. + $this->readByte(); + + return $this->operands = new Operands( + ['indexbyte', $indexbyte, ['indexbyte1', 'indexbyte2']], + ['count', $count, ['count']] + ); + } + + public function execute(): void + { + parent::execute(); + $cp = $this->getConstantPool(); + $index = $this->getOperands()['indexbyte']; + $count = $this->getOperands()['count']; + + $interfaceMethodRef = $cp[$cp[$index]->getNameAndTypeIndex()]; + $className = $cp[$cp[$cp[$index]->getClassIndex()]->getClassIndex()]->getString(); + $name = $cp[$interfaceMethodRef->getNameIndex()]->getString(); + $descriptor = $cp[$interfaceMethodRef->getDescriptorIndex()]->getString(); + + $signature = Formatter::parseSignature($descriptor); + + // POP with right-to-left (objectref + arguments) + $arguments = array_fill(0, $signature['arguments_count'], null); + for ($i = count($arguments) - 1; $i >= 0; $i--) { + $arguments[$i] = $this->popFromOperandStack(); + } + + /** + * @var JavaClassInterface|Lambda $objectref + */ + $objectref = $this->popFromOperandStack(); + + $this->getDebugTool()->getLogger()->debug( + vsprintf( + 'Call a method: %s, parameters: %d', + [ + $name, + count($arguments), + ] + ) + ); + + if ($objectref instanceof Lambda) { + $result = $objectref($name, ...$arguments); + } else { + try { + $result = $objectref->getInvoker()->getDynamic()->getMethods()->call( + $name, + ...$arguments + ); + } catch (NoSuchMethodException | NoSuchCodeAttributeException $e) { + // If targeted method is an abstract or method is undefined, then to find in InnerClass. + // NOTE: Currently, nested InnerClass does not supported. + + $foundClass = false; + foreach ($objectref->getDefinedInnerClasses() as [$class]) { + /** + * @var JavaClass $class + */ + if ($class->getClassName() !== $className) { + continue; + } + + $result = $class->getInvoker()->getDynamic()->getMethods()->call( + $name, + ...$arguments + ); + + $foundClass = true; + break; + } + + try { + // NOTICE: This implementation is a trial. + // I don't know how to refer SuperClass from anonymous type InnerClass. + if (!$foundClass) { + $result = $this->javaClass->getInvoker()->getDynamic()->getMethods()->call( + $name, + ...$arguments + ); + } + } catch (NoSuchMethodException | NoSuchCodeAttributeException $e) { + throw $e; + } + } + } + + if ($signature[0]['type'] !== Void_::class) { + $this->pushToOperandStack( + Normalizer::normalizeReturnValue( + $result, + $signature[0] + ) + ); + } + } +} diff --git a/src/Kernel/Mnemonics/_invokespecial.php b/src/Kernel/Mnemonics/_invokespecial.php new file mode 100644 index 00000000..ba2e7d83 --- /dev/null +++ b/src/Kernel/Mnemonics/_invokespecial.php @@ -0,0 +1,135 @@ +operands !== null) { + return $this->operands; + } + $indexbyte = $this->readUnsignedShort(); + + return $this->operands = new Operands( + ['indexbyte', $indexbyte, ['indexbyte1', 'indexbyte2']] + ); + } + + public function execute(): void + { + parent::execute(); + $cpInfo = $this->getConstantPool(); + $cp = $cpInfo[$this->getOperands()['indexbyte']]; + $nameAndTypeIndex = $cpInfo[$cp->getNameAndTypeIndex()]; + $className = $cpInfo[$cpInfo[$cp->getClassIndex()]->getClassIndex()]->getString(); + $methodName = $cpInfo[$nameAndTypeIndex->getNameIndex()]->getString(); + $signature = $cpInfo[$nameAndTypeIndex->getDescriptorIndex()]->getString(); + $parsedSignature = Formatter::parseSignature($signature); + + // POP with right-to-left (objectref + arguments) + $arguments = array_fill(0, $parsedSignature['arguments_count'], null); + for ($i = count($arguments) - 1; $i >= 0; $i--) { + $arguments[$i] = $this->popFromOperandStack(); + } + + /** + * @var JavaClassInterface $objectref + */ + $objectref = $newObject = $this->popFromOperandStack(); + try { + $methodName = $cpInfo[$nameAndTypeIndex->getNameIndex()]->getString(); + + // load a class dynamically if not match class name and objectref class + if (!CompareTool::compareClassName($className, $objectref->getClassName())) { + $newObject = JavaClass::load( + $className, + $this->javaClass->getOptions() + ); + } + + $this->getDebugTool()->getLogger()->debug( + vsprintf( + 'Call a method: %s, parameters: %d', + [ + $methodName, + count($arguments), + ] + ) + ); + + /** + * @var JavaClassInterface $newObject + */ + $result = $newObject->getInvoker()->getDynamic()->getMethods()->call( + $methodName, + ...$arguments + ); + + // Call special method (e.g., , and soon) + if (MethodNameResolver::isConstructorMethod($methodName)) { + $result = $objectref; + + // Set initialized parent parameters + + // Static field is written below. + $existFieldList = array_keys($objectref->getInvoker()->getStatic()->getFields()->getList()); + foreach ($newObject->getInvoker()->getStatic()->getFields()->getList() as $fieldName => $value) { + if (in_array($fieldName, $existFieldList, true)) { + continue; + } + $objectref + ->getInvoker() + ->getStatic() + ->getFields() + ->set( + $fieldName, + $value + ); + } + + // Dynamic field is written below. + $existFieldList = array_keys($objectref->getInvoker()->getDynamic()->getFields()->getList()); + foreach ($newObject->getInvoker()->getDynamic()->getFields()->getList() as $fieldName => $value) { + if (in_array($fieldName, $existFieldList, true)) { + continue; + } + $objectref + ->getInvoker() + ->getDynamic() + ->getFields() + ->set( + $fieldName, + $value + ); + } + } + } catch (\Exception $e) { + $this->inspectExceptionTable($e); + return; + } + + if ($parsedSignature[0]['type'] !== Void_::class) { + $this->pushToOperandStack( + Normalizer::normalizeReturnValue( + $result, + $parsedSignature[0] + ) + ); + } + } +} diff --git a/src/Kernel/Mnemonics/_invokestatic.php b/src/Kernel/Mnemonics/_invokestatic.php new file mode 100644 index 00000000..c22ef7a0 --- /dev/null +++ b/src/Kernel/Mnemonics/_invokestatic.php @@ -0,0 +1,97 @@ +operands !== null) { + return $this->operands; + } + $indexbyte = $this->readUnsignedShort(); + + return $this->operands = new Operands( + ['indexbyte', $indexbyte, ['indexbyte1', 'indexbyte2']] + ); + } + + public function execute(): void + { + parent::execute(); + $cpInfo = $this->getConstantPool(); + $cp = $cpInfo[$this->getOperands()['indexbyte']]; + $className = $cpInfo[$cpInfo[$cp->getClassIndex()]->getClassIndex()]->getString(); + $methodName = $cpInfo[$cpInfo[$cp->getNameAndTypeIndex()]->getNameIndex()]->getString(); + $signature = Formatter::parseSignature($cpInfo[$cpInfo[$cp->getNameAndTypeIndex()]->getDescriptorIndex()]->getString()); + + $arguments = []; + if (($length = $signature['arguments_count'] - 1) >= 0) { + $arguments = array_fill(0, $length, null); + for ($i = $length; $i >= 0; $i--) { + $arguments[$i] = $this->popFromOperandStack(); + } + } + + $result = null; + + try { + $classObject = JavaClass::load($className, $this->javaClass->getOptions(), false); + $annotations = $this->getAnnotateInjections( + $classObject + ->getInvoker() + ->getStatic() + ->getMethods() + ->getAnnotations( + $methodName + ) + ); + + if (!empty($annotations)) { + array_unshift($arguments, ...$annotations); + } + + $this->getDebugTool()->getLogger()->debug( + vsprintf( + 'Call a method: %s, parameters: %d', + [ + $methodName, + count($arguments), + ] + ) + ); + + $result = $classObject + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + $methodName, + ...$arguments + ); + } catch (\Exception $e) { + $this->inspectExceptionTable($e); + return; + } + + if ($signature[0]['type'] !== Void_::class) { + $this->pushToOperandStack( + Normalizer::normalizeReturnValue( + $result, + $signature[0] + ) + ); + } + } +} diff --git a/src/Kernel/Mnemonics/_invokevirtual.php b/src/Kernel/Mnemonics/_invokevirtual.php new file mode 100644 index 00000000..6a7d59fc --- /dev/null +++ b/src/Kernel/Mnemonics/_invokevirtual.php @@ -0,0 +1,127 @@ +operands !== null) { + return $this->operands; + } + + $indexbyte = $this->readUnsignedShort(); + + return $this->operands = new Operands( + ['indexbyte', $indexbyte, ['indexbyte1', 'indexbyte2']] + ); + } + + /** + * @throws NullPointerException + * @throws \PHPJava\Exceptions\IllegalOperationException + * @throws \PHPJava\Exceptions\NormalizerException + * @throws \PHPJava\Exceptions\TypeException + * @throws \PHPJava\Exceptions\UnableToFindAttributionException + * @throws \PHPJava\Exceptions\UncaughtException + */ + public function execute(): void + { + parent::execute(); + $cpInfo = $this->getConstantPool(); + $cp = $cpInfo[$this->getOperands()['indexbyte']]; + $class = $cpInfo[$cpInfo[$cp->getClassIndex()]->getClassIndex()]->getString(); + $nameAndTypeIndex = $cpInfo[$cp->getNameAndTypeIndex()]; + + // signature + $signature = Formatter::parseSignature($cpInfo[$nameAndTypeIndex->getDescriptorIndex()]->getString()); + + $arguments = []; + if (($length = $signature['arguments_count'] - 1) >= 0) { + $arguments = array_fill(0, $length, null); + for ($i = $length; $i >= 0; $i--) { + $arguments[$i] = $this->popFromOperandStack(); + } + + $arguments = Normalizer::normalizeValues( + $arguments, + $signature['arguments'] + ); + } + + $this->methodSignature = $signature; + + /** + * @var JavaClassInterface $invokerClass + */ + $invokerClass = $this->popFromOperandStack(); + $methodName = $cpInfo[$cpInfo[$cp->getNameAndTypeIndex()]->getNameIndex()]->getString(); + + if ($invokerClass === null) { + throw new NullPointerException( + str_replace('/', '.', $class) . ' is a NULL.' + ); + } + + try { + $annotations = $this->getAnnotateInjections( + $invokerClass + ->getInvoker() + ->getDynamic() + ->getMethods() + ->getAnnotations( + $methodName + ) + ); + + if (!empty($annotations)) { + array_unshift($arguments, ...$annotations); + } + + $this->getDebugTool()->getLogger()->debug( + vsprintf( + 'Call a method: %s, parameters: %d', + [ + $methodName, + count($arguments), + ] + ) + ); + + $result = $invokerClass + ->getInvoker() + ->getDynamic() + ->getMethods() + ->call( + $methodName, + ...$arguments + ); + } catch (\Exception $e) { + $this->inspectExceptionTable($e); + return; + } + + if ($signature[0]['type'] !== Void_::class) { + $this->pushToOperandStack( + Normalizer::normalizeReturnValue( + $result, + $signature[0] + ) + ); + } + } +} diff --git a/src/Kernel/Mnemonics/_ior.php b/src/Kernel/Mnemonics/_ior.php new file mode 100644 index 00000000..9a65a2fd --- /dev/null +++ b/src/Kernel/Mnemonics/_ior.php @@ -0,0 +1,29 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value2 = (int) Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $value1 = (int) Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + $this->pushToOperandStack(Int_::get($value1 | $value2)); + } +} diff --git a/src/Kernel/Mnemonics/_irem.php b/src/Kernel/Mnemonics/_irem.php new file mode 100644 index 00000000..dff3dd85 --- /dev/null +++ b/src/Kernel/Mnemonics/_irem.php @@ -0,0 +1,36 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + // JVM spec wrote `value1 - (value1 / value2) * value2` + // But PHP can modulo calculation. + + $rightOperand = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $leftOperand = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + $this->pushToOperandStack( + Int_::get( + $leftOperand % $rightOperand + ) + ); + } +} diff --git a/src/Kernel/Mnemonics/_ireturn.php b/src/Kernel/Mnemonics/_ireturn.php new file mode 100644 index 00000000..712855cd --- /dev/null +++ b/src/Kernel/Mnemonics/_ireturn.php @@ -0,0 +1,33 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + // return an integer from a method + public function execute(): void + { + parent::execute(); + $value = $this->popFromOperandStack(); + $isIntegerValue = $value instanceof Int_ || + $value instanceof Char_ || + $value instanceof Short_ || + $value instanceof Byte_ || + $value instanceof _Boolean; + $this->returnValue = $isIntegerValue ? $value : Int_::get($value); + } +} diff --git a/src/Kernel/Mnemonics/_ishl.php b/src/Kernel/Mnemonics/_ishl.php new file mode 100644 index 00000000..338d4206 --- /dev/null +++ b/src/Kernel/Mnemonics/_ishl.php @@ -0,0 +1,29 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value2 = (int) Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $value1 = (int) Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + $this->pushToOperandStack(Int_::get($value1 << $value2)); + } +} diff --git a/src/Kernel/Mnemonics/_ishr.php b/src/Kernel/Mnemonics/_ishr.php new file mode 100644 index 00000000..33574c2d --- /dev/null +++ b/src/Kernel/Mnemonics/_ishr.php @@ -0,0 +1,29 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value2 = (int) Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $value1 = (int) Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + $this->pushToOperandStack(Int_::get($value1 >> $value2)); + } +} diff --git a/src/Kernel/Mnemonics/_istore.php b/src/Kernel/Mnemonics/_istore.php new file mode 100644 index 00000000..5238e0c8 --- /dev/null +++ b/src/Kernel/Mnemonics/_istore.php @@ -0,0 +1,26 @@ +operands !== null) { + return $this->operands; + } + $index = $this->readUnsignedByte(); + + return $this->operands = new Operands( + ['index', $index, ['index']] + ); + } + + public function execute(): void + { + parent::execute(); + $index = $this->getOperands()['index']; + $this->setLocalStorage($index, $this->popFromOperandStack()); + } +} diff --git a/src/Kernel/Mnemonics/_istore_0.php b/src/Kernel/Mnemonics/_istore_0.php new file mode 100644 index 00000000..ff2e3472 --- /dev/null +++ b/src/Kernel/Mnemonics/_istore_0.php @@ -0,0 +1,21 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->setLocalStorage(0, $this->popFromOperandStack()); + } +} diff --git a/src/Kernel/Mnemonics/_istore_1.php b/src/Kernel/Mnemonics/_istore_1.php new file mode 100644 index 00000000..85cbf88b --- /dev/null +++ b/src/Kernel/Mnemonics/_istore_1.php @@ -0,0 +1,21 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->setLocalStorage(1, $this->popFromOperandStack()); + } +} diff --git a/src/Kernel/Mnemonics/_istore_2.php b/src/Kernel/Mnemonics/_istore_2.php new file mode 100644 index 00000000..9c53ddab --- /dev/null +++ b/src/Kernel/Mnemonics/_istore_2.php @@ -0,0 +1,21 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->setLocalStorage(2, $this->popFromOperandStack()); + } +} diff --git a/src/Kernel/Mnemonics/_istore_3.php b/src/Kernel/Mnemonics/_istore_3.php new file mode 100644 index 00000000..1b288639 --- /dev/null +++ b/src/Kernel/Mnemonics/_istore_3.php @@ -0,0 +1,21 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->setLocalStorage(3, $this->popFromOperandStack()); + } +} diff --git a/src/Kernel/Mnemonics/_isub.php b/src/Kernel/Mnemonics/_isub.php new file mode 100644 index 00000000..990668b0 --- /dev/null +++ b/src/Kernel/Mnemonics/_isub.php @@ -0,0 +1,29 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value2 = (int) Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $value1 = (int) Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + $this->pushToOperandStack(Int_::get($value1 - $value2)); + } +} diff --git a/src/Kernel/Mnemonics/_iushr.php b/src/Kernel/Mnemonics/_iushr.php new file mode 100644 index 00000000..3322edde --- /dev/null +++ b/src/Kernel/Mnemonics/_iushr.php @@ -0,0 +1,32 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value2 = (int) Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $value1 = (int) Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + // See: https://stackoverflow.com/questions/14428193/php-unsigned-right-shift-malfunctioning + $this->pushToOperandStack( + Int_::get(($value1 >> $value2) & ~(1 << (8 * PHP_INT_SIZE - 1) >> ($value2 - 1))) + ); + } +} diff --git a/src/Kernel/Mnemonics/_ixor.php b/src/Kernel/Mnemonics/_ixor.php new file mode 100644 index 00000000..ccf12cab --- /dev/null +++ b/src/Kernel/Mnemonics/_ixor.php @@ -0,0 +1,29 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value2 = (int) Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $value1 = (int) Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + $this->pushToOperandStack(Int_::get($value1 ^ $value2)); + } +} diff --git a/src/Kernel/Mnemonics/_jsr.php b/src/Kernel/Mnemonics/_jsr.php new file mode 100644 index 00000000..c97286db --- /dev/null +++ b/src/Kernel/Mnemonics/_jsr.php @@ -0,0 +1,23 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + throw new NotImplementedException(__CLASS__); + } +} diff --git a/src/Kernel/Mnemonics/_jsr_w.php b/src/Kernel/Mnemonics/_jsr_w.php new file mode 100644 index 00000000..91374168 --- /dev/null +++ b/src/Kernel/Mnemonics/_jsr_w.php @@ -0,0 +1,23 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + throw new NotImplementedException(__CLASS__); + } +} diff --git a/src/Kernel/Mnemonics/_l2d.php b/src/Kernel/Mnemonics/_l2d.php new file mode 100644 index 00000000..47f1014c --- /dev/null +++ b/src/Kernel/Mnemonics/_l2d.php @@ -0,0 +1,30 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value = Normalizer::getPrimitiveValue( + $this->popFromOperandStack() + ); + + $this->pushToOperandStack(Double_::get($value)); + } +} diff --git a/src/Kernel/Mnemonics/_l2f.php b/src/Kernel/Mnemonics/_l2f.php new file mode 100644 index 00000000..fa6f5403 --- /dev/null +++ b/src/Kernel/Mnemonics/_l2f.php @@ -0,0 +1,30 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value = Normalizer::getPrimitiveValue( + $this->popFromOperandStack() + ); + + $this->pushToOperandStack(Float_::get($value)); + } +} diff --git a/src/Kernel/Mnemonics/_l2i.php b/src/Kernel/Mnemonics/_l2i.php new file mode 100644 index 00000000..8e7f3b5e --- /dev/null +++ b/src/Kernel/Mnemonics/_l2i.php @@ -0,0 +1,30 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value = Normalizer::getPrimitiveValue( + $this->popFromOperandStack() + ); + + $this->pushToOperandStack(Int_::get($value)); + } +} diff --git a/src/Kernel/Mnemonics/_ladd.php b/src/Kernel/Mnemonics/_ladd.php new file mode 100644 index 00000000..ad668b53 --- /dev/null +++ b/src/Kernel/Mnemonics/_ladd.php @@ -0,0 +1,33 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value2 = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $value1 = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + $result = (string) BigInteger::of($value1) + ->plus(BigInteger::of($value2)); + + $this->pushToOperandStack(Long_::get($result)); + } +} diff --git a/src/Kernel/Mnemonics/_laload.php b/src/Kernel/Mnemonics/_laload.php new file mode 100644 index 00000000..9e16024d --- /dev/null +++ b/src/Kernel/Mnemonics/_laload.php @@ -0,0 +1,31 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + /** + * load a long from an array. + */ + public function execute(): void + { + parent::execute(); + $index = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $arrayref = $this->popFromOperandStack(); + + $this->pushToOperandStack($arrayref[$index]); + } +} diff --git a/src/Kernel/Mnemonics/_land.php b/src/Kernel/Mnemonics/_land.php new file mode 100644 index 00000000..5ea18f0e --- /dev/null +++ b/src/Kernel/Mnemonics/_land.php @@ -0,0 +1,33 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value2 = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $value1 = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + $result = (string) BigInteger::of($value1) + ->and(BigInteger::of($value2)); + + $this->pushToOperandStack(Long_::get($result)); + } +} diff --git a/src/Kernel/Mnemonics/_lastore.php b/src/Kernel/Mnemonics/_lastore.php new file mode 100644 index 00000000..06fe3449 --- /dev/null +++ b/src/Kernel/Mnemonics/_lastore.php @@ -0,0 +1,30 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + /** + * store a long to an array. + */ + public function execute(): void + { + parent::execute(); + $value = $this->popFromOperandStack(); + $index = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $arrayref = $this->popFromOperandStack(); + + $arrayref[$index] = $value; + } +} diff --git a/src/Kernel/Mnemonics/_lcmp.php b/src/Kernel/Mnemonics/_lcmp.php new file mode 100644 index 00000000..429feeac --- /dev/null +++ b/src/Kernel/Mnemonics/_lcmp.php @@ -0,0 +1,42 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value2 = (string) Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $value1 = (string) Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + $compare = BigInteger::of($value1)->compareTo($value2); + + if ($compare == 1) { + $this->pushToOperandStack(Int_::get(1)); + return; + } + + if ($compare == -1) { + $this->pushToOperandStack(Int_::get(-1)); + return; + } + + $this->pushToOperandStack(Int_::get(0)); + } +} diff --git a/src/Kernel/Mnemonics/_lconst_0.php b/src/Kernel/Mnemonics/_lconst_0.php new file mode 100644 index 00000000..66d4cfc5 --- /dev/null +++ b/src/Kernel/Mnemonics/_lconst_0.php @@ -0,0 +1,23 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->pushToOperandStack(0); + } +} diff --git a/src/Kernel/Mnemonics/_lconst_1.php b/src/Kernel/Mnemonics/_lconst_1.php new file mode 100644 index 00000000..f9f668a7 --- /dev/null +++ b/src/Kernel/Mnemonics/_lconst_1.php @@ -0,0 +1,23 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->pushToOperandStack(1); + } +} diff --git a/src/Kernel/Mnemonics/_ldc.php b/src/Kernel/Mnemonics/_ldc.php new file mode 100644 index 00000000..a84a3818 --- /dev/null +++ b/src/Kernel/Mnemonics/_ldc.php @@ -0,0 +1,54 @@ +operands !== null) { + return $this->operands; + } + $index = $this->readUnsignedByte(); + + return $this->operands = new Operands( + ['index', $index, ['index']] + ); + } + + public function execute(): void + { + parent::execute(); + $cpInfo = $this->getConstantPool(); + $data = $cpInfo[$this->getOperands()['index']]; + + $value = null; + + if ($data instanceof StringInfo) { + $value = $cpInfo[$data->getStringIndex()]; + if ($value instanceof Utf8Info) { + $value = $value->getStringObject(); + } + } elseif ($data instanceof IntegerInfo) { + $value = Int_::get($data->getBytes()); + } elseif ($data instanceof FloatInfo) { + $value = \PHPJava\Kernel\Types\Float_::get($data->getBytes()); + } elseif ($data instanceof ClassInfo) { + $value = $cpInfo[$data->getClassIndex()]->getStringObject(); + } else { + $value = $cpInfo[$data->getStringIndex()]; + } + + $this->pushToOperandStack($value); + } +} diff --git a/src/Kernel/Mnemonics/_ldc2_w.php b/src/Kernel/Mnemonics/_ldc2_w.php new file mode 100644 index 00000000..94be9a31 --- /dev/null +++ b/src/Kernel/Mnemonics/_ldc2_w.php @@ -0,0 +1,43 @@ +operands !== null) { + return $this->operands; + } + $indexbyte = $this->readUnsignedShort(); + + return $this->operands = new Operands( + ['indexbyte', $indexbyte, ['indexbyte1', 'indexbyte2']] + ); + } + + public function execute(): void + { + parent::execute(); + $cpInfo = $this->getConstantPool(); + $data = $cpInfo[$this->getOperands()['indexbyte']]; + $value = null; + + if (($data instanceof \PHPJava\Kernel\Structures\LongInfo)) { + $value = Long_::get($data->getBytes()); + } elseif ($data instanceof \PHPJava\Kernel\Structures\DoubleInfo) { + $value = Double_::get($data->getBytes()); + } else { + throw new UnsupportedOperationException('Unsupported operation.'); + } + + $this->pushToOperandStack($value); + } +} diff --git a/src/Kernel/Mnemonics/_ldc_w.php b/src/Kernel/Mnemonics/_ldc_w.php new file mode 100644 index 00000000..f7502989 --- /dev/null +++ b/src/Kernel/Mnemonics/_ldc_w.php @@ -0,0 +1,52 @@ +operands !== null) { + return $this->operands; + } + $indexbyte = $this->readUnsignedShort(); + + return $this->operands = new Operands( + ['indexbyte', $indexbyte, ['indexbyte1', 'indexbyte2']] + ); + } + + public function execute(): void + { + parent::execute(); + $cpInfo = $this->getConstantPool(); + $data = $cpInfo[$this->getOperands()['indexbyte']]; + + $value = null; + + if ($data instanceof StringInfo) { + $value = $cpInfo[$data->getStringIndex()]; + + if ($value instanceof Utf8Info) { + $value = $value->getStringObject(); + } + } elseif (($data instanceof IntegerInfo)) { + $value = Int_::get($data->getBytes()); + } elseif ($data instanceof Float_) { + $value = \PHPJava\Kernel\Types\Float_::get($data->getBytes()); + } else { + $value = $cpInfo[$data->getClassIndex()]; + } + + $this->pushToOperandStack($value); + } +} diff --git a/src/Kernel/Mnemonics/_ldiv.php b/src/Kernel/Mnemonics/_ldiv.php new file mode 100644 index 00000000..4b082214 --- /dev/null +++ b/src/Kernel/Mnemonics/_ldiv.php @@ -0,0 +1,33 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value2 = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $value1 = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + $result = (string) BigInteger::of($value1) + ->dividedBy(BigInteger::of($value2)); + + $this->pushToOperandStack(Long_::get($result)); + } +} diff --git a/src/Kernel/Mnemonics/_lload.php b/src/Kernel/Mnemonics/_lload.php new file mode 100644 index 00000000..e02691a3 --- /dev/null +++ b/src/Kernel/Mnemonics/_lload.php @@ -0,0 +1,35 @@ +operands !== null) { + return $this->operands; + } + $index = $this->readUnsignedByte(); + + return $this->operands = new Operands( + ['index', $index, ['index']] + ); + } + + public function execute(): void + { + parent::execute(); + $index = $this->getOperands()['index']; + + $this->pushToOperandStack( + Long_::get( + $this->getLocalStorage($index) + ) + ); + } +} diff --git a/src/Kernel/Mnemonics/_lload_0.php b/src/Kernel/Mnemonics/_lload_0.php new file mode 100644 index 00000000..a3fb7c6d --- /dev/null +++ b/src/Kernel/Mnemonics/_lload_0.php @@ -0,0 +1,29 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->pushToOperandStack( + Long_::get( + $this->getLocalStorage(0) + ) + ); + } +} diff --git a/src/Kernel/Mnemonics/_lload_1.php b/src/Kernel/Mnemonics/_lload_1.php new file mode 100644 index 00000000..772630e8 --- /dev/null +++ b/src/Kernel/Mnemonics/_lload_1.php @@ -0,0 +1,29 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->pushToOperandStack( + Long_::get( + $this->getLocalStorage(1) + ) + ); + } +} diff --git a/src/Kernel/Mnemonics/_lload_2.php b/src/Kernel/Mnemonics/_lload_2.php new file mode 100644 index 00000000..86b490a1 --- /dev/null +++ b/src/Kernel/Mnemonics/_lload_2.php @@ -0,0 +1,29 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->pushToOperandStack( + Long_::get( + $this->getLocalStorage(2) + ) + ); + } +} diff --git a/src/Kernel/Mnemonics/_lload_3.php b/src/Kernel/Mnemonics/_lload_3.php new file mode 100644 index 00000000..06ec6cf8 --- /dev/null +++ b/src/Kernel/Mnemonics/_lload_3.php @@ -0,0 +1,29 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->pushToOperandStack( + Long_::get( + $this->getLocalStorage(3) + ) + ); + } +} diff --git a/src/Kernel/Mnemonics/_lmul.php b/src/Kernel/Mnemonics/_lmul.php new file mode 100644 index 00000000..592d31db --- /dev/null +++ b/src/Kernel/Mnemonics/_lmul.php @@ -0,0 +1,33 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value2 = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $value1 = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + $result = (string) BigInteger::of($value1) + ->multipliedBy(BigInteger::of($value2)); + + $this->pushToOperandStack(Long_::get($result)); + } +} diff --git a/src/Kernel/Mnemonics/_lneg.php b/src/Kernel/Mnemonics/_lneg.php new file mode 100644 index 00000000..41233c40 --- /dev/null +++ b/src/Kernel/Mnemonics/_lneg.php @@ -0,0 +1,34 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value = Normalizer::getPrimitiveValue( + $this->popFromOperandStack() + ); + + $result = (string) BigInteger::of($value) + ->multipliedBy(BigInteger::of(-1)); + + $this->pushToOperandStack(Long_::get($result)); + } +} diff --git a/src/Kernel/Mnemonics/_lookupswitch.php b/src/Kernel/Mnemonics/_lookupswitch.php new file mode 100644 index 00000000..2cff7d6e --- /dev/null +++ b/src/Kernel/Mnemonics/_lookupswitch.php @@ -0,0 +1,55 @@ +operands !== null) { + return $this->operands; + } + // padding data + $paddingData = 4 - (($this->getOffset()) % 4); + if ($paddingData != 4) { + $this->read($paddingData); + } + + $default = $this->readInt(); + $npairs = $this->readUnsignedInt(); + $offsets = []; + + for ($i = 0; $i < $npairs; $i++) { + $label = $this->readUnsignedInt(); + $offsets[(string) $label] = $this->readInt(); + } + + return $this->operands = new Operands( + ['default', $default, ['defaultbyte1', 'defaultbyte2', 'defaultbyte3', 'defaultbyte4']], + ['npairs', $npairs, ['npairs1', 'npairs2', 'npairs3', 'npairs4']], + ['offsets', $offsets, ['match-offset']] + ); + } + + public function execute(): void + { + parent::execute(); + $key = Normalizer::getPrimitiveValue( + $this->popFromOperandStack() + ); + + $offsets = $this->getOperands()['offsets']; + + if (isset($offsets[$key])) { + // goto PC + $this->setOffset($this->getProgramCounter() + $offsets[$key]); + return; + } + + // goto default + $this->setOffset($this->getProgramCounter() + $this->getOperands()['default']); + } +} diff --git a/src/Kernel/Mnemonics/_lor.php b/src/Kernel/Mnemonics/_lor.php new file mode 100644 index 00000000..1c860377 --- /dev/null +++ b/src/Kernel/Mnemonics/_lor.php @@ -0,0 +1,33 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value2 = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $value1 = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + $result = (string) BigInteger::of($value1) + ->or(BigInteger::of($value2)); + + $this->pushToOperandStack(Long_::get($result)); + } +} diff --git a/src/Kernel/Mnemonics/_lrem.php b/src/Kernel/Mnemonics/_lrem.php new file mode 100644 index 00000000..e603bd61 --- /dev/null +++ b/src/Kernel/Mnemonics/_lrem.php @@ -0,0 +1,33 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $rightOperand = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $leftOperand = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + $this->pushToOperandStack( + Long_::get( + $leftOperand % $rightOperand + ) + ); + } +} diff --git a/src/Kernel/Mnemonics/_lreturn.php b/src/Kernel/Mnemonics/_lreturn.php new file mode 100644 index 00000000..c58cb91d --- /dev/null +++ b/src/Kernel/Mnemonics/_lreturn.php @@ -0,0 +1,26 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value = $this->popFromOperandStack(); + $this->returnValue = ($value instanceof Long_) + ? $value + : Long_::get($value); + } +} diff --git a/src/Kernel/Mnemonics/_lshl.php b/src/Kernel/Mnemonics/_lshl.php new file mode 100644 index 00000000..8615d7d2 --- /dev/null +++ b/src/Kernel/Mnemonics/_lshl.php @@ -0,0 +1,33 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value2 = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $value1 = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + $result = (string) BigInteger::of($value1) + ->shiftedLeft((int) $value2); + + $this->pushToOperandStack(Long_::get($result)); + } +} diff --git a/src/Kernel/Mnemonics/_lshr.php b/src/Kernel/Mnemonics/_lshr.php new file mode 100644 index 00000000..f098751f --- /dev/null +++ b/src/Kernel/Mnemonics/_lshr.php @@ -0,0 +1,33 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value2 = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $value1 = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + $result = (string) BigInteger::of($value1) + ->shiftedRight((int) $value2); + + $this->pushToOperandStack(Long_::get($result)); + } +} diff --git a/src/Kernel/Mnemonics/_lstore.php b/src/Kernel/Mnemonics/_lstore.php new file mode 100644 index 00000000..4322e4c0 --- /dev/null +++ b/src/Kernel/Mnemonics/_lstore.php @@ -0,0 +1,26 @@ +operands !== null) { + return $this->operands; + } + $index = $this->readUnsignedByte(); + + return $this->operands = new Operands( + ['index', $index, ['index']] + ); + } + + public function execute(): void + { + parent::execute(); + $index = $this->getOperands()['index']; + $this->setLocalStorage($index, $this->popFromOperandStack()); + } +} diff --git a/src/Kernel/Mnemonics/_lstore_0.php b/src/Kernel/Mnemonics/_lstore_0.php new file mode 100644 index 00000000..9df37ad0 --- /dev/null +++ b/src/Kernel/Mnemonics/_lstore_0.php @@ -0,0 +1,21 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->setLocalStorage(0, $this->popFromOperandStack()); + } +} diff --git a/src/Kernel/Mnemonics/_lstore_1.php b/src/Kernel/Mnemonics/_lstore_1.php new file mode 100644 index 00000000..c0e3985c --- /dev/null +++ b/src/Kernel/Mnemonics/_lstore_1.php @@ -0,0 +1,21 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->setLocalStorage(1, $this->popFromOperandStack()); + } +} diff --git a/src/Kernel/Mnemonics/_lstore_2.php b/src/Kernel/Mnemonics/_lstore_2.php new file mode 100644 index 00000000..4ec017a0 --- /dev/null +++ b/src/Kernel/Mnemonics/_lstore_2.php @@ -0,0 +1,21 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->setLocalStorage(2, $this->popFromOperandStack()); + } +} diff --git a/src/Kernel/Mnemonics/_lstore_3.php b/src/Kernel/Mnemonics/_lstore_3.php new file mode 100644 index 00000000..c9185ecf --- /dev/null +++ b/src/Kernel/Mnemonics/_lstore_3.php @@ -0,0 +1,21 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->setLocalStorage(3, $this->popFromOperandStack()); + } +} diff --git a/src/Kernel/Mnemonics/_lsub.php b/src/Kernel/Mnemonics/_lsub.php new file mode 100644 index 00000000..18eaac07 --- /dev/null +++ b/src/Kernel/Mnemonics/_lsub.php @@ -0,0 +1,33 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value2 = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $value1 = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + $result = (string) BigInteger::of($value1) + ->minus(BigInteger::of($value2)); + + $this->pushToOperandStack(Long_::get($result)); + } +} diff --git a/src/Kernel/Mnemonics/_lushr.php b/src/Kernel/Mnemonics/_lushr.php new file mode 100644 index 00000000..b51b5480 --- /dev/null +++ b/src/Kernel/Mnemonics/_lushr.php @@ -0,0 +1,32 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value2 = (int) Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $value1 = (int) Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + // See: https://stackoverflow.com/questions/14428193/php-unsigned-right-shift-malfunctioning + $this->pushToOperandStack( + Long_::get(($value1 >> $value2) & ~(1 << (8 * PHP_INT_SIZE - 1) >> ($value2 - 1))) + ); + } +} diff --git a/src/Kernel/Mnemonics/_lxor.php b/src/Kernel/Mnemonics/_lxor.php new file mode 100644 index 00000000..463309b4 --- /dev/null +++ b/src/Kernel/Mnemonics/_lxor.php @@ -0,0 +1,33 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value2 = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $value1 = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + $result = (string) BigInteger::of($value1) + ->xor(BigInteger::of($value2)); + + $this->pushToOperandStack(Long_::get($result)); + } +} diff --git a/src/Kernel/Mnemonics/_monitorenter.php b/src/Kernel/Mnemonics/_monitorenter.php new file mode 100644 index 00000000..cf473d18 --- /dev/null +++ b/src/Kernel/Mnemonics/_monitorenter.php @@ -0,0 +1,23 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + throw new NotImplementedException(__CLASS__); + } +} diff --git a/src/Kernel/Mnemonics/_monitorexit.php b/src/Kernel/Mnemonics/_monitorexit.php new file mode 100644 index 00000000..493be19b --- /dev/null +++ b/src/Kernel/Mnemonics/_monitorexit.php @@ -0,0 +1,23 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + throw new NotImplementedException(__CLASS__); + } +} diff --git a/src/Kernel/Mnemonics/_multianewarray.php b/src/Kernel/Mnemonics/_multianewarray.php new file mode 100644 index 00000000..55cd058a --- /dev/null +++ b/src/Kernel/Mnemonics/_multianewarray.php @@ -0,0 +1,82 @@ +operands !== null) { + return $this->operands; + } + $indexbyte = $this->readUnsignedShort(); + $dimensions = $this->readByte(); + + return $this->operands = new Operands( + ['indexbyte', $indexbyte, ['indexbyte1', 'indexbyte2']], + ['dimensions', $dimensions, ['dimensions']] + ); + } + + public function execute(): void + { + parent::execute(); + $cp = $this->getConstantPool(); + $index = $this->getOperands()['indexbyte']; + $dimensions = $this->getOperands()['dimensions']; + + $descriptor = Formatter::parseSignature( + $cp[$cp[$index]->getClassIndex()]->getString() + ); + + $counts = array_fill(0, $dimensions, 0); + + for ($i = $dimensions - 1; $i >= 0; $i--) { + $counts[$i] = Normalizer::getPrimitiveValue( + $this->popFromOperandStack() + ); + } + + $data = null; + $multiDimensionArray = $this->makeMultiDimensionArray( + $data, + $counts, + $descriptor[0]['type'], + 0, + $dimensions + ); + + $this->pushToOperandStackByReference($multiDimensionArray); + } + + /** + * @param $array + * @return $this + */ + private function makeMultiDimensionArray($array, array $counts, string $type, int $currentDimension, int $maxDimension) + { + $newArray = (new Collection())->setType($type); + for ($i = 0; $i < $counts[$currentDimension]; $i++) { + if ($currentDimension === $maxDimension - 1) { + $newArray[$i] = null; + continue; + } + $collection = (new Collection())->setType($type); + $newArray[$i] = $this->makeMultiDimensionArray( + $collection, + $counts, + $type, + $currentDimension + 1, + $maxDimension + ); + } + return $newArray; + } +} diff --git a/src/Kernel/Mnemonics/_new.php b/src/Kernel/Mnemonics/_new.php new file mode 100644 index 00000000..0506c3f5 --- /dev/null +++ b/src/Kernel/Mnemonics/_new.php @@ -0,0 +1,38 @@ +operands !== null) { + return $this->operands; + } + $indexbyte = $this->readUnsignedShort(); + + return $this->operands = new Operands( + ['indexbyte', $indexbyte, ['indexbyte1', 'indexbyte2']] + ); + } + + public function execute(): void + { + parent::execute(); + $cpInfo = $this->getConstantPool(); + $class = $cpInfo[$this->getOperands()['indexbyte']]; + $className = $cpInfo[$class->getClassIndex()]->getString(); + + $javaClass = JavaClass::load( + $className, + $this->javaClass->getOptions() + ); + + $this->pushToOperandStackByReference($javaClass); + } +} diff --git a/src/Kernel/Mnemonics/_newarray.php b/src/Kernel/Mnemonics/_newarray.php new file mode 100644 index 00000000..1cba37e5 --- /dev/null +++ b/src/Kernel/Mnemonics/_newarray.php @@ -0,0 +1,76 @@ +operands !== null) { + return $this->operands; + } + $atype = $this->readUnsignedByte(); + + return $this->operands = new Operands( + ['atype', $atype, ['atype']] + ); + } + + public function execute(): void + { + parent::execute(); + $atype = $this->getOperands()['atype']; + $count = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + $array = array_fill(0, $count, null); + + // need reference + $className = null; + + switch ($atype) { + case 4: // T_BOOLEAN + $className = 'Boolean_'; + break; + case 5: // T_CHAR + $className = 'Char_'; + break; + case 6: // T_FLOAT + $className = 'Float_'; + break; + case 7: // T_DOUBLE + $className = 'Double_'; + break; + case 8: // T_BYTE + $className = 'Byte_'; + break; + case 9: // T_SHORT + $className = 'Short_'; + break; + case 10: // T_INT + $className = 'Int_'; + break; + case 11: // T_LONG + $className = 'Long_'; + break; + default: + throw new RuntimeException('Unknown array type ' . $atype); + break; + } + + $className = '\\PHPJava\\Kernel\\Types\\' . $className; + foreach ($array as &$item) { + $item = new $className(null); + } + + $ref = new Collection($array); + + $this->pushToOperandStackByReference($ref); + } +} diff --git a/src/Kernel/Mnemonics/_nop.php b/src/Kernel/Mnemonics/_nop.php new file mode 100644 index 00000000..0e521c91 --- /dev/null +++ b/src/Kernel/Mnemonics/_nop.php @@ -0,0 +1,20 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + } +} diff --git a/src/Kernel/Mnemonics/_pop.php b/src/Kernel/Mnemonics/_pop.php new file mode 100644 index 00000000..387f6bc3 --- /dev/null +++ b/src/Kernel/Mnemonics/_pop.php @@ -0,0 +1,21 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->popStack(); + } +} diff --git a/src/Kernel/Mnemonics/_pop2.php b/src/Kernel/Mnemonics/_pop2.php new file mode 100644 index 00000000..6dc0ceb5 --- /dev/null +++ b/src/Kernel/Mnemonics/_pop2.php @@ -0,0 +1,23 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + throw new NotImplementedException(__CLASS__); + } +} diff --git a/src/Kernel/Mnemonics/_putfield.php b/src/Kernel/Mnemonics/_putfield.php new file mode 100644 index 00000000..85d52744 --- /dev/null +++ b/src/Kernel/Mnemonics/_putfield.php @@ -0,0 +1,59 @@ +operands !== null) { + return $this->operands; + } + $indexbyte = $this->readUnsignedShort(); + + return $this->operands = new Operands( + ['indexbyte', $indexbyte, ['indexbyte1', 'indexbyte2']] + ); + } + + public function execute(): void + { + parent::execute(); + $cpInfo = $this->getConstantPool(); + $cp = $cpInfo[$this->getOperands()['indexbyte']]; + $class = $cpInfo[$cp->getNameAndTypeIndex()]; + + $value = $this->popFromOperandStack(); + $fieldName = $cpInfo[$class->getNameIndex()]->getString(); + + $signature = Formatter::parseSignature($cpInfo[$class->getDescriptorIndex()]->getString()); + [$type, $typeClass] = TypeResolver::getType($signature[0]); + + if ($type === TypeResolver::IS_PRIMITIVE) { + /** + * @var Type $typeClass + */ + $value = $typeClass::get( + Normalizer::getPrimitiveValue( + $value + ) + ); + } + + /** + * @var JavaClassInterface $objectref + */ + $objectref = $this->popFromOperandStack(); + $objectref->getInvoker()->getDynamic()->getFields()->set( + $fieldName, + $value + ); + } +} diff --git a/src/Kernel/Mnemonics/_putstatic.php b/src/Kernel/Mnemonics/_putstatic.php new file mode 100644 index 00000000..54517cc9 --- /dev/null +++ b/src/Kernel/Mnemonics/_putstatic.php @@ -0,0 +1,61 @@ +operands !== null) { + return $this->operands; + } + $indexbyte = $this->readUnsignedShort(); + + return $this->operands = new Operands( + ['indexbyte', $indexbyte, ['indexbyte1', 'indexbyte2']] + ); + } + + public function execute(): void + { + parent::execute(); + $cpInfo = $this->getConstantPool(); + + $value = $this->popFromOperandStack(); + + $cp = $cpInfo[$this->getOperands()['indexbyte']]; + $class = $cpInfo[$cp->getNameAndTypeIndex()]; + + $className = $cpInfo[$cpInfo[$cp->getClassIndex()]->getClassIndex()]->getString(); + $fieldName = $cpInfo[$class->getNameIndex()]->getString(); + $signature = Formatter::parseSignature($cpInfo[$class->getDescriptorIndex()]->getString()); + [$type, $typeClass] = TypeResolver::getType($signature[0]); + + if ($type === TypeResolver::IS_PRIMITIVE) { + /** + * @var Type $typeClass + */ + $value = $typeClass::get( + Normalizer::getPrimitiveValue( + $value + ) + ); + } + + JavaClass::load($className, $this->javaClass->getOptions(), false) + ->getInvoker() + ->getStatic() + ->getFields() + ->set( + $fieldName, + $value + ); + } +} diff --git a/src/Kernel/Mnemonics/_ret.php b/src/Kernel/Mnemonics/_ret.php new file mode 100644 index 00000000..5f27ae53 --- /dev/null +++ b/src/Kernel/Mnemonics/_ret.php @@ -0,0 +1,23 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + throw new NotImplementedException(__CLASS__); + } +} diff --git a/src/Kernel/Mnemonics/_return.php b/src/Kernel/Mnemonics/_return.php new file mode 100644 index 00000000..b5a8dc3f --- /dev/null +++ b/src/Kernel/Mnemonics/_return.php @@ -0,0 +1,23 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $this->returnValue = new Void_(); + } +} diff --git a/src/Kernel/Mnemonics/_saload.php b/src/Kernel/Mnemonics/_saload.php new file mode 100644 index 00000000..06418138 --- /dev/null +++ b/src/Kernel/Mnemonics/_saload.php @@ -0,0 +1,30 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $index = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + $arrayref = $this->popFromOperandStack(); + + $this->pushToOperandStack( + $arrayref[$index] + ); + } +} diff --git a/src/Kernel/Mnemonics/_sastore.php b/src/Kernel/Mnemonics/_sastore.php new file mode 100644 index 00000000..299b63bb --- /dev/null +++ b/src/Kernel/Mnemonics/_sastore.php @@ -0,0 +1,34 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + $value = $this->popFromOperandStack(); + $index = Normalizer::getPrimitiveValue($this->popFromOperandStack()); + + /** + * @var Type $arrayref + */ + $arrayref = $this->popFromOperandStack(); + + // The value is a ref. + $arrayref[$index] = Short_::get($value); + } +} diff --git a/src/Kernel/Mnemonics/_sipush.php b/src/Kernel/Mnemonics/_sipush.php new file mode 100644 index 00000000..76c758f2 --- /dev/null +++ b/src/Kernel/Mnemonics/_sipush.php @@ -0,0 +1,29 @@ +operands !== null) { + return $this->operands; + } + $byte = $this->readShort(); + + return $this->operands = new Operands( + ['byte', $byte, ['byte1', 'byte2']] + ); + } + + public function execute(): void + { + parent::execute(); + $this->pushToOperandStack(Short_::get($this->getOperands()['byte'])); + } +} diff --git a/src/Kernel/Mnemonics/_swap.php b/src/Kernel/Mnemonics/_swap.php new file mode 100644 index 00000000..b2d9c4a5 --- /dev/null +++ b/src/Kernel/Mnemonics/_swap.php @@ -0,0 +1,23 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + throw new NotImplementedException(__CLASS__); + } +} diff --git a/src/Kernel/Mnemonics/_tableswitch.php b/src/Kernel/Mnemonics/_tableswitch.php new file mode 100644 index 00000000..9b4d1536 --- /dev/null +++ b/src/Kernel/Mnemonics/_tableswitch.php @@ -0,0 +1,57 @@ +operands !== null) { + return $this->operands; + } + + // padding data + $paddingData = 4 - (($this->getOffset()) % 4); + if ($paddingData != 4) { + $this->read($paddingData); + } + + $offsets = []; + $offsets['default'] = $default = $this->readInt(); + $lowByte = $this->readInt(); + $highByte = $this->readInt(); + + for ($i = $lowByte; $i <= $highByte; $i++) { + $offsets[$i] = $this->readInt(); + } + + return $this->operands = new Operands( + ['default', $default, ['defaultbyte1', 'defaultbyte2', 'defaultbyte3', 'defaultbyte4']], + ['lowByte', $lowByte, ['lowbyte1', 'lowbyte2', 'lowbyte3', 'lowbyte4']], + ['highByte', $lowByte, ['highByte1', 'highByte2', 'highByte3', 'highByte4']], + ['offsets', $offsets, ['jump offsets']] + ); + } + + public function execute(): void + { + parent::execute(); + $key = Normalizer::getPrimitiveValue( + $this->popFromOperandStack() + ); + + $offsets = $this->getOperands()['offsets']; + + if (isset($offsets[$key])) { + // goto PC + $this->setOffset($this->getProgramCounter() + $offsets[$key]); + return; + } + + // goto default + $this->setOffset($this->getProgramCounter() + $this->getOperands()['default']); + } +} diff --git a/src/Kernel/Mnemonics/_wide.php b/src/Kernel/Mnemonics/_wide.php new file mode 100644 index 00000000..80d448c1 --- /dev/null +++ b/src/Kernel/Mnemonics/_wide.php @@ -0,0 +1,23 @@ +operands !== null) { + return $this->operands; + } + return $this->operands = new Operands(); + } + + public function execute(): void + { + parent::execute(); + throw new NotImplementedException(__CLASS__); + } +} diff --git a/src/Kernel/Provider/DependencyInjectionProvider.php b/src/Kernel/Provider/DependencyInjectionProvider.php new file mode 100644 index 00000000..f86b17ee --- /dev/null +++ b/src/Kernel/Provider/DependencyInjectionProvider.php @@ -0,0 +1,7 @@ +entries[$key] = $value; + return $this; + } + + /** + * @throws ProviderException + */ + public function get($key, ...$arguments) + { + if (!isset($this->entries[$key])) { + throw new ProviderException('Entry does not exist.'); + } + if (is_callable($this->entries[$key])) { + return ($this->entries[$key])(...$arguments); + } + return $this->entries[$key]; + } +} diff --git a/src/Kernel/Provider/ProviderInterface.php b/src/Kernel/Provider/ProviderInterface.php new file mode 100644 index 00000000..a4dff9e6 --- /dev/null +++ b/src/Kernel/Provider/ProviderInterface.php @@ -0,0 +1,10 @@ +getAttributeData() instanceof $attributeName) { + return $attribute->getAttributeData(); + } + } + throw new UnableToFindAttributionException('Unable to find attribution ' . $attributeName); + } +} diff --git a/src/Kernel/Resolvers/ClassResolver.php b/src/Kernel/Resolvers/ClassResolver.php new file mode 100644 index 00000000..48c3edeb --- /dev/null +++ b/src/Kernel/Resolvers/ClassResolver.php @@ -0,0 +1,51 @@ + '', + '__staticConstruct' => '', + ]; + + public static function resolve(string $name): string + { + $flipped = array_flip(static::PHP_METHOD_MAP); + if (isset($flipped[$name])) { + return $flipped[$name]; + } + return $name; + } + + public static function isConstructorMethod(string $name): bool + { + $flipped = array_flip(static::PHP_METHOD_MAP); + return isset($flipped[$name]); + } +} diff --git a/src/Kernel/Resolvers/MnemonicResolver.php b/src/Kernel/Resolvers/MnemonicResolver.php new file mode 100644 index 00000000..e4980c33 --- /dev/null +++ b/src/Kernel/Resolvers/MnemonicResolver.php @@ -0,0 +1,133 @@ +getOpCode()) { + case OpCode::_iload: + case OpCode::_iload_0: + case OpCode::_iload_1: + case OpCode::_iload_2: + case OpCode::_iload_3: + case OpCode::_iconst_0: + case OpCode::_iconst_1: + case OpCode::_iconst_2: + case OpCode::_iconst_3: + case OpCode::_iconst_4: + case OpCode::_iconst_5: + case OpCode::_irem: + case OpCode::_iadd: + case OpCode::_isub: + case OpCode::_idiv: + case OpCode::_imul: + return Int_::class; + case OpCode::_fload: + case OpCode::_fload_0: + case OpCode::_fload_1: + case OpCode::_fload_2: + case OpCode::_fload_3: + return Float_::class; + case OpCode::_dload: + case OpCode::_dload_0: + case OpCode::_dload_1: + case OpCode::_dload_2: + case OpCode::_dload_3: + return Double_::class; + case OpCode::_lload: + case OpCode::_lload_0: + case OpCode::_lload_1: + case OpCode::_lload_2: + case OpCode::_lload_3: + return Long_::class; + } + + throw new ResolverException( + sprintf( + 'Unable to resolve type by opcode: 0x%X', + $operation->getOpCode() + ) + ); + } + + public static function resolveStoreByNumberAndType(int $number, string $classType): int + { + $prefix = static::getAmbiguousOperationCodePrefixByType($classType); + $suffix = ''; + if (static::inDefaultLocalStorageNumber($number)) { + $suffix = '_' . $number; + } + + $operationName = $prefix . 'store' . $suffix; + + return (int) (new OpCode()) + ->getValue( + '_' . $operationName + ); + } + + public static function resolveLoadByNumberAndType(int $number, string $classType): int + { + $prefix = static::getAmbiguousOperationCodePrefixByType($classType); + $suffix = ''; + if (static::inDefaultLocalStorageNumber($number)) { + $suffix = '_' . $number; + } + + $operationName = $prefix . 'load' . $suffix; + + return (int) (new OpCode()) + ->getValue( + '_' . $operationName + ); + } + + protected static function getAmbiguousOperationCodePrefixByType(string $classType): string + { + $prefix = 'a'; + switch ($classType) { + case Int_::class: + case Short_::class: + case Byte_::class: + case Char_::class: + $prefix = 'i'; + break; + case Long_::class: + $prefix = 'l'; + break; + case Float_::class: + $prefix = 'f'; + break; + case Double_::class: + $prefix = 'd'; + break; + } + + return $prefix; + } + + public static function isLDCOperation(int $opcode) + { + return $opcode === OpCode::_ldc + || $opcode === OpCode::_ldc_w + || $opcode === OpCode::_ldc2_w; + } + + public static function inDefaultLocalStorageNumber(int $localStorageNumber): bool + { + return $localStorageNumber <= 3; + } +} diff --git a/src/Kernel/Resolvers/SDKVersionResolver.php b/src/Kernel/Resolvers/SDKVersionResolver.php new file mode 100644 index 00000000..5f60ede7 --- /dev/null +++ b/src/Kernel/Resolvers/SDKVersionResolver.php @@ -0,0 +1,56 @@ + '1.0.2', + '45.65535' => '1.1', + '46.0' => '1.2', + '47.0' => '1.3', + '48.0' => '1.4', + '49.0' => '5.0', + '50.0' => '6', + '51.0' => '7', + '52.0' => '8', + '53.0' => '9', + '54.0' => '10', + '55.0' => '11', + '56.0' => '12', + '57.0' => '13', + '58.0' => '14', + '59.0' => '15', + '60.0' => '16', + '61.0' => '17', + '62.0' => '18', + '63.0' => '19', + ]; + + public static function resolveByVersion(string $version): array + { + $version = array_flip(static::VERSION_MAP)[$version] ?? null; + + if (isset($version)) { + [$majorVersion, $minorVersion] = explode('.', $version); + return [ + (int) $majorVersion, + (int) $minorVersion, + ]; + } + throw new UnknownVersionException('Unsupported JDK version ' . $version); + } + + /** + * @throws UnknownVersionException + */ + public static function resolve(string $version): string + { + if (isset(static::VERSION_MAP[$version])) { + return static::VERSION_MAP[$version]; + } + throw new UnknownVersionException('Unsupported JDK version ' . $version); + } +} diff --git a/src/Kernel/Resolvers/SuperClassResolver.php b/src/Kernel/Resolvers/SuperClassResolver.php new file mode 100644 index 00000000..3c2bfcec --- /dev/null +++ b/src/Kernel/Resolvers/SuperClassResolver.php @@ -0,0 +1,63 @@ +getSuperClass() === null) { + return $classes; + } + $accessor = $class->getSuperClass()->getInvoker(); + $accessor = $isDynamic ? $accessor->getDynamic() : $accessor->getStatic(); + $methods = $accessor->getMethods()->getList(); + foreach ($methods as $calleeMethodName => $callee) { + if ($methodName !== $calleeMethodName) { + continue; + } + if (!isset($classes[$methodName])) { + $classes[$methodName] = []; + } + $classes[$methodName] = array_merge( + $classes[$methodName], + $callee + ); + } + + if ($class->isSimpleClass()) { + return $classes; + } + + return static::resolveMethod( + $methodName, + $class->getSuperClass(), + $isDynamic, + $classes + ); + } +} diff --git a/src/Kernel/Resolvers/TypeResolver.php b/src/Kernel/Resolvers/TypeResolver.php new file mode 100644 index 00000000..7df7227b --- /dev/null +++ b/src/Kernel/Resolvers/TypeResolver.php @@ -0,0 +1,379 @@ + 'I', + 'float' => 'F', + 'double' => 'F', + 'string' => 'Ljava.lang.String', + 'boolean' => 'Z', + ]; + + const AMBIGUOUS_TYPES_ON_PHP = [ + Long_::class => Int_::class, + Double_::class => Float_::class, + // Char is same as Int on Java, but strict mode cannot decide Int or String on PHPJava. + // 'char' => 'int', + Byte_::class => Int_::class, + Short_::class => Int_::class, + ]; + + const SIGNATURE_MAP = [ + 'B' => Byte_::class, + 'C' => Char_::class, + 'D' => Double_::class, + 'F' => Float_::class, + 'I' => Int_::class, + 'J' => Long_::class, + 'S' => Short_::class, + 'V' => Void_::class, + 'Z' => Boolean_::class, + 'L' => 'class', + ]; + + const TYPES_MAP = [ + 'byte' => Byte_::class, + 'char' => Char_::class, + 'double' => Double_::class, + 'float' => Float_::class, + 'int' => Int_::class, + 'long' => Long_::class, + 'short' => Short_::class, + 'boolean' => Boolean_::class, + 'void' => Void_::class, + ]; + + const VERIFICATION_TYPE_TAG_MAPS = [ + Int_::class => VerificationTypeTag::ITEM_Integer, + Float_::class => VerificationTypeTag::ITEM_Float, + Double_::class => VerificationTypeTag::ITEM_Double, + Char_::class => VerificationTypeTag::ITEM_Integer, + Short_::class => VerificationTypeTag::ITEM_Integer, + Long_::class => VerificationTypeTag::ITEM_Long, + null => VerificationTypeTag::ITEM_Null, + ]; + + const PHP_SCALAR_MAP = [ + // [ TypeClass, Instantiation ] + 'float' => [Float_::class, false], + 'double' => [Float_::class, false], + 'integer' => [Int_::class, false], + 'boolean' => [Boolean_::class, false], + ]; + + const PHP_TO_JAVA_MAP = [ + 'integer' => Int_::class, + 'string' => String_::class, + 'float' => Double_::class, + 'double' => Double_::class, + ]; + + /** + * @throws TypeException + */ + public static function getMappedSignatureType(string $signature): string + { + if (isset(static::SIGNATURE_MAP[$signature])) { + return static::SIGNATURE_MAP[$signature]; + } + throw new TypeException('Passed undefined signature ' . $signature); + } + + public static function resolveSignatureByType(string $classType): string + { + $signatureMap = array_flip(static::SIGNATURE_MAP); + $signatureName = $signatureMap[$classType] ?? null; + + if ($signatureName === null) { + throw new TypeException('Unknown signature: ' . $classType); + } + + return $signatureName; + } + + public static function resolve(string $type): string + { + $flipped = array_flip(static::SIGNATURE_MAP); + if (isset($flipped[$type])) { + if (!(GlobalOptions::get('strict') ?? Runtime::STRICT)) { + $ambiguousType = static::AMBIGUOUS_TYPES_ON_PHP[$type] ?? $type; + return $flipped[$ambiguousType] ?? ('L' . $ambiguousType); + } + return $flipped[$type]; + } + return 'L' . $type; + } + + public static function extractPrimitiveValueFromType(Type $value) + { + $extractedValue = (string) $value->getValue(); + if ($value instanceof Int_ || $value instanceof Long_ || $value instanceof Byte_) { + return (int) $extractedValue; + } + if ($value instanceof Float_ || $value instanceof Double_) { + return (float) $extractedValue; + } + if ($value instanceof Boolean_) { + return $extractedValue === 'true' ? true : false; + } + + return $extractedValue; + } + + /** + * @param array $signatureArray a formatted signature array + * @throws TypeException + * @return string[] + */ + public static function getType(array $signatureArray): array + { + $typeName = $type = $signatureArray['type']; + $signatureType = static::IS_PRIMITIVE; + if (!static::isPrimitive($type)) { + $signatureType = static::IS_CLASS; + } + if ($signatureArray['dimensions_of_array'] > 0) { + $signatureType = static::IS_ARRAY; + } + if ($typeName === null) { + throw new TypeException('Unknown type: ' . $type); + } + return [ + $signatureType, + $typeName, + $signatureArray['dimensions_of_array'], + ]; + } + + /** + * @return null|mixed|string + */ + public static function resolveFromPHPType($value) + { + $type = gettype($value); + $type = static::PHP_TYPE_MAP[$type][0]; + if ($type === 'L') { + return substr(static::PHP_TYPE_MAP[$type], 1); + } + return static::SIGNATURE_MAP[$type] ?? null; + } + + public static function convertJavaTypeSimplyForPHP(string $type): string + { + return static::AMBIGUOUS_TYPES_ON_PHP[$type] ?? $type; + } + + /** + * @throws TypeException + * @return (int|string)[] + */ + public static function convertPHPtoJava($arguments, string $defaultJavaArgumentType = String_::class): array + { + $phpType = gettype($arguments); + $dimensionsOfArray = 0; + if ($phpType === 'array') { + $dimensionsOfArray++; + $getNestedValues = []; + foreach ($arguments as $argument) { + $getNestedValues[] = static::convertPHPtoJava($argument, $defaultJavaArgumentType); + } + if (empty($getNestedValues)) { + $flipped = array_flip(static::PHP_TYPE_MAP); + $resolveType = static::SIGNATURE_MAP[static::resolve($defaultJavaArgumentType)[0]]; + if (!static::isPrimitive($resolveType)) { + return [ + 'type' => $defaultJavaArgumentType, + 'dimensions_of_array' => $dimensionsOfArray, + ]; + } + return [ + 'type' => $resolveType, + 'dimensions_of_array' => $dimensionsOfArray, + ]; + } + $firstParameter = $getNestedValues[0]; + + // TODO: Validate Parameters + $firstParameter['dimensions_of_array'] += $dimensionsOfArray; + return $firstParameter; + } + if ($phpType === 'object') { + if ($arguments instanceof JavaClassInterface) { + return [ + 'type' => Formatter::convertPHPNamespacesToJava( + $arguments->getClassName() + ), + 'dimensions_of_array' => $dimensionsOfArray, + ]; + } + if ($arguments instanceof PrimitiveValueInterface) { + return [ + 'type' => get_class($arguments), + 'dimensions_of_array' => $dimensionsOfArray, + ]; + } + if ($arguments instanceof Collection) { + return [ + 'type' => $arguments->getType($defaultJavaArgumentType), + 'dimensions_of_array' => $dimensionsOfArray, + ]; + } + throw new TypeException(get_class($arguments) . ' does not supported to convert to Java\'s argument.'); + } + $resolveType = static::SIGNATURE_MAP[static::PHP_TYPE_MAP[$phpType][0]] ?? null; + + if (!static::isPrimitive($resolveType)) { + return [ + 'type' => substr(static::PHP_TYPE_MAP[$phpType], 1), + 'dimensions_of_array' => $dimensionsOfArray, + ]; + } + + return [ + 'type' => $resolveType, + 'dimensions_of_array' => $dimensionsOfArray, + ]; + } + + /** + * @throws TypeException + * @throws \ReflectionException + */ + public static function compare(JavaClassInterface $javaClass, string $a, string $b): bool + { + if ($a === $b) { + return true; + } + + $a = static::getExtendedClasses($javaClass, $a); + $b = static::getExtendedClasses($javaClass, $b); + + $resultClassesComparison = []; + $resultInterfacesComparison = []; + for ($i = 0, $size = max(count($a), count($b)); $i < $size; $i++) { + if (!isset($a[$i], $b[$i])) { + // No match absolutely. + return false; + } + + [$aClasses, $aInterfaces] = $a[$i]; + [$bClasses, $bInterfaces] = $b[$i]; + + $resultClassesComparison[] = count(array_intersect($aClasses, $bClasses)) > 0; + $resultInterfacesComparison[] = count(array_intersect($aInterfaces, $bInterfaces)) > 0; + } + + return !in_array(false, $resultClassesComparison, true) || + !in_array(false, $resultInterfacesComparison, true); + } + + /** + * @throws TypeException + * @throws \ReflectionException + */ + public static function getExtendedClasses(JavaClassInterface $javaClass, string $class): array + { + static $loadedExtendedRoots = []; + $result = []; + foreach (Formatter::parseSignature($class) as $signature) { + if (static::isPrimitive($signature['type'])) { + $result[] = [[$signature['type']], []]; + continue; + } + + $javaClass = JavaClass::load($signature['type'], [], false); + $extendedClasses = $javaClass->getDefinedExtendedClasses(); + $interfaces = $javaClass->getDefinedInterfaceClasses(); + $result[] = $loadedExtendedRoots[$signature['type']] = [$extendedClasses, $interfaces]; + } + + array_walk_recursive($result, function (&$className) { + $className = Formatter::convertPHPNamespacesToJava((string) $className); + }); + + return $result; + } + + /** + * @throws TypeException + * @return Collection|Type + */ + public static function convertPHPTypeToJavaType($value) + { + $type = gettype($value); + if ((static::PHP_SCALAR_MAP[$type] ?? null) !== null) { + /** + * @var Type $typeClass + * @var bool $instantiation + */ + [$typeClass, $instantiation] = static::PHP_SCALAR_MAP[$type]; + if ($instantiation) { + return new $typeClass($value); + } + return $typeClass::get($value); + } + + switch ($type) { + case 'string': + return JavaClass::load('java.lang.String') + ->getInvoker() + ->construct($value) + ->getJavaClass(); + case 'object': + return $value; + case 'array': + $collectionData = []; + foreach ($value as $item) { + $collectionData[] = static::convertPHPTypeToJavaType($item); + } + return new Collection($collectionData); + } + throw new TypeException('Cannot convert your definition'); + } + + public static function isPrimitive(string $value): bool + { + return in_array( + Formatter::convertStringifiedPrimitiveTypeToKernelType($value), + array_values(static::TYPES_MAP), + true + ); + } + + public static function getVerificationTypeTagByKernelType(string $kernelType): array + { + $verificationTypeTag = static::VERIFICATION_TYPE_TAG_MAPS[$kernelType] ?? null; + if ($verificationTypeTag !== null) { + return [$verificationTypeTag, null]; + } + return [VerificationTypeTag::ITEM_Object, $kernelType]; + } +} diff --git a/src/Kernel/Structures/Annotations/Annotation.php b/src/Kernel/Structures/Annotations/Annotation.php new file mode 100644 index 00000000..aaf7a18e --- /dev/null +++ b/src/Kernel/Structures/Annotations/Annotation.php @@ -0,0 +1,43 @@ +typeIndex = $this->readUnsignedShort(); + $this->numElementValuePairs = $this->readUnsignedShort(); + + for ($i = 0; $i < $this->numElementValuePairs; $i++) { + $elementNameIndex = $this->readUnsignedShort(); + + $this->elementValuePairs[] = [ + 'element_name_index' => $elementNameIndex, + 'element_value_pair' => (new ElementValue($this->reader)) + ->setConstantPool($this->getConstantPool()) + ->setDebugTool($this->getDebugTool()) + ->execute(), + ]; + } + } +} diff --git a/src/Kernel/Structures/Annotations/AnnotationInterface.php b/src/Kernel/Structures/Annotations/AnnotationInterface.php new file mode 100644 index 00000000..58ff44e1 --- /dev/null +++ b/src/Kernel/Structures/Annotations/AnnotationInterface.php @@ -0,0 +1,8 @@ +tag = chr($this->readByte()); + switch ($this->tag) { + case 'B': // const_value_index + case 'C': + case 'D': + case 'F': + case 'I': + case 'J': + case 'S': + case 'Z': + case 's': + $this->value = $this->readUnsignedShort(); + break; + case 'e': // enum_const_value + $this->value = [ + 'type_name_index' => $this->readUnsignedShort(), + 'const_name_index' => $this->readUnsignedShort(), + ]; + break; + case 'c': // class_info_index + $this->value = $this->readUnsignedShort(); + break; + case '@': // annotation_value + $this->value = new Annotation($this->reader); + $this->value->setConstantPool($this->getConstantPool()); + $this->value->setDebugTool($this->getDebugTool()); + $this->value->execute(); + break; + case '[': // array_value + $this->value = [ + 'num_values' => $this->readUnsignedShort(), + 'values' => [], + ]; + for ($i = 0; $i < $this->value['num_values']; $i++) { + $value = new static($this->reader); + $value->setConstantPool($this->getConstantPool()); + $value->setDebugTool($this->getDebugTool()); + $value->execute(); + $this->value['values'][] = $value; + } + break; + default: + throw new RuntimeException('Invalid tag "' . $this->tag . '" in element_value structure.'); + } + } + + public function getValue() + { + return $this->value; + } +} diff --git a/src/Kernel/Structures/BootstrapMethod.php b/src/Kernel/Structures/BootstrapMethod.php new file mode 100644 index 00000000..3dc66f5d --- /dev/null +++ b/src/Kernel/Structures/BootstrapMethod.php @@ -0,0 +1,43 @@ +bootstrapMethodRef = $this->readUnsignedShort(); + $this->numBootstrapArguments = $this->readUnsignedShort(); + + $cp = $this->getConstantPool(); + for ($i = 0; $i < $this->numBootstrapArguments; $i++) { + $this->bootstrapArguments[] = $cp[$this->readUnsignedShort()]; + } + } + + public function getBootstrapMethodRef(): int + { + return $this->bootstrapMethodRef; + } + + public function getBootstrapArguments(): array + { + return $this->bootstrapArguments; + } +} diff --git a/src/Kernel/Structures/ClassInfo.php b/src/Kernel/Structures/ClassInfo.php new file mode 100644 index 00000000..a4b86adb --- /dev/null +++ b/src/Kernel/Structures/ClassInfo.php @@ -0,0 +1,25 @@ +classIndex = $this->readUnsignedShort(); + } + + public function getClassIndex(): int + { + return $this->classIndex; + } +} diff --git a/src/Kernel/Structures/DoubleInfo.php b/src/Kernel/Structures/DoubleInfo.php new file mode 100644 index 00000000..a32851ca --- /dev/null +++ b/src/Kernel/Structures/DoubleInfo.php @@ -0,0 +1,43 @@ +highBytes = $this->readUnsignedInt(); + $this->lowBytes = $this->readUnsignedInt(); + } + + public function getBytes(): float + { + if ($this->realByte) { + return $this->realByte; + } + $bits = ($this->highBytes << 32) + $this->lowBytes; + $s = ($bits >> 63) == 0 ? 1 : -1; + $e = ($bits >> 52) & 0x7ff; + $m = ($e == 0) ? (($bits & 0xfffffffffffff) << 1) : ($bits & 0xfffffffffffff) | 0x10000000000000; + return $this->realByte = ($s * $m * pow(2, $e - 1075)); + } +} diff --git a/src/Kernel/Structures/ExceptionTable.php b/src/Kernel/Structures/ExceptionTable.php new file mode 100644 index 00000000..22ee17de --- /dev/null +++ b/src/Kernel/Structures/ExceptionTable.php @@ -0,0 +1,58 @@ +startPc = $this->readUnsignedShort(); + $this->endPc = $this->readUnsignedShort(); + $this->handlerPc = $this->readUnsignedShort(); + $this->catchType = $this->readUnsignedShort(); + } + + public function getStartPc(): int + { + return $this->startPc; + } + + public function getEndPc(): int + { + return $this->endPc; + } + + public function getHandlerPc(): int + { + return $this->handlerPc; + } + + public function getCatchType(): int + { + return $this->catchType; + } +} diff --git a/src/Kernel/Structures/FieldInfo.php b/src/Kernel/Structures/FieldInfo.php new file mode 100644 index 00000000..f5e3944e --- /dev/null +++ b/src/Kernel/Structures/FieldInfo.php @@ -0,0 +1,74 @@ +accessFlag = $this->readUnsignedShort(); + $this->nameIndex = $this->readUnsignedShort(); + $this->descriptorIndex = $this->readUnsignedShort(); + $this->attributeCount = $this->readUnsignedShort(); + for ($i = 0; $i < $this->attributeCount; $i++) { + $attribute = new \PHPJava\Kernel\Attributes\AttributeInfo($this->reader); + $attribute->setConstantPool($this->getConstantPool()); + $attribute->setDebugTool($this->getDebugTool()); + $attribute->execute(); + + $this->attributes[] = $attribute; + } + } + + public function getAccessFlag(): int + { + return $this->accessFlag; + } + + public function getNameIndex(): int + { + return $this->nameIndex; + } + + public function getDescriptorIndex(): int + { + return $this->descriptorIndex; + } + + /** + * @var \PHPJava\Kernel\Attributes\AttributeInfo[] + */ + public function getAttributes(): array + { + return $this->attributes; + } +} diff --git a/src/Kernel/Structures/FieldrefInfo.php b/src/Kernel/Structures/FieldrefInfo.php new file mode 100644 index 00000000..ddd484b3 --- /dev/null +++ b/src/Kernel/Structures/FieldrefInfo.php @@ -0,0 +1,36 @@ +classIndex = $this->readUnsignedShort(); + $this->nameAndTypeIndex = $this->readUnsignedShort(); + } + + public function getClassIndex(): int + { + return $this->classIndex; + } + + public function getNameAndTypeIndex(): int + { + return $this->nameAndTypeIndex; + } +} diff --git a/src/Kernel/Structures/FloatInfo.php b/src/Kernel/Structures/FloatInfo.php new file mode 100644 index 00000000..ac8c9f73 --- /dev/null +++ b/src/Kernel/Structures/FloatInfo.php @@ -0,0 +1,37 @@ +bytes = $this->readUnsignedInt(); + } + + public function getBytes(): float + { + if ($this->realByte) { + return $this->realByte; + } + $bits = $this->bytes; + $s = ($bits >> 31) == 0 ? 1 : -1; + $e = ($bits >> 23) & 0xff; + $m = ($e == 0) ? (($bits & 0x7fffff) << 1) : ($bits & 0x7fffff) | 0x800000; + return $this->realByte = ($s * $m * pow(2, $e - 150)); + } +} diff --git a/src/Kernel/Structures/FreezableInterface.php b/src/Kernel/Structures/FreezableInterface.php new file mode 100644 index 00000000..8f94372b --- /dev/null +++ b/src/Kernel/Structures/FreezableInterface.php @@ -0,0 +1,8 @@ +innerClassInfoIndex = $this->readUnsignedShort(); + $this->outerClassInfoIndex = $this->readUnsignedShort(); + $this->innerNameIndex = $this->readUnsignedShort(); + $this->innerClassAccessFlag = $this->readUnsignedShort(); + } + + public function getInnerClassInfoIndex(): int + { + return $this->innerClassInfoIndex; + } + + public function getOuterClassInfoIndex(): int + { + return $this->outerClassInfoIndex; + } + + public function getInnerNameIndex(): int + { + return $this->innerNameIndex; + } + + public function getInnerClassAccessFlag(): int + { + return $this->innerClassAccessFlag; + } +} diff --git a/src/Kernel/Structures/IntegerInfo.php b/src/Kernel/Structures/IntegerInfo.php new file mode 100644 index 00000000..8455e70d --- /dev/null +++ b/src/Kernel/Structures/IntegerInfo.php @@ -0,0 +1,25 @@ +bytes = $this->readUnsignedInt(); + } + + public function getBytes(): int + { + return $this->bytes; + } +} diff --git a/src/Kernel/Structures/InterfaceMethodrefInfo.php b/src/Kernel/Structures/InterfaceMethodrefInfo.php new file mode 100644 index 00000000..b470bcda --- /dev/null +++ b/src/Kernel/Structures/InterfaceMethodrefInfo.php @@ -0,0 +1,36 @@ +classIndex = $this->readUnsignedShort(); + $this->nameAndTypeIndex = $this->readUnsignedShort(); + } + + public function getClassIndex(): int + { + return $this->classIndex; + } + + public function getNameAndTypeIndex(): int + { + return $this->nameAndTypeIndex; + } +} diff --git a/src/Kernel/Structures/InvokeDynamicInfo.php b/src/Kernel/Structures/InvokeDynamicInfo.php new file mode 100644 index 00000000..ffd3dfaa --- /dev/null +++ b/src/Kernel/Structures/InvokeDynamicInfo.php @@ -0,0 +1,36 @@ +bootstrapMethodAttrIndex = $this->readUnsignedShort(); + $this->nameAndTypeIndex = $this->readUnsignedShort(); + } + + public function getBootstrapMethodAttrIndex(): int + { + return $this->bootstrapMethodAttrIndex; + } + + public function getNameAndTypeIndex(): int + { + return $this->nameAndTypeIndex; + } +} diff --git a/src/Kernel/Structures/LineNumberTable.php b/src/Kernel/Structures/LineNumberTable.php new file mode 100644 index 00000000..63f2b0da --- /dev/null +++ b/src/Kernel/Structures/LineNumberTable.php @@ -0,0 +1,36 @@ +startPc = $this->readUnsignedShort(); + $this->lineNumber = $this->readUnsignedShort(); + } + + public function getStartPc(): int + { + return $this->startPc; + } + + public function getLineNumber(): int + { + return $this->lineNumber; + } +} diff --git a/src/Kernel/Structures/LocalVariableTable.php b/src/Kernel/Structures/LocalVariableTable.php new file mode 100644 index 00000000..0027e68d --- /dev/null +++ b/src/Kernel/Structures/LocalVariableTable.php @@ -0,0 +1,44 @@ +startPc = $this->readUnsignedShort(); + $this->length = $this->readUnsignedShort(); + $this->nameIndex = $this->readUnsignedShort(); + $this->descriptorIndex = $this->readUnsignedShort(); + $this->index = $this->readUnsignedShort(); + } +} diff --git a/src/Kernel/Structures/LocalVariableTypeTable.php b/src/Kernel/Structures/LocalVariableTypeTable.php new file mode 100644 index 00000000..ea2bfa21 --- /dev/null +++ b/src/Kernel/Structures/LocalVariableTypeTable.php @@ -0,0 +1,44 @@ +startPc = $this->readUnsignedShort(); + $this->length = $this->readUnsignedShort(); + $this->nameIndex = $this->readUnsignedShort(); + $this->descriptorIndex = $this->readUnsignedShort(); + $this->index = $this->readUnsignedShort(); + } +} diff --git a/src/Kernel/Structures/LongInfo.php b/src/Kernel/Structures/LongInfo.php new file mode 100644 index 00000000..76cc82aa --- /dev/null +++ b/src/Kernel/Structures/LongInfo.php @@ -0,0 +1,25 @@ +bytes = $this->readUnsignedLong(); + } + + public function getBytes(): int + { + return $this->bytes; + } +} diff --git a/src/Kernel/Structures/MethodHandleInfo.php b/src/Kernel/Structures/MethodHandleInfo.php new file mode 100644 index 00000000..c2d9f2f8 --- /dev/null +++ b/src/Kernel/Structures/MethodHandleInfo.php @@ -0,0 +1,36 @@ +referenceKind = $this->readUnsignedByte(); + $this->referenceIndex = $this->readUnsignedShort(); + } + + public function getReferenceKind(): int + { + return $this->referenceKind; + } + + public function getReferenceIndex(): int + { + return $this->referenceIndex; + } +} diff --git a/src/Kernel/Structures/MethodInfo.php b/src/Kernel/Structures/MethodInfo.php new file mode 100644 index 00000000..df11a80a --- /dev/null +++ b/src/Kernel/Structures/MethodInfo.php @@ -0,0 +1,74 @@ +accessFlag = $this->readUnsignedShort(); + $this->nameIndex = $this->readUnsignedShort(); + $this->descriptorIndex = $this->readUnsignedShort(); + $this->attributeCount = $this->readUnsignedShort(); + for ($i = 0; $i < $this->attributeCount; $i++) { + $attribute = new \PHPJava\Kernel\Attributes\AttributeInfo($this->reader); + $attribute->setConstantPool($this->getConstantPool()); + $attribute->setDebugTool($this->getDebugTool()); + $attribute->execute(); + + $this->attributes[] = $attribute; + } + } + + public function getAccessFlag(): int + { + return $this->accessFlag; + } + + public function getNameIndex(): int + { + return $this->nameIndex; + } + + public function getDescriptorIndex(): int + { + return $this->descriptorIndex; + } + + /** + * @return \PHPJava\Kernel\Attributes\AttributeInfo[] + */ + public function getAttributes(): array + { + return $this->attributes; + } +} diff --git a/src/Kernel/Structures/MethodTypeInfo.php b/src/Kernel/Structures/MethodTypeInfo.php new file mode 100644 index 00000000..7cfeb430 --- /dev/null +++ b/src/Kernel/Structures/MethodTypeInfo.php @@ -0,0 +1,25 @@ +descriptorIndex = $this->readUnsignedShort(); + } + + public function getDescriptorIndex(): int + { + return $this->descriptorIndex; + } +} diff --git a/src/Kernel/Structures/MethodrefInfo.php b/src/Kernel/Structures/MethodrefInfo.php new file mode 100644 index 00000000..e7a823bf --- /dev/null +++ b/src/Kernel/Structures/MethodrefInfo.php @@ -0,0 +1,36 @@ +classIndex = $this->readUnsignedShort(); + $this->nameAndTypeIndex = $this->readUnsignedShort(); + } + + public function getClassIndex(): int + { + return $this->classIndex; + } + + public function getNameAndTypeIndex(): int + { + return $this->nameAndTypeIndex; + } +} diff --git a/src/Kernel/Structures/NameAndTypeInfo.php b/src/Kernel/Structures/NameAndTypeInfo.php new file mode 100644 index 00000000..b8cdc765 --- /dev/null +++ b/src/Kernel/Structures/NameAndTypeInfo.php @@ -0,0 +1,36 @@ +nameIndex = $this->readUnsignedShort(); + $this->descriptorIndex = $this->readUnsignedShort(); + } + + public function getNameIndex(): int + { + return $this->nameIndex; + } + + public function getDescriptorIndex(): int + { + return $this->descriptorIndex; + } +} diff --git a/src/Kernel/Structures/ParameterAnnotation.php b/src/Kernel/Structures/ParameterAnnotation.php new file mode 100644 index 00000000..b3b69bc5 --- /dev/null +++ b/src/Kernel/Structures/ParameterAnnotation.php @@ -0,0 +1,34 @@ +numAnnotations = $this->readUnsignedShort(); + for ($i = 0; $i < $this->numAnnotations; $i++) { + $annotation = new Annotation($this->reader); + $annotation->setConstantPool($this->getConstantPool()); + $annotation->setDebugTool($this->getDebugTool()); + $annotation->execute(); + $this->annotations[] = $annotation; + } + } +} diff --git a/src/Kernel/Structures/StackMapFrameInfo.php b/src/Kernel/Structures/StackMapFrameInfo.php new file mode 100644 index 00000000..dc88acac --- /dev/null +++ b/src/Kernel/Structures/StackMapFrameInfo.php @@ -0,0 +1,49 @@ +frameType = $this->readUnsignedByte(); + // back by frametype + $this->reader->getReader()->seek(-1); + + if ($this->frameType >= 0 && $this->frameType <= 63) { + $this->frame = new \PHPJava\Kernel\Frames\SameFrame($this->reader); + } elseif ($this->frameType >= 64 && $this->frameType <= 127) { + $this->frame = new \PHPJava\Kernel\Frames\SameLocals1StackItemFrame($this->reader); + } elseif ($this->frameType == 247) { + $this->frame = new \PHPJava\Kernel\Frames\SameLocals1StackItemFrameExtended($this->reader); + } elseif ($this->frameType >= 248 && $this->frameType <= 250) { + $this->frame = new \PHPJava\Kernel\Frames\ChopFrame($this->reader); + } elseif ($this->frameType == 251) { + $this->frame = new \PHPJava\Kernel\Frames\SameFrameExtended($this->reader); + } elseif ($this->frameType >= 252 && $this->frameType <= 254) { + $this->frame = new \PHPJava\Kernel\Frames\AppendFrame($this->reader); + } elseif ($this->frameType == 255) { + $this->frame = new \PHPJava\Kernel\Frames\FullFrame($this->reader); + } + $this->frame->execute(); + } + + public function getFrame(): StructureInterface + { + return $this->frame; + } +} diff --git a/src/Kernel/Structures/StringInfo.php b/src/Kernel/Structures/StringInfo.php new file mode 100644 index 00000000..3f4afdb6 --- /dev/null +++ b/src/Kernel/Structures/StringInfo.php @@ -0,0 +1,25 @@ +stringIndex = $this->readUnsignedShort(); + } + + public function getStringIndex(): int + { + return $this->stringIndex; + } +} diff --git a/src/Kernel/Structures/StructureInterface.php b/src/Kernel/Structures/StructureInterface.php new file mode 100644 index 00000000..3a51b346 --- /dev/null +++ b/src/Kernel/Structures/StructureInterface.php @@ -0,0 +1,8 @@ +length = $this->readUnsignedShort(); + for ($i = 0; $i < $this->length; $i++) { + $this->string .= chr($this->readUnsignedByte()); + } + $this->stringObject = JavaClass::load('java.lang.String'); + $this->stringObject->getInvoker()->construct($this->string); + } + + public function getLength(): int + { + return $this->length; + } + + public function enableWrite(bool $enable): self + { + if (!$this->isFrozen) { + $this->isWritable = $enable; + } + return $this; + } + + public function freeze(): void + { + $this->isFrozen = true; + $this->enableWrite(false); + } + + public function getString(): string + { + return $this->string; + } + + public function getStringObject(): JavaClass + { + return $this->stringObject; + } + + public function setStringObject(JavaClass $stringObject): self + { + // TODO: Add validation that checks $stringObject is a java.lang.String. + if ($this->isWritable) { + $this->stringObject = $stringObject; + $this->freeze(); + } + return $this; + } + + public function __toString(): string + { + return $this->getString(); + } +} diff --git a/src/Kernel/Structures/VerificationTypeInfo.php b/src/Kernel/Structures/VerificationTypeInfo.php new file mode 100644 index 00000000..9d237e2b --- /dev/null +++ b/src/Kernel/Structures/VerificationTypeInfo.php @@ -0,0 +1,62 @@ +tag = $this->readUnsignedByte(); + + // back by tag + $this->reader->getReader()->seek(-1); + + switch ($this->tag) { + case VerificationTypeTag::ITEM_Top: + $this->variableInfo = new \PHPJava\Kernel\Variables\TopVariableInfo($this->reader); + break; + case VerificationTypeTag::ITEM_Integer: + $this->variableInfo = new \PHPJava\Kernel\Variables\IntegerVariableInfo($this->reader); + break; + case VerificationTypeTag::ITEM_Float: + $this->variableInfo = new \PHPJava\Kernel\Variables\FloatVariableInfo($this->reader); + break; + case VerificationTypeTag::ITEM_Long: + $this->variableInfo = new \PHPJava\Kernel\Variables\LongVariableInfo($this->reader); + break; + case VerificationTypeTag::ITEM_Double: + $this->variableInfo = new \PHPJava\Kernel\Variables\DoubleVariableInfo($this->reader); + break; + case VerificationTypeTag::ITEM_Null: + $this->variableInfo = new \PHPJava\Kernel\Variables\NullVariableInfo($this->reader); + break; + case VerificationTypeTag::ITEM_UninitializedThis: + $this->variableInfo = new \PHPJava\Kernel\Variables\UninitializedThisVariableInfo($this->reader); + break; + case VerificationTypeTag::ITEM_Object: + $this->variableInfo = new \PHPJava\Kernel\Variables\ObjectVariableInfo($this->reader); + break; + case VerificationTypeTag::ITEM_Uninitialized: + $this->variableInfo = new \PHPJava\Kernel\Variables\UninitializedVariableInfo($this->reader); + break; + } + $this->variableInfo->execute(); + } +} diff --git a/src/Kernel/Types/Array_/Collection.php b/src/Kernel/Types/Array_/Collection.php new file mode 100644 index 00000000..296c8472 --- /dev/null +++ b/src/Kernel/Types/Array_/Collection.php @@ -0,0 +1,109 @@ +data = $data; + } + + public function setType(string $type = null): self + { + $this->type = $type; + return $this; + } + + public function getType($default = null): ?string + { + if (!isset($this->data[0])) { + return $this->type ?? $default; + } + return TypeResolver::resolveFromPHPType( + Normalizer::getPrimitiveValue($this->data[0]) + ) ?? $this->type ?? $default; + } + + public function __toString(): string + { + return implode($this->data); + } + + public function toArray(): array + { + return $this->data; + } + + /** + * @param int $offset + * @return bool + */ + public function offsetExists($offset) + { + return isset($this->data[$offset]); + } + + /** + * @param int $offset + */ + public function offsetGet($offset) + { + if (!$this->offsetExists($offset)) { + throw new ArrayIndexOutOfBoundsException($offset); + } + return $this->data[$offset]; + } + + /** + * @param int $offset + */ + public function offsetUnset($offset) + { + unset($this->data[$offset]); + } + + public function offsetSet($offset, $value) + { + if ($offset === null) { + $this->data[] = $value; + return; + } + $this->data[$offset] = $value; + } + + /** + * @return int + */ + public function count() + { + return count($this->data); + } + + /** + * @return \ArrayIterator + */ + public function getIterator() + { + return new \ArrayIterator($this->data); + } +} diff --git a/src/Kernel/Types/Array_/Generator.php b/src/Kernel/Types/Array_/Generator.php new file mode 100644 index 00000000..83d9b2fb --- /dev/null +++ b/src/Kernel/Types/Array_/Generator.php @@ -0,0 +1,15 @@ +value + ? static::TRUE + : static::FALSE; + } +} diff --git a/src/Kernel/Types/Byte_.php b/src/Kernel/Types/Byte_.php new file mode 100644 index 00000000..5371bdef --- /dev/null +++ b/src/Kernel/Types/Byte_.php @@ -0,0 +1,32 @@ += static::MIN && $value <= static::MAX; + } + + protected static function filter($value) + { + return (int) $value; + } +} diff --git a/src/Kernel/Types/Char_.php b/src/Kernel/Types/Char_.php new file mode 100644 index 00000000..70a12fc2 --- /dev/null +++ b/src/Kernel/Types/Char_.php @@ -0,0 +1,44 @@ += static::MIN && $value <= static::MAX; + } + + protected static function filter($value) + { + if (ctype_alpha($value) && strlen((string) $value) === 1) { + return ord($value); + } + return $value; + } + + public function __toString(): string + { + return json_decode(sprintf('"\\u%04X"', $this->value)); + } +} diff --git a/src/Kernel/Types/Double_.php b/src/Kernel/Types/Double_.php new file mode 100644 index 00000000..376abacc --- /dev/null +++ b/src/Kernel/Types/Double_.php @@ -0,0 +1,35 @@ +abs(); + + return $value->isEqualTo('0') || ( + $value->isGreaterThan(static::MIN) && + $value->isLessThan(static::MAX) + ); + } + + protected static function filter($value) + { + return (string) $value; + } +} diff --git a/src/Kernel/Types/Float_.php b/src/Kernel/Types/Float_.php new file mode 100644 index 00000000..04268488 --- /dev/null +++ b/src/Kernel/Types/Float_.php @@ -0,0 +1,29 @@ += static::MIN && $value <= static::MAX); + } + + protected static function filter($value) + { + return (float) $value; + } +} diff --git a/src/Kernel/Types/Int_.php b/src/Kernel/Types/Int_.php new file mode 100644 index 00000000..7564edbc --- /dev/null +++ b/src/Kernel/Types/Int_.php @@ -0,0 +1,40 @@ +> 32; + if (!ctype_digit((string) abs($value))) { + return false; + } + + return $value >= static::MIN && $value <= static::MAX; + } + + protected static function filter($value) + { + if (ctype_alpha($value) && strlen((string) $value) === 1) { + return ord($value); + } + + return ($value << 32) >> 32; + } +} diff --git a/src/Kernel/Types/Long_.php b/src/Kernel/Types/Long_.php new file mode 100644 index 00000000..354466e5 --- /dev/null +++ b/src/Kernel/Types/Long_.php @@ -0,0 +1,35 @@ += static::MIN && $value <= static::MAX; + } + + protected static function filter($value) + { + return (int) $value; + } +} diff --git a/src/Kernel/Types/PrimitiveValueInterface.php b/src/Kernel/Types/PrimitiveValueInterface.php new file mode 100644 index 00000000..c13ecd20 --- /dev/null +++ b/src/Kernel/Types/PrimitiveValueInterface.php @@ -0,0 +1,7 @@ += static::MIN && + $value <= static::MAX; + } + + protected static function filter($value) + { + return (int) $value; + } +} diff --git a/src/Kernel/Types/Type.php b/src/Kernel/Types/Type.php new file mode 100644 index 00000000..001850fb --- /dev/null +++ b/src/Kernel/Types/Type.php @@ -0,0 +1,110 @@ +value = static::filter( + ($value instanceof self) + ? $value->getValue() + : $value + ); + } + + public function __debugInfo() + { + return [ + 'value' => $this->value, + ]; + } + + /** + * @param null|mixed $value + * @throws TypeException + */ + public static function get($value = null): self + { + static $instantiated = null; + if ($value === null) { + if (static::DEFAULT_VALUE === null) { + throw new TypeException('The type has not default value.'); + } + return static::get( + static::DEFAULT_VALUE + ); + } + if (is_object($value)) { + if ($value instanceof static) { + return $value; + } + $identity = spl_object_hash($value); + } else { + $identity = (string) $value; + } + return $instantiated[$identity] = $instantiated[$identity] ?? new static($value); + } + + public function getValue() + { + return $this->value; + } + + public function getTypeNameInJava(): string + { + return $this->nameInJava; + } + + public function getTypeNameInPHP(): string + { + return $this->nameInPHP; + } + + public function __toString(): string + { + return (string) $this->getValue(); + } + + public static function isValid($value): bool + { + throw new TypeException('Not implemented type validation.'); + } + + protected static function filter($value) + { + return $value; + } +} diff --git a/src/Kernel/Types/Void_.php b/src/Kernel/Types/Void_.php new file mode 100644 index 00000000..184cc099 --- /dev/null +++ b/src/Kernel/Types/Void_.php @@ -0,0 +1,15 @@ +tag = $this->readUnsignedByte(); + } +} diff --git a/src/Kernel/Variables/FloatVariableInfo.php b/src/Kernel/Variables/FloatVariableInfo.php new file mode 100644 index 00000000..547c7aa3 --- /dev/null +++ b/src/Kernel/Variables/FloatVariableInfo.php @@ -0,0 +1,19 @@ +tag = $this->readUnsignedByte(); + } +} diff --git a/src/Kernel/Variables/IntegerVariableInfo.php b/src/Kernel/Variables/IntegerVariableInfo.php new file mode 100644 index 00000000..2c46560b --- /dev/null +++ b/src/Kernel/Variables/IntegerVariableInfo.php @@ -0,0 +1,19 @@ +tag = $this->readUnsignedByte(); + } +} diff --git a/src/Kernel/Variables/LongVariableInfo.php b/src/Kernel/Variables/LongVariableInfo.php new file mode 100644 index 00000000..f0fc6763 --- /dev/null +++ b/src/Kernel/Variables/LongVariableInfo.php @@ -0,0 +1,19 @@ +tag = $this->readUnsignedByte(); + } +} diff --git a/src/Kernel/Variables/NullVariableInfo.php b/src/Kernel/Variables/NullVariableInfo.php new file mode 100644 index 00000000..1cc93cf7 --- /dev/null +++ b/src/Kernel/Variables/NullVariableInfo.php @@ -0,0 +1,19 @@ +tag = $this->readUnsignedByte(); + } +} diff --git a/src/Kernel/Variables/ObjectVariableInfo.php b/src/Kernel/Variables/ObjectVariableInfo.php new file mode 100644 index 00000000..d3433cbf --- /dev/null +++ b/src/Kernel/Variables/ObjectVariableInfo.php @@ -0,0 +1,25 @@ +tag = $this->readUnsignedByte(); + $this->cpoolIndex = $this->readUnsignedShort(); + } +} diff --git a/src/Kernel/Variables/TopVariableInfo.php b/src/Kernel/Variables/TopVariableInfo.php new file mode 100644 index 00000000..967baabb --- /dev/null +++ b/src/Kernel/Variables/TopVariableInfo.php @@ -0,0 +1,19 @@ +tag = $this->readUnsignedByte(); + } +} diff --git a/src/Kernel/Variables/UninitializedThisVariableInfo.php b/src/Kernel/Variables/UninitializedThisVariableInfo.php new file mode 100644 index 00000000..a85a2644 --- /dev/null +++ b/src/Kernel/Variables/UninitializedThisVariableInfo.php @@ -0,0 +1,19 @@ +tag = $this->readUnsignedByte(); + } +} diff --git a/src/Kernel/Variables/UninitializedVariableInfo.php b/src/Kernel/Variables/UninitializedVariableInfo.php new file mode 100644 index 00000000..a1b4c36a --- /dev/null +++ b/src/Kernel/Variables/UninitializedVariableInfo.php @@ -0,0 +1,25 @@ +tag = $this->readUnsignedByte(); + $this->offset = $this->readUnsignedShort(); + } +} diff --git a/src/Kernel/Variables/VariableInfoInterface.php b/src/Kernel/Variables/VariableInfoInterface.php new file mode 100644 index 00000000..1f9c5c97 --- /dev/null +++ b/src/Kernel/Variables/VariableInfoInterface.php @@ -0,0 +1,8 @@ +parameters = $parameters; + } + + /** + * This method covers processing on PHPJava. + */ + public static function __staticConstruct() + { + } + + public function setJavaClass(JavaClass $javaClass): self + { + $this->javaClass = $javaClass; + return $this; + } + + /** + * @throws NoSuchMethodException + */ + public function __destruct() + { + $this->finalize(); + } + + /** + * @throws NoSuchMethodException + */ + public function __call(string $name, array $arguments) + { + $defaultName = Runtime::PREFIX_DEFAULT . $name; + if (method_exists($this, $defaultName)) { + return $this->{$defaultName}(...$arguments); + } + throw new NoSuchMethodException($name . ' does not exist on ' . get_class($this)); + } + + public function __default_clone(): void + { + throw new CloneNotSupportedException(); + } + + public function __default_equals($a = null): bool + { + return $this === $a; + } + + public function __default_getClass(): self + { + return $this; + } + + public function __default_hashCode() + { + return System::identityHashCode($this); + } + + public function __default_notify(): void + { + // not implemented. + } + + public function __default_notifyAll(): void + { + // not implemented. + } + + /** + * @return string + */ + public function __default_toString() + { + return 'java.lang.Object@PHPJava' . spl_object_hash($this); + } + + /** + * @throws NoSuchMethodException + */ + public function __toString(): string + { + return $this->toString(); + } + + public function __default_wait(int $timeout = null, int $nanos = null): void + { + // not implemented. + } + + public function __default_finalize(): void + { + } +} diff --git a/src/Packages/PHPJava/Kernel/Behavior/System.php b/src/Packages/PHPJava/Kernel/Behavior/System.php new file mode 100644 index 00000000..3a412d5a --- /dev/null +++ b/src/Packages/PHPJava/Kernel/Behavior/System.php @@ -0,0 +1,13 @@ +getInvoker()->getClassObject()); + } +} diff --git a/src/Packages/java/io/BufferedInputStream.php b/src/Packages/java/io/BufferedInputStream.php new file mode 100644 index 00000000..8bfcdb26 --- /dev/null +++ b/src/Packages/java/io/BufferedInputStream.php @@ -0,0 +1,140 @@ +sequence .= $string; + return $this; + } + + /** + * Flushes the stream and checks its error state. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/package-summary.html#checkError + * @param null|mixed $a + * @throws NotImplementedException + */ + public function checkError($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Clears the internal error state of this stream. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/package-summary.html#clearError + * @param null|mixed $a + * @throws NotImplementedException + */ + protected function clearError($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Closes the stream. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/package-summary.html#close + * @param null|mixed $a + * @throws NotImplementedException + */ + public function close($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Flushes the stream. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/package-summary.html#flush + * @param null|mixed $a + * @throws NotImplementedException + */ + public function flush($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Writes a formatted string to this output stream using the specified format string and arguments. + * Writes a formatted string to this output stream using the specified format string and arguments. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/package-summary.html#format + * @param null|mixed $a + * @param null|mixed $b + * @param null|mixed $c + * @throws NotImplementedException + */ + public function format($a = null, $b = null, $c = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Prints a boolean value. + * Prints a character. + * Prints an array of characters. + * Prints a double-precision floating-point number. + * Prints a floating-point number. + * Prints an integer. + * Prints a long integer. + * Prints an object. + * Prints a string. + * + * @depended-info signature + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/package-summary.html#print + * @param null|mixed $a + * @throws NotImplementedException + */ + public function print($methodSignature, $a = null) + { + /** + * TODO: Rewrite here. + * This method is very weak. + * We need to judge parameters type with methodSignature parameter. + */ + // Did not pass a parameter. + if (!isset($methodSignature['arguments'][0])) { + return; + } + + $arg = $a; + + if ($arg instanceof Utf8Info) { + Output::write($arg->getString()); + return; + } + + if (is_scalar($arg) || + $arg instanceof Collection || + $arg instanceof PrimitiveValueInterface + ) { + if (((string) $arg) !== "\x00") { + Output::write($arg); + return; + } + } + + if ($arg instanceof JavaClass) { + Output::write($arg); + return; + } + + [ $signatureType, $typeName ] = TypeResolver::getType($methodSignature['arguments'][0]); + + if ($typeName === String_::class) { + Output::write('null'); + return; + } + + throw new NullPointerException(); + } + + /** + * A convenience method to write a formatted string to this output stream using the specified format string and arguments. + * A convenience method to write a formatted string to this output stream using the specified format string and arguments. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/package-summary.html#printf + * @param null|mixed $a + * @param null|mixed $b + * @param null|mixed $c + * @throws NotImplementedException + */ + public function printf($a = null, $b = null, $c = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Terminates the current line by writing the line separator string. + * Prints a boolean and then terminate the line. + * Prints a character and then terminate the line. + * Prints an array of characters and then terminate the line. + * Prints a double and then terminate the line. + * Prints a float and then terminate the line. + * Prints an integer and then terminate the line. + * Prints a long and then terminate the line. + * Prints an Object and then terminate the line. + * Prints a String and then terminate the line. + * + * @depended-info signature + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/package-summary.html#println + * @param null|mixed $a + * @throws NotImplementedException + */ + public function println($methodSignature, $a = null) + { + try { + $this->print($methodSignature, $a); + // output break line + Output::write("\n"); + } catch (\Exception $e) { + Output::write("\n"); + throw $e; + } + } + + /** + * Sets the error state of the stream to true. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/package-summary.html#setError + * @param null|mixed $a + * @throws NotImplementedException + */ + protected function setError($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Writes len bytes from the specified byte array starting at offset off to this stream. + * Writes the specified byte to this stream. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/package-summary.html#write + * @param null|mixed $a + * @param null|mixed $b + * @param null|mixed $c + * @throws NotImplementedException + */ + public function write($a = null, $b = null, $c = null) + { + throw new NotImplementedException(__METHOD__); + } +} diff --git a/src/Packages/java/io/PrintWriter.php b/src/Packages/java/io/PrintWriter.php new file mode 100644 index 00000000..a4896713 --- /dev/null +++ b/src/Packages/java/io/PrintWriter.php @@ -0,0 +1,189 @@ + + * @param null|mixed $a + */ + public static function static_atan($a = null) + { + return atan(Normalizer::getPrimitiveValue($a)); + } + + /** + * Returns the angle theta from the conversion of rectangular coordinates (x, y) to polar coordinates (r, theta). + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Math.html#atan2(double,double) + * @param null|mixed $a + * @param null|mixed $b + */ + public static function static_atan2($a = null, $b = null) + { + return atan2(Normalizer::getPrimitiveValue($a), Normalizer::getPrimitiveValue($b)); + } + + /** + * Returns the cube root of a double value. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Math.html#cbrt(double) + * @param null|mixed $a + */ + public static function static_cbrt($a = null) + { + $a = Normalizer::getPrimitiveValue($a); + + if ($a >= 0) { + return pow($a, 1 / 3); + } + + return -pow(abs($a), 1 / 3); + } + + /** + * Returns the smallest (closest to negative infinity) double value that is greater than or equal to the argument and is equal to a mathematical integer. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Math.html#ceil(double) + * @param null|mixed $a + */ + public static function static_ceil($a = null) + { + return ceil(Normalizer::getPrimitiveValue($a)); + } + + /** + * Returns the first floating-point argument with the sign of the second floating-point argument. + * Returns the first floating-point argument with the sign of the second floating-point argument. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#copySign + * @param null|mixed $a + * @param null|mixed $b + */ + public static function static_copySign($a = null, $b = null) + { + $magnitude = Normalizer::getPrimitiveValue($a); + $sign = Normalizer::getPrimitiveValue($b); + + return $sign >= 0 ? abs($magnitude) : -abs($magnitude); + } + + /** + * Returns the trigonometric cosine of an angle. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Math.html#cos(double) + * @param null|mixed $a + */ + public static function static_cos($a = null) + { + return cos(Normalizer::getPrimitiveValue($a)); + } + + /** + * Returns the hyperbolic cosine of a double value. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Math.html#cosh(double) + * @param null|mixed $a + */ + public static function static_cosh($a = null) + { + return cosh(Normalizer::getPrimitiveValue($a)); + } + + /** + * Returns the argument decremented by one, throwing an exception if the result overflows an int. + * Returns the argument decremented by one, throwing an exception if the result overflows a long. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Math.html#decrementExact(int) + * @param null|mixed $a + */ + public static function static_decrementExact($a = null) + { + return Normalizer::getPrimitiveValue($a) - 1; + } + + /** + * Returns Euler's number e raised to the power of a double value. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Math.html#exp(double) + * @param null|mixed $a + */ + public static function static_exp($a = null) + { + return exp(Normalizer::getPrimitiveValue($a)); + } + + /** + * Returns ex -1. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Math.html#expm1(double) + * @param null|mixed $a + */ + public static function static_expm1($a = null) + { + return expm1(Normalizer::getPrimitiveValue($a)); + } + + /** + * Returns the largest (closest to positive infinity) double value that is less than or equal to the argument and is equal to a mathematical integer. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Math.html#floor(double) + * @param null|mixed $a + */ + public static function static_floor($a = null) + { + return floor(Normalizer::getPrimitiveValue($a)); + } + + /** + * Returns the largest (closest to positive infinity) int value that is less than or equal to the algebraic quotient. + * Returns the largest (closest to positive infinity) long value that is less than or equal to the algebraic quotient. + * Returns the largest (closest to positive infinity) long value that is less than or equal to the algebraic quotient. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Math.html#floorDiv(int,int) + * @param null|mixed $a + * @param null|mixed $b + */ + public static function static_floorDiv($a = null, $b = null) + { + $a = Normalizer::getPrimitiveValue($a); + $b = Normalizer::getPrimitiveValue($b); + + return (int) floor($a / $b); + } + + /** + * Returns the floor modulus of the int arguments. + * Returns the floor modulus of the long and int arguments. + * Returns the floor modulus of the long arguments. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Math.html#floorMod(int,int) + * @param null|mixed $a + * @param null|mixed $b + */ + public static function static_floorMod($a = null, $b = null) + { + $a = Normalizer::getPrimitiveValue($a); + $b = Normalizer::getPrimitiveValue($b); + + return $a - static::static_floorDiv($a, $b) * $b; + } + + /** + * Returns the fused multiply add of the three arguments; that is, returns the exact product of the first two arguments summed with the third argument and then rounded once to the nearest double. + * Returns the fused multiply add of the three arguments; that is, returns the exact product of the first two arguments summed with the third argument and then rounded once to the nearest float. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#fma + * @param null|mixed $a + * @param null|mixed $b + * @param null|mixed $c + * @throws NotImplementedException + */ + public static function static_fma($a = null, $b = null, $c = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the unbiased exponent used in the representation of a double. + * Returns the unbiased exponent used in the representation of a float. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#getExponent + * @param null|mixed $a + * @throws NotImplementedException + */ + public static function static_getExponent($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns sqrt(x2 +y2) without intermediate overflow or underflow. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#hypot + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public static function static_hypot($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Computes the remainder operation on two arguments as prescribed by the IEEE 754 standard. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#IEEEremainder + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public static function static_IEEEremainder($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the argument incremented by one, throwing an exception if the result overflows an int. + * Returns the argument incremented by one, throwing an exception if the result overflows a long. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Math.html#incrementExact(int) + * @param null|mixed $a + */ + public static function static_incrementExact($a = null) + { + return Normalizer::getPrimitiveValue($a) + 1; + } + + /** + * Returns the natural logarithm (base e) of a double value. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Math.html#log(double) + * @param null|mixed $a + */ + public static function static_log($a = null) + { + return log(Normalizer::getPrimitiveValue($a)); + } + + /** + * Returns the base 10 logarithm of a double value. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Math.html#log10(double) + * @param null|mixed $a + */ + public static function static_log10($a = null) + { + return log10(Normalizer::getPrimitiveValue($a)); + } + + /** + * Returns the natural logarithm of the sum of the argument and 1. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Math.html#log1p(double) + * @param null|mixed $a + */ + public static function static_log1p($a = null) + { + return log1p(Normalizer::getPrimitiveValue($a)); + } + + /** + * Returns the greater of two double values. + * Returns the greater of two float values. + * Returns the greater of two int values. + * Returns the greater of two long values. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Math.html#max(double,double) + * @param null|mixed $a + * @param null|mixed $b + */ + public static function static_max($a = null, $b = null) + { + return max(Normalizer::getPrimitiveValue($a), Normalizer::getPrimitiveValue($b)); + } + + /** + * Returns the smaller of two double values. + * Returns the smaller of two float values. + * Returns the smaller of two int values. + * Returns the smaller of two long values. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Math.html#min(double,double) + * @param null|mixed $a + * @param null|mixed $b + */ + public static function static_min($a = null, $b = null) + { + return min(Normalizer::getPrimitiveValue($a), Normalizer::getPrimitiveValue($b)); + } + + /** + * Returns the product of the arguments, throwing an exception if the result overflows an int. + * Returns the product of the arguments, throwing an exception if the result overflows a long. + * Returns the product of the arguments, throwing an exception if the result overflows a long. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Math.html#multiplyExact(int,int) + * @param null|mixed $a + * @param null|mixed $b + */ + public static function static_multiplyExact($a = null, $b = null) + { + return Normalizer::getPrimitiveValue($a) * Normalizer::getPrimitiveValue($b); + } + + /** + * Returns the exact mathematical product of the arguments. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Math.html#multiplyFull(int,int) + * @param null|mixed $a + * @param null|mixed $b + */ + public static function static_multiplyFull($a = null, $b = null) + { + return Normalizer::getPrimitiveValue($a) * Normalizer::getPrimitiveValue($b); + } + + /** + * Returns as a long the most significant 64 bits of the 128-bit product of two 64-bit factors. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#multiplyHigh + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public static function static_multiplyHigh($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the negation of the argument, throwing an exception if the result overflows an int. + * Returns the negation of the argument, throwing an exception if the result overflows a long. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#negateExact + * @param null|mixed $a + * @throws NotImplementedException + */ + public static function static_negateExact($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the floating-point number adjacent to the first argument in the direction of the second argument. + * Returns the floating-point number adjacent to the first argument in the direction of the second argument. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#nextAfter + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public static function static_nextAfter($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the floating-point value adjacent to d in the direction of negative infinity. + * Returns the floating-point value adjacent to f in the direction of negative infinity. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#nextDown + * @param null|mixed $a + * @throws NotImplementedException + */ + public static function static_nextDown($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the floating-point value adjacent to d in the direction of positive infinity. + * Returns the floating-point value adjacent to f in the direction of positive infinity. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#nextUp + * @param null|mixed $a + * @throws NotImplementedException + */ + public static function static_nextUp($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the value of the first argument raised to the power of the second argument. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Math.html#pow(double,double) + * @param null|mixed $a + * @param null|mixed $b + */ + public static function static_pow($a = null, $b = null) + { + return pow(Normalizer::getPrimitiveValue($a), Normalizer::getPrimitiveValue($b)); + } + + /** + * Returns a double value with a positive sign, greater than or equal to 0.0 and less than 1.0. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Math.html#random() + */ + public static function static_random() + { + return mt_rand() / mt_getrandmax(); + } + + /** + * Returns the double value that is closest in value to the argument and is equal to a mathematical integer. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#rint + * @param null|mixed $a + * @throws NotImplementedException + */ + public static function static_rint($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the closest long to the argument, with ties rounding to positive infinity. + * Returns the closest int to the argument, with ties rounding to positive infinity. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Math.html#round(double) + * @param null|mixed $a + */ + public static function static_round($a = null) + { + return round(Normalizer::getPrimitiveValue($a)); + } + + /** + * Returns d × 2scaleFactor rounded as if performed by a single correctly rounded floating-point multiply to a member of the double value set. + * Returns f × 2scaleFactor rounded as if performed by a single correctly rounded floating-point multiply to a member of the float value set. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#scalb + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public static function static_scalb($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the signum function of the argument; zero if the argument is zero, 1.0 if the argument is greater than zero, -1.0 if the argument is less than zero. + * Returns the signum function of the argument; zero if the argument is zero, 1.0f if the argument is greater than zero, -1.0f if the argument is less than zero. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#signum + * @param null|mixed $a + * @throws NotImplementedException + */ + public static function static_signum($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the trigonometric sine of an angle. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Math.html#sin(double) + * @param null|mixed $a + */ + public static function static_sin($a = null) + { + return sin(Normalizer::getPrimitiveValue($a)); + } + + /** + * Returns the hyperbolic sine of a double value. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Math.html#sinh(double) + * @param null|mixed $a + */ + public static function static_sinh($a = null) + { + return sinh(Normalizer::getPrimitiveValue($a)); + } + + /** + * Returns the correctly rounded positive square root of a double value. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Math.html#sqrt(double) + * @param null|mixed $a + */ + public static function static_sqrt($a = null) + { + return sqrt(Normalizer::getPrimitiveValue($a)); + } + + /** + * Returns the difference of the arguments, throwing an exception if the result overflows an int. + * Returns the difference of the arguments, throwing an exception if the result overflows a long. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Math.html#subtractExact(int,int) + * @param null|mixed $a + * @param null|mixed $b + */ + public static function static_subtractExact($a = null, $b = null) + { + return Normalizer::getPrimitiveValue($a) - Normalizer::getPrimitiveValue($b); + } + + /** + * Returns the trigonometric tangent of an angle. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Math.html#tan(double) + * @param null|mixed $a + */ + public static function static_tan($a = null) + { + return tan(Normalizer::getPrimitiveValue($a)); + } + + /** + * Returns the hyperbolic tangent of a double value. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Math.html#tanh(double) + * @param null|mixed $a + */ + public static function static_tanh($a = null) + { + return tanh(Normalizer::getPrimitiveValue($a)); + } + + /** + * Converts an angle measured in radians to an approximately equivalent angle measured in degrees. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#toDegrees + * @param null|mixed $a + * @throws NotImplementedException + */ + public static function static_toDegrees($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the value of the long argument; throwing an exception if the value overflows an int. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#toIntExact + * @param null|mixed $a + * @throws NotImplementedException + */ + public static function static_toIntExact($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Converts an angle measured in degrees to an approximately equivalent angle measured in radians. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#toRadians + * @param null|mixed $a + * @throws NotImplementedException + */ + public static function static_toRadians($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the size of an ulp of the argument. + * Returns the size of an ulp of the argument. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#ulp + * @param null|mixed $a + * @throws NotImplementedException + */ + public static function static_ulp($a = null) + { + throw new NotImplementedException(__METHOD__); + } +} diff --git a/src/Packages/java/lang/Module.php b/src/Packages/java/lang/Module.php new file mode 100644 index 00000000..2542cedd --- /dev/null +++ b/src/Packages/java/lang/Module.php @@ -0,0 +1,251 @@ +sequence .= $a->toString(); + } else { + if (is_array($a)) { + $a = implode($a); + } + $this->sequence .= $a; + } + return $this->javaClass; + } + + /** + * Appends the string representation of the codePoint argument to this sequence. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#appendCodePoint + * @param null|mixed $a + * @throws NotImplementedException + */ + public function appendCodePoint($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the current capacity. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#capacity + * @param null|mixed $a + * @throws NotImplementedException + */ + public function capacity($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the char value in this sequence at the specified index. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#charAt + * @param null|mixed $a + * @throws NotImplementedException + */ + public function charAt($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns a stream of int zero-extending the char values from this sequence. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#chars + * @param null|mixed $a + * @throws NotImplementedException + */ + public function chars($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the character (Unicode code point) at the specified index. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#codePointAt + * @param null|mixed $a + * @throws NotImplementedException + */ + public function codePointAt($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the character (Unicode code point) before the specified index. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#codePointBefore + * @param null|mixed $a + * @throws NotImplementedException + */ + public function codePointBefore($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the number of Unicode code points in the specified text range of this sequence. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#codePointCount + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public function codePointCount($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns a stream of code point values from this sequence. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#codePoints + * @param null|mixed $a + * @throws NotImplementedException + */ + public function codePoints($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Compares two StringBuilder instances lexicographically. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#compareTo + * @param null|mixed $a + * @throws NotImplementedException + */ + public function compareTo($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Removes the characters in a substring of this sequence. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#delete + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public function delete($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Removes the char at the specified position in this sequence. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#deleteCharAt + * @param null|mixed $a + * @throws NotImplementedException + */ + public function deleteCharAt($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Ensures that the capacity is at least equal to the specified minimum. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#ensureCapacity + * @param null|mixed $a + * @throws NotImplementedException + */ + public function ensureCapacity($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Characters are copied from this sequence into the destination character array dst. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#getChars + * @param null|mixed $a + * @param null|mixed $b + * @param null|mixed $c + * @param null|mixed $d + * @throws NotImplementedException + */ + public function getChars($a = null, $b = null, $c = null, $d = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the index within this string of the first occurrence of the specified substring. + * Returns the index within this string of the first occurrence of the specified substring, starting at the specified index. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#indexOf + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public function indexOf($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Inserts the string representation of the boolean argument into this sequence. + * Inserts the string representation of the char argument into this sequence. + * Inserts the string representation of the char array argument into this sequence. + * Inserts the string representation of a subarray of the str array argument into this sequence. + * Inserts the string representation of the double argument into this sequence. + * Inserts the string representation of the float argument into this sequence. + * Inserts the string representation of the second int argument into this sequence. + * Inserts the string representation of the long argument into this sequence. + * Inserts the specified CharSequence into this sequence. + * Inserts a subsequence of the specified CharSequence into this sequence. + * Inserts the string representation of the Object argument into this character sequence. + * Inserts the string into this character sequence. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#insert + * @param null|mixed $a + * @param null|mixed $b + * @param null|mixed $c + * @param null|mixed $d + * @throws NotImplementedException + */ + public function insert($a = null, $b = null, $c = null, $d = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the index within this string of the last occurrence of the specified substring. + * Returns the index within this string of the last occurrence of the specified substring, searching backward starting at the specified index. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#lastIndexOf + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public function lastIndexOf($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the length (character count). + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#length + * @param null|mixed $a + * @throws NotImplementedException + */ + public function length($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the index within this sequence that is offset from the given index by codePointOffset code points. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#offsetByCodePoints + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public function offsetByCodePoints($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Replaces the characters in a substring of this sequence with characters in the specified String. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#replace + * @param null|mixed $a + * @param null|mixed $b + * @param null|mixed $c + * @throws NotImplementedException + */ + public function replace($a = null, $b = null, $c = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Causes this character sequence to be replaced by the reverse of the sequence. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#reverse + * @param null|mixed $a + * @throws NotImplementedException + */ + public function reverse($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * The character at the specified index is set to ch. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#setCharAt + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public function setCharAt($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Sets the length of the character sequence. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#setLength + * @param null|mixed $a + * @throws NotImplementedException + */ + public function setLength($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns a new character sequence that is a subsequence of this sequence. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#subSequence + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public function subSequence($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns a new String that contains a subsequence of characters currently contained in this character sequence. + * Returns a new String that contains a subsequence of characters currently contained in this sequence. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#substring + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public function substring($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Attempts to reduce storage used for the character sequence. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#trimToSize + * @param null|mixed $a + * @throws NotImplementedException + */ + public function trimToSize($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + public function toString() + { + return JavaClass::load('java.lang.String', $this->javaClass->getOptions()) + ->getInvoker() + ->construct((string) $this) + ->getJavaClass(); + } + + public function __toString(): string + { + return $this->sequence; + } +} diff --git a/src/Packages/java/lang/String_.php b/src/Packages/java/lang/String_.php new file mode 100644 index 00000000..460c0c18 --- /dev/null +++ b/src/Packages/java/lang/String_.php @@ -0,0 +1,730 @@ +object = $object; + } + + /** + * Returns the char value at the specified index. + * + * @throws IndexOutOfBoundsException + * @return _Char + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/String.html#charAt(int) + */ + public function charAt($a) + { + $index = Normalizer::getPrimitiveValue($a); + $length = $this->length(); + + if ($index < 0 || $length <= $index) { + throw new IndexOutOfBoundsException("String index out of range: {$index}"); + } + + return new Char_($this->toString()[$index]); + } + + /** + * Returns a stream of int zero-extending the char values from this sequence. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#chars + * @param null|mixed $a + * @throws NotImplementedException + */ + public function chars($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the character (Unicode code point) at the specified index. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#codePointAt + * @param null|mixed $a + * @throws NotImplementedException + */ + public function codePointAt($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the character (Unicode code point) before the specified index. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#codePointBefore + * @param null|mixed $a + * @throws NotImplementedException + */ + public function codePointBefore($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the number of Unicode code points in the specified text range of this String. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#codePointCount + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public function codePointCount($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns a stream of code point values from this sequence. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#codePoints + * @param null|mixed $a + * @throws NotImplementedException + */ + public function codePoints($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Compares two strings lexicographically. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#compareTo + * @param null|mixed $a + * @throws NotImplementedException + */ + public function compareTo($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Compares two strings lexicographically, ignoring case differences. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#compareToIgnoreCase + * @param null|mixed $a + * @throws NotImplementedException + */ + public function compareToIgnoreCase($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Concatenates the specified string to the end of this string. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/String.html#concat(java.lang.String) + * @param null|mixed $a + */ + public function concat($a = null) + { + return JavaClass::load('java.lang.String', $this->javaClass->getOptions()) + ->getInvoker() + ->construct($this . $a) + ->getJavaClass(); + } + + /** + * Returns true if and only if this string contains the specified sequence of char values. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#contains + * @param null|mixed $a + * @throws NotImplementedException + */ + public function contains($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Compares this string to the specified CharSequence. + * Compares this string to the specified StringBuffer. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#contentEquals + * @param null|mixed $a + * @throws NotImplementedException + */ + public function contentEquals($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Equivalent to valueOf(char[]). + * Equivalent to valueOf(char[], int, int). + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#copyValueOf + * @param null|mixed $a + * @param null|mixed $b + * @param null|mixed $c + * @throws NotImplementedException + */ + public static function static_copyValueOf($a = null, $b = null, $c = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Tests if this string ends with the specified suffix. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#endsWith + * @param null|mixed $a + * @throws NotImplementedException + */ + public function endsWith($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Compares this string to the specified object. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/String.html#equals(java.lang.Object) + * @param null|mixed $a + */ + public function equals($a = null) + { + if (!($this->object instanceof Utf8Info)) { + return false; + } + if ($a instanceof String_) { + return $this->toString() === $a->toString(); + } + return $this->toString() === $a; + } + + /** + * Compares this String to another String, ignoring case considerations. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#equalsIgnoreCase + * @param null|mixed $a + * @throws NotImplementedException + */ + public function equalsIgnoreCase($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns a formatted string using the specified format string and arguments. + * Returns a formatted string using the specified locale, format string, and arguments. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#format + * @param null|mixed $a + * @param null|mixed $b + * @param null|mixed $c + * @throws NotImplementedException + */ + public static function static_format($a = null, $b = null, $c = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Encodes this String into a sequence of bytes using the platform's default charset, storing the result into a new byte array. + * Deprecated.This method does not properly convert characters into bytes. + * Encodes this String into a sequence of bytes using the named charset, storing the result into a new byte array. + * Encodes this String into a sequence of bytes using the given charset, storing the result into a new byte array. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#getBytes + * @param null|mixed $a + * @param null|mixed $b + * @param null|mixed $c + * @param null|mixed $d + * @throws NotImplementedException + */ + public function getBytes($a = null, $b = null, $c = null, $d = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Copies characters from this string into the destination character array. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#getChars + * @param null|mixed $a + * @param null|mixed $b + * @param null|mixed $c + * @param null|mixed $d + * @throws NotImplementedException + */ + public function getChars($a = null, $b = null, $c = null, $d = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns a hash code for this string. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/String.html#hashCode() + */ + public function hashCode() + { + $count = $this->length(); + + $h = $this->hash; + if ($h || !$count) { + return $h; + } + + $str = $this->toString(); + for ($i = 0; $i < $count; $i++) { + $h = 31 * $h + ord($str[$i]); + } + + return $this->hash = $h; + } + + /** + * Returns the index within this string of the first occurrence of the specified character. + * Returns the index within this string of the first occurrence of the specified character, starting the search at the specified index. + * Returns the index within this string of the first occurrence of the specified substring. + * Returns the index within this string of the first occurrence of the specified substring, starting at the specified index. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#indexOf + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public function indexOf($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns a canonical representation for the string object. + * + * @native ConstantPool + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#intern + * @throws NotImplementedException + */ + public function intern(ConstantPool $cp) + { + // Find the string from the Constant Pool. + foreach ($cp as $key => $value) { + if (!($value instanceof Utf8Info)) { + continue; + } + /** + * @var Utf8Info $value + */ + if ((string) $value === (string) $this->object) { + $this->object = $value + ->enableWrite(true) + ->setStringObject( + $this->javaClass + ); + break; + } + } + return $this->javaClass; + } + + /** + * Returns true if the string is empty or contains only white space codepoints, otherwise false. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#isBlank + * @param null|mixed $a + * @throws NotImplementedException + */ + public function isBlank($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns true if, and only if, length() is 0. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#isEmpty + * @param null|mixed $a + * @throws NotImplementedException + */ + public function isEmpty($a = null) + { + return $this->length() === 0; + } + + /** + * Returns a new String composed of copies of the CharSequence elements joined together with a copy of the specified delimiter. + * Returns a new String composed of copies of the CharSequence elements joined together with a copy of the specified delimiter. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#join + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public static function static_join($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the index within this string of the last occurrence of the specified character. + * Returns the index within this string of the last occurrence of the specified character, searching backward starting at the specified index. + * Returns the index within this string of the last occurrence of the specified substring. + * Returns the index within this string of the last occurrence of the specified substring, searching backward starting at the specified index. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#lastIndexOf + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public function lastIndexOf($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the length of this string. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#length + * @param null|mixed $a + * @throws NotImplementedException + */ + public function length($a = null) + { + return strlen($this->toString()); + } + + /** + * Returns a stream of lines extracted from this string, separated by line terminators. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#lines + * @param null|mixed $a + * @throws NotImplementedException + */ + public function lines($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Tells whether or not this string matches the given regular expression. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#matches + * @param null|mixed $a + * @throws NotImplementedException + */ + public function matches($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the index within this String that is offset from the given index by codePointOffset code points. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#offsetByCodePoints + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public function offsetByCodePoints($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Tests if two string regions are equal. + * Tests if two string regions are equal. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#regionMatches + * @param null|mixed $a + * @param null|mixed $b + * @param null|mixed $c + * @param null|mixed $d + * @param null|mixed $e + * @throws NotImplementedException + */ + public function regionMatches($a = null, $b = null, $c = null, $d = null, $e = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns a string whose value is the concatenation of this string repeated count times. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#repeat + * @param null|mixed $a + * @throws NotImplementedException + */ + public function repeat($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns a string resulting from replacing all occurrences of oldChar in this string with newChar. + * Replaces each substring of this string that matches the literal target sequence with the specified literal replacement sequence. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/String.html#replace(java.lang.CharSequence,java.lang.CharSequence) + * @param null|mixed $a + * @param null|mixed $b + */ + public function replace($a = null, $b = null) + { + return JavaClass::load('java.lang.String', $this->javaClass->getOptions()) + ->getInvoker() + ->construct(str_replace($a, $b, $this)) + ->getJavaClass(); + } + + /** + * Replaces each substring of this string that matches the given regular expression with the given replacement. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#replaceAll + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public function replaceAll($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Replaces the first substring of this string that matches the given regular expression with the given replacement. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#replaceFirst + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public function replaceFirst($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Splits this string around matches of the given regular expression. + * Splits this string around matches of the given regular expression. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#split + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public function split($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Tests if this string starts with the specified prefix. + * Tests if the substring of this string beginning at the specified index starts with the specified prefix. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#startsWith + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public function startsWith($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns a string whose value is this string, with all leading and trailing white space removed. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#strip + * @param null|mixed $a + * @throws NotImplementedException + */ + public function strip($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns a string whose value is this string, with all leading white space removed. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#stripLeading + * @param null|mixed $a + * @throws NotImplementedException + */ + public function stripLeading($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns a string whose value is this string, with all trailing white space removed. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#stripTrailing + * @param null|mixed $a + * @throws NotImplementedException + */ + public function stripTrailing($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns a character sequence that is a subsequence of this sequence. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#subSequence + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public function subSequence($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns a string that is a substring of this string. + * Returns a string that is a substring of this string. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#substring + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public function substring($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Converts this string to a new character array. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#toCharArray + * @param null|mixed $a + * @throws NotImplementedException + */ + public function toCharArray($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Converts all of the characters in this String to lower case using the rules of the default locale. + * Converts all of the characters in this String to lower case using the rules of the given Locale. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#toLowerCase + * @param null|mixed $a + * @throws NotImplementedException + */ + public function toLowerCase($a = null) + { + $locale = $a; + if ($locale) { + throw new NotImplementedException(__METHOD__); + } + return JavaClass::load('java.lang.String', $this->javaClass->getOptions()) + ->getInvoker() + ->construct( + strtolower((string) $this) + ) + ->getJavaClass(); + } + + /** + * This object (which is already a string!). + * + * @throws NotImplementedException + * @return mixed + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#toString + */ + public function toString(): string + { + return $this->__toString(); + } + + /** + * Converts all of the characters in this String to upper case using the rules of the default locale. + * Converts all of the characters in this String to upper case using the rules of the given Locale. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#toUpperCase + * @param null|mixed $a + * @throws NotImplementedException + */ + public function toUpperCase($a = null) + { + $locale = $a; + if ($locale) { + throw new NotImplementedException(__METHOD__); + } + + return JavaClass::load('java.lang.String', $this->javaClass->getOptions()) + ->getInvoker() + ->construct(strtoupper((string) $this)) + ->getJavaClass(); + } + + /** + * Returns a string whose value is this string, with all leading and trailing space removed, where space is defined as any character whose codepoint is less than or equal to 'U+0020' (the space character). + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#trim + * @param null|mixed $a + * @throws NotImplementedException + */ + public function trim($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the string representation of the boolean argument. + * Returns the string representation of the char argument. + * Returns the string representation of the char array argument. + * Returns the string representation of a specific subarray of the char array argument. + * Returns the string representation of the double argument. + * Returns the string representation of the float argument. + * Returns the string representation of the int argument. + * Returns the string representation of the long argument. + * Returns the string representation of the Object argument. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#valueOf + * @param null|mixed $a + * @param null|mixed $b + * @param null|mixed $c + * @throws NotImplementedException + */ + public static function static_valueOf($a = null, $b = null, $c = null) + { + throw new NotImplementedException(__METHOD__); + } + + public function __toString(): string + { + if (!($this->object instanceof Utf8Info)) { + return (string) $this->object; + } + return $this->object->getString(); + } +} diff --git a/src/Packages/java/lang/System.php b/src/Packages/java/lang/System.php new file mode 100644 index 00000000..5b310240 --- /dev/null +++ b/src/Packages/java/lang/System.php @@ -0,0 +1,355 @@ +getInvoker()->construct(); + } + + /** + * Copies an array from the specified source array, beginning at the specified position, to the specified position of the destination array. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#arraycopy + * @param null|mixed $a + * @param null|mixed $b + * @param null|mixed $c + * @param null|mixed $d + * @param null|mixed $e + * @throws NotImplementedException + */ + public static function static_arraycopy($a = null, $b = null, $c = null, $d = null, $e = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Removes the system property indicated by the specified key. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#clearProperty + * @param null|mixed $a + * @throws NotImplementedException + */ + public static function static_clearProperty($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the unique Console object associated with the current Java virtual machine, if any. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#console + * @param null|mixed $a + * @throws NotImplementedException + */ + public static function static_console($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the current time in milliseconds. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#currentTimeMillis + * @param null|mixed $a + * @throws NotImplementedException + */ + public static function static_currentTimeMillis($a = null) + { + return time() * 1000; + } + + /** + * Terminates the currently running Java Virtual Machine. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#exit + * @param null|mixed $a + * @throws NotImplementedException + */ + public static function static_exit($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Runs the garbage collector. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#gc + * @param null|mixed $a + * @throws NotImplementedException + */ + public static function static_gc($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns an unmodifiable string map view of the current system environment. + * Gets the value of the specified environment variable. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#getenv + * @param null|mixed $a + * @throws NotImplementedException + */ + public static function static_getenv($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns an instance of Logger for the caller's use. + * Returns a localizable instance of Logger for the caller's use. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#getLogger + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public static function static_getLogger($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Determines the current system properties. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#getProperties + * @param null|mixed $a + * @throws NotImplementedException + */ + public static function static_getProperties($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Gets the system property indicated by the specified key. + * Gets the system property indicated by the specified key. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#getProperty + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public static function static_getProperty($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Gets the system security interface. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#getSecurityManager + * @param null|mixed $a + * @throws NotImplementedException + */ + public static function static_getSecurityManager($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the same hash code for the given object as would be returned by the default method hashCode(), whether or not the given object's class overrides hashCode(). + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/System.html#identityHashCode(java.lang.Object) + * @param null|mixed $a + */ + public static function static_identityHashCode($a = null) + { + return SystemBehavior::identityHashCode($a); + } + + /** + * Returns the channel inherited from the entity that created this Java virtual machine. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#inheritedChannel + * @param null|mixed $a + * @throws NotImplementedException + */ + public static function static_inheritedChannel($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the system-dependent line separator string. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#lineSeparator + * @param null|mixed $a + * @throws NotImplementedException + */ + public static function static_lineSeparator($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Loads the native library specified by the filename argument. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#load + * @param null|mixed $a + * @throws NotImplementedException + */ + public static function static_load($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Loads the native library specified by the libname argument. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#loadLibrary + * @param null|mixed $a + * @throws NotImplementedException + */ + public static function static_loadLibrary($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Maps a library name into a platform-specific string representing a native library. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#mapLibraryName + * @param null|mixed $a + * @throws NotImplementedException + */ + public static function static_mapLibraryName($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the current value of the running Java Virtual Machine's high-resolution time source, in nanoseconds. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#nanoTime + * @param null|mixed $a + * @throws NotImplementedException + */ + public static function static_nanoTime($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Runs the finalization methods of any objects pending finalization. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#runFinalization + * @param null|mixed $a + * @throws NotImplementedException + */ + public static function static_runFinalization($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Reassigns the "standard" error output stream. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#setErr + * @param null|mixed $a + * @throws NotImplementedException + */ + public static function static_setErr($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Reassigns the "standard" input stream. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#setIn + * @param null|mixed $a + * @throws NotImplementedException + */ + public static function static_setIn($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Reassigns the "standard" output stream. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#setOut + * @param null|mixed $a + * @throws NotImplementedException + */ + public static function static_setOut($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Sets the system properties to the Properties argument. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#setProperties + * @param null|mixed $a + * @throws NotImplementedException + */ + public static function static_setProperties($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Sets the system property indicated by the specified key. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#setProperty + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public static function static_setProperty($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Sets the System security. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/package-summary.html#setSecurityManager + * @param null|mixed $a + * @throws NotImplementedException + */ + public static function static_setSecurityManager($a = null) + { + throw new NotImplementedException(__METHOD__); + } +} diff --git a/src/Packages/java/lang/System/Logger.php b/src/Packages/java/lang/System/Logger.php new file mode 100644 index 00000000..0d2ea162 --- /dev/null +++ b/src/Packages/java/lang/System/Logger.php @@ -0,0 +1,55 @@ +getMessage(); + } +} diff --git a/src/Packages/java/lang/UnknownError.php b/src/Packages/java/lang/UnknownError.php new file mode 100644 index 00000000..4dc9412e --- /dev/null +++ b/src/Packages/java/lang/UnknownError.php @@ -0,0 +1,17 @@ +getInvoker() + ->getDynamic() + ->getMethods() + ->call( + 'returnType' + ); + + $lambdaInfo = $cp[$cp[$implMethod->getReferenceIndex()]->getNameAndTypeIndex()]; + $lambdaName = $cp[$lambdaInfo->getNameIndex()]->getString(); + $lambdaDescriptor = $cp[$lambdaInfo->getDescriptorIndex()]->getString(); + + // Create a lambda class. + return new Lambda( + $javaClass, + $invokedName, + $lambdaName, + $lambdaDescriptor, + $class + ); + } +} diff --git a/src/Packages/java/lang/invoke/MethodHandle.php b/src/Packages/java/lang/invoke/MethodHandle.php new file mode 100644 index 00000000..484a5b7b --- /dev/null +++ b/src/Packages/java/lang/invoke/MethodHandle.php @@ -0,0 +1,211 @@ +parameters; + $class = $args[0] ?? null; + + $result = $class + ->getInvoker() + ->getDynamic() + ->getMethods() + ->call( + $name, + ...array_slice($args, 1) + ); + + /** + * @var JavaClassInterface $returnType + */ + $returnType = $methodType + ->getInvoker() + ->getDynamic() + ->getMethods() + ->call( + 'returnType' + ); + + if ($returnType instanceof Void_) { + return null; + } + return $result; + } + + /** + * Performs a variable arity invocation, passing the arguments in the given array to the method handle, as if via an inexact invoke from a call site which mentions only the type Object, and whose actual argument count is the length of the argument array. + * Performs a variable arity invocation, passing the arguments in the given list to the method handle, as if via an inexact invoke from a call site which mentions only the type Object, and whose actual argument count is the length of the argument list. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#invokeWithArguments + * @param null|mixed $a + * @throws NotImplementedException + */ + public function invokeWithArguments($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Determines if this method handle supports variable arity calls. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#isVarargsCollector + * @param null|mixed $a + * @throws NotImplementedException + */ + public function isVarargsCollector($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns a string representation of the method handle, starting with the string "MethodHandle" and ending with the string representation of the method handle's type. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#toString + * @param null|mixed $a + * @throws NotImplementedException + */ + public function toString($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Reports the type of this method handle. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#type + * @param null|mixed $a + * @throws NotImplementedException + */ + public function type($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Adapts this method handle to be variable arity if the boolean flag is true, else fixed arity. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#withVarargs + * @param null|mixed $a + * @throws NotImplementedException + */ + public function withVarargs($a = null) + { + throw new NotImplementedException(__METHOD__); + } +} diff --git a/src/Packages/java/lang/invoke/MethodHandleInfo.php b/src/Packages/java/lang/invoke/MethodHandleInfo.php new file mode 100644 index 00000000..8a49a50c --- /dev/null +++ b/src/Packages/java/lang/invoke/MethodHandleInfo.php @@ -0,0 +1,115 @@ + true, + ] + )->getInvoker() + ->construct($a) + ->getJavaClass(); + } + + /** + * Constructs a method handle representing a loop with several loop variables that are updated and checked upon each iteration. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#loop + * @param null|mixed $a + * @throws NotImplementedException + */ + public static function loop($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Produces a method handle which adapts the calling sequence of the given method handle to a new type, by reordering the arguments. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#permuteArguments + * @param null|mixed $a + * @param null|mixed $b + * @param null|mixed $c + * @throws NotImplementedException + */ + public static function permuteArguments($a = null, $b = null, $c = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns a lookup object with full capabilities to emulate all supported bytecode behaviors, including private access, on a target class. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#privateLookupIn + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public static function privateLookupIn($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns a lookup object which is trusted minimally. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#publicLookup + * @param null|mixed $a + * @throws NotImplementedException + */ + public static function publicLookup($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Performs an unchecked "crack" of a direct method handle. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#reflectAs + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public static function reflectAs($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Produces a method handle which will invoke any method handle of the given type, with a given number of trailing arguments replaced by a single trailing Object[] array. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#spreadInvoker + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public static function spreadInvoker($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Produces a method handle which will throw exceptions of the given exType. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#throwException + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public static function throwException($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Makes a method handle that adapts a target method handle by wrapping it in a try-finally block. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#tryFinally + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public static function tryFinally($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Produces a special invoker method handle which can be used to invoke a signature-polymorphic access mode method on any VarHandle whose associated access mode type is compatible with the given type. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#varHandleExactInvoker + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public static function varHandleExactInvoker($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Produces a special invoker method handle which can be used to invoke a signature-polymorphic access mode method on any VarHandle whose associated access mode type is compatible with the given type. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#varHandleInvoker + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public static function varHandleInvoker($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Constructs a while loop from an initializer, a body, and a predicate. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#whileLoop + * @param null|mixed $a + * @param null|mixed $b + * @param null|mixed $c + * @throws NotImplementedException + */ + public static function whileLoop($a = null, $b = null, $c = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Produces a constant method handle of the requested return type which returns the default value for that type every time it is invoked. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#zero + * @param null|mixed $a + * @throws NotImplementedException + */ + public static function zero($a = null) + { + throw new NotImplementedException(__METHOD__); + } +} diff --git a/src/Packages/java/lang/invoke/MethodHandles/Lookup.php b/src/Packages/java/lang/invoke/MethodHandles/Lookup.php new file mode 100644 index 00000000..510a0ced --- /dev/null +++ b/src/Packages/java/lang/invoke/MethodHandles/Lookup.php @@ -0,0 +1,432 @@ + true, + ] + )->getInvoker() + ->construct( + $refc, + $name, + $type + ) + ->getJavaClass(); + } + + /** + * Returns true if this lookup has PRIVATE access. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#hasPrivateAccess + * @param null|mixed $a + * @throws NotImplementedException + */ + public function hasPrivateAccess($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Creates a lookup on the specified new lookup class. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#in + * @param null|mixed $a + * @throws NotImplementedException + */ + public function in($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Tells which class is performing the lookup. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#lookupClass + * @param null|mixed $a + * @throws NotImplementedException + */ + public function lookupClass($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Tells which access-protection classes of members this lookup object can produce. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#lookupModes + * @param null|mixed $a + * @throws NotImplementedException + */ + public function lookupModes($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Cracks a direct method handle created by this lookup object or a similar one. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#revealDirect + * @param null|mixed $a + * @throws NotImplementedException + */ + public function revealDirect($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Displays the name of the class from which lookups are to be made. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#toString + * @param null|mixed $a + * @throws NotImplementedException + */ + public function toString($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Makes a direct method handle to m, if the lookup class has permission. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#unreflect + * @param null|mixed $a + * @throws NotImplementedException + */ + public function unreflect($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Produces a method handle for a reflected constructor. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#unreflectConstructor + * @param null|mixed $a + * @throws NotImplementedException + */ + public function unreflectConstructor($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Produces a method handle giving read access to a reflected field. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#unreflectGetter + * @param null|mixed $a + * @throws NotImplementedException + */ + public function unreflectGetter($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Produces a method handle giving write access to a reflected field. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#unreflectSetter + * @param null|mixed $a + * @throws NotImplementedException + */ + public function unreflectSetter($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Produces a method handle for a reflected method. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#unreflectSpecial + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public function unreflectSpecial($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Produces a VarHandle giving access to a reflected field f of type T declared in a class of type R. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#unreflectVarHandle + * @param null|mixed $a + * @throws NotImplementedException + */ + public function unreflectVarHandle($a = null) + { + throw new NotImplementedException(__METHOD__); + } +} diff --git a/src/Packages/java/lang/invoke/MethodType.php b/src/Packages/java/lang/invoke/MethodType.php new file mode 100644 index 00000000..db3aae55 --- /dev/null +++ b/src/Packages/java/lang/invoke/MethodType.php @@ -0,0 +1,333 @@ +parameters = $parameters; + } + + /** + * Finds or creates a method type with additional parameter types. + * Finds or creates a method type with additional parameter types. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#appendParameterTypes + * @param null|mixed $a + * @throws NotImplementedException + */ + public function appendParameterTypes($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Finds or creates a method type with a single different parameter type. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#changeParameterType + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public function changeParameterType($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Finds or creates a method type with a different return type. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#changeReturnType + * @param null|mixed $a + * @throws NotImplementedException + */ + public function changeReturnType($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Finds or creates a method type with some parameter types omitted. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#dropParameterTypes + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public function dropParameterTypes($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Compares the specified object with this type for equality. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#equals + * @param null|mixed $a + * @throws NotImplementedException + */ + public function equals($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Erases all reference types to Object. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#erase + * @param null|mixed $a + * @throws NotImplementedException + */ + public function erase($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Finds or creates an instance of a method type, given the spelling of its bytecode descriptor. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#fromMethodDescriptorString + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public static function fromMethodDescriptorString($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Converts all types, both reference and primitive, to Object. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#generic + * @param null|mixed $a + * @throws NotImplementedException + */ + public function generic($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Finds or creates a method type whose components are all Object. + * Finds or creates a method type whose components are Object with an optional trailing Object[] array. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#genericMethodType + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public static function genericMethodType($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the hash code value for this method type. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#hashCode + * @param null|mixed $a + * @throws NotImplementedException + */ + public function hashCode($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Reports if this type contains a primitive argument or return value. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#hasPrimitives + * @param null|mixed $a + * @throws NotImplementedException + */ + public function hasPrimitives($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Reports if this type contains a wrapper argument or return value. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#hasWrappers + * @param null|mixed $a + * @throws NotImplementedException + */ + public function hasWrappers($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Finds or creates a method type with additional parameter types. + * Finds or creates a method type with additional parameter types. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#insertParameterTypes + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public function insertParameterTypes($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the last parameter type of this method type. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#lastParameterType + * @param null|mixed $a + * @throws NotImplementedException + */ + public function lastParameterType($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Finds or creates a method type with the given components. + * Finds or creates a method type with the given components. + * Finds or creates an instance of the given method type. + * Finds or creates a method type with the given components. + * Finds or creates a method type with the given components. + * Finds or creates a method type with the given components. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#methodType + * @param null|mixed $a + * @param null|mixed $b + * @param null|mixed $c + */ + public static function methodType($a = null, $b = null, $c = null) + { + return JavaClass::load( + 'java.lang.invoke.MethodType', + [ + 'inherit' => true, + ] + )->getInvoker() + ->construct(...func_get_args()) + ->getJavaClass(); + } + + /** + * Presents the parameter types as an array (a convenience method). + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#parameterArray + * @param null|mixed $a + * @throws NotImplementedException + */ + public function parameterArray($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the number of parameter types in this method type. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#parameterCount + * @param null|mixed $a + * @throws NotImplementedException + */ + public function parameterCount($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Presents the parameter types as a list (a convenience method). + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#parameterList + * @param null|mixed $a + * @throws NotImplementedException + */ + public function parameterList($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the parameter type at the specified index, within this method type. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#parameterType + * @param null|mixed $a + * @throws NotImplementedException + */ + public function parameterType($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the return type of this method type. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#returnType + */ + public function returnType() + { + return $this->parameters[0]; + } + + /** + * Produces a bytecode descriptor representation of the method type. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#toMethodDescriptorString + * @param null|mixed $a + * @throws NotImplementedException + */ + public function toMethodDescriptorString($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns a string representation of the method type, of the form "(PT0,PT1...)RT". + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#toString + * @param null|mixed $a + * @throws NotImplementedException + */ + public function toString($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Converts all wrapper types to their corresponding primitive types. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#unwrap + * @param null|mixed $a + * @throws NotImplementedException + */ + public function unwrap($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Converts all primitive types to their corresponding wrapper types. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#wrap + * @param null|mixed $a + * @throws NotImplementedException + */ + public function wrap($a = null) + { + throw new NotImplementedException(__METHOD__); + } +} diff --git a/src/Packages/java/lang/invoke/MutableCallSite.php b/src/Packages/java/lang/invoke/MutableCallSite.php new file mode 100644 index 00000000..2909d97b --- /dev/null +++ b/src/Packages/java/lang/invoke/MutableCallSite.php @@ -0,0 +1,50 @@ +getStringIndex()]->getString(); + } + $constants = $e; + + $newString = ''; + + $unicodeMeta = [ + '\u0001' => "\x01", + '\u0002' => "\x02", + ]; + + $counter = 0; + for ($i = 0, $s = strlen($recipe); $i < $s; $i++) { + $char = $recipe[$i]; + if ($char === $unicodeMeta['\\u0001']) { + if (!isset($constants[$counter])) { + throw new StringConcatException('Unknown error.'); + } + $newString .= $constants[$counter]; + $counter++; + continue; + } + $newString .= $char; + } + + [, $returnType] = Formatter::convertJavaNamespaceToPHP( + $concatType + ->getInvoker() + ->getDynamic() + ->getMethods() + ->call( + 'returnType' + ) + ); + + return JavaClass::load('java.lang.String') + ->getInvoker() + ->construct($newString) + ->getJavaClass(); + } +} diff --git a/src/Packages/java/lang/invoke/SwitchPoint.php b/src/Packages/java/lang/invoke/SwitchPoint.php new file mode 100644 index 00000000..0dc233fc --- /dev/null +++ b/src/Packages/java/lang/invoke/SwitchPoint.php @@ -0,0 +1,51 @@ +getInvoker() + ->getDynamic() + ->getMethods() + ->call('hashCode'); + } + + return $a->hashCode(); + } + + /** + * Returns true if the provided reference is null otherwise returns false. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/package-summary.html#isNull + * @param null|mixed $a + * @throws NotImplementedException + */ + public static function static_isNull($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns true if the provided reference is non-null otherwise returns false. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/package-summary.html#nonNull + * @param null|mixed $a + * @throws NotImplementedException + */ + public static function static_nonNull($a = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Checks that the specified object reference is not null. + * Checks that the specified object reference is not null and throws a customized NullPointerException if it is. + * Checks that the specified object reference is not null and throws a customized NullPointerException if it is. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/package-summary.html#requireNonNull + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public static function static_requireNonNull($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the first argument if it is non-null and otherwise returns the non-null second argument. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/package-summary.html#requireNonNullElse + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public static function static_requireNonNullElse($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the first argument if it is non-null and otherwise returns the non-null value of supplier.get(). + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/package-summary.html#requireNonNullElseGet + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public static function static_requireNonNullElseGet($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns the result of calling toString for a non- null argument and "null" for a null argument. + * Returns the result of calling toString on the first argument if the first argument is not null and returns the second argument otherwise. + * + * @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/package-summary.html#toString + * @param null|mixed $a + * @param null|mixed $b + * @throws NotImplementedException + */ + public static function static_toString($a = null, $b = null) + { + throw new NotImplementedException(__METHOD__); + } +} diff --git a/src/Packages/java/util/Observable.php b/src/Packages/java/util/Observable.php new file mode 100644 index 00000000..43339ea9 --- /dev/null +++ b/src/Packages/java/util/Observable.php @@ -0,0 +1,113 @@ +logger = new Logger($channelName); + $this->options = $options; + $stream = new StreamHandler( + $this->options['log']['path'] ?? GlobalOptions::get('log.path') ?? Runtime::LOG_PATH, + $this->options['log']['level'] ?? GlobalOptions::get('log.level') ?? Runtime::LOG_LEVEL + ); + $stream->setFormatter( + new LineFormatter( + null, + null, + true, + true + ) + ); + $this->logger->pushHandler($stream); + } + + public function getLogLevel(): int + { + return $this->options['log']['level'] ?? GlobalOptions::get('log.level') ?? Runtime::LOG_LEVEL; + } + + public function getLogger(): Logger + { + return $this->logger; + } +} diff --git a/src/Utilities/Formatter.php b/src/Utilities/Formatter.php new file mode 100644 index 00000000..65435c13 --- /dev/null +++ b/src/Utilities/Formatter.php @@ -0,0 +1,281 @@ + TypeResolver::getMappedSignatureType($signature[$i]), + 'dimensions_of_array' => $dimensionsOfArray, + ]; + $dimensionsOfArray = 0; + break; + case 'L': + // class name + $build = ''; + // read to ; + for ($i++; $i < $size && $signature[$i] !== ';'; $i++) { + $build .= $signature[$i]; + } + [, $className] = Formatter::convertJavaNamespaceToPHP($build); + $data[] = [ + 'type' => $className, + 'dimensions_of_array' => $dimensionsOfArray, + ]; + $dimensionsOfArray = 0; + + break; + case '[': + // array + $dimensionsOfArray++; + for ($i++; $signature[$i] === '['; $i++) { + $dimensionsOfArray++; + } + // loop + continue 2; + case '(': + $build = ''; + // read to ) + for ($i++; $i < $size && $signature[$i] !== ')'; $i++) { + $build .= $signature[$i]; + } + $data['arguments'] = ($build !== '') ? static::parseSignature($build) : []; + $data['arguments_count'] = count($data['arguments']); + break; + } + $i++; + } + return $data; + } + + public static function buildArgumentsSignature(array $signatures): string + { + $string = ''; + foreach ($signatures as $signature) { + $build = str_repeat('[', $signature['dimensions_of_array']); + if (!TypeResolver::isPrimitive($signature['type'])) { + $build .= 'L' . str_replace('/', '.', $signature['type']); + } else { + $build .= TypeResolver::resolve($signature['type']); + } + $string .= $build . ';'; + } + return $string; + } + + public static function signatureConvertToAmbiguousForPHP(array $signatures): array + { + $result = []; + foreach ($signatures as $signature) { + $type = $signature['type']; + if (!TypeResolver::isPrimitive($type)) { + $result[] = $signature; + continue; + } + $type = TypeResolver::convertJavaTypeSimplyForPHP($type); + if ($type === String_::class) { + $result[] = [ + 'type' => $type, + 'dimensions_of_array' => $signature['dimensions_of_array'], + ]; + continue; + } + $signature['type'] = $type; + $result[] = $signature; + } + return $result; + } + + public static function buildSignature(string $type, int $dimensionsOfArray): string + { + $hasSignatureType = array_flip(TypeResolver::SIGNATURE_MAP)[$type] ?? null; + return str_repeat('[', $dimensionsOfArray) . ($hasSignatureType ?? 'L' . static::convertPHPNamespacesToJava($type) . ';'); + } + + public static function formatClassPath(string $className) + { + return str_replace('.', '/', $className); + } + + public static function convertPHPNamespacesToJava(string $className, string $mergeChar = '.'): string + { + $className = Formatter::formatClassPath(str_replace('/', '\\', $className)); + if (TypeResolver::isPrimitive($className)) { + return $className; + } + $newClassName = explode( + '.', + str_replace( + [ltrim(Runtime::PHP_PACKAGES_NAMESPACE, '\\') . '\\', '\\'], + ['', '.'], + ltrim($className, '\\') + ) + ); + foreach ($newClassName as $key => $value) { + $newClassName[$key] = array_flip(Runtime::PHP_PACKAGES_MAPS)[$value] ?? $value; + } + + return implode($mergeChar, $newClassName); + } + + public static function convertPrimitiveValueToJavaSignature(string $className): ?string + { + $signatureMap = array_flip(TypeResolver::SIGNATURE_MAP); + $typeMap = array_flip(TypeResolver::TYPES_MAP); + + return $signatureMap[$typeMap[$className] ?? null] ?? null; + } + + public static function convertJavaNamespaceToPHP(string $className): array + { + $className = str_replace('.', '/', $className); + $newClassName = explode( + '/', + $className + ); + + foreach ($newClassName as $key => $value) { + $newClassName[$key] = Runtime::PHP_PACKAGES_MAPS[$value] ?? $value; + } + + $newClassName = explode('$', implode('\\', $newClassName)); + $inPackage = Runtime::PHP_PACKAGES_NAMESPACE . '\\' . $newClassName[0]; + if (class_exists($inPackage)) { + return [static::BUILT_IN_PACKAGE, $inPackage]; + } + return [static::USER_DEFINED_PACKAGE, implode('$', $newClassName)]; + } + + /** + * @throws \PHPJava\Exceptions\TypeException + */ + public static function beatifyMethodFromConstantPool(MethodInfo $method, ConstantPool $constantPool): string + { + $cpInfo = $constantPool->getEntries(); + $methodAccessFlags = $method->getAccessFlag(); + $accessFlags = []; + $accessFlag = new MethodAccessFlag(); + foreach ($accessFlag->getValues() as $value) { + if (($methodAccessFlags & $value) !== 0) { + $accessFlags[] = strtolower(str_replace('ACC_', '', $accessFlag->getName($value))); + } + } + + $methodName = $cpInfo[$method->getNameIndex()]->getString(); + $descriptor = Formatter::parseSignature($cpInfo[$method->getDescriptorIndex()]->getString()); + $formattedArguments = str_replace( + '/', + '.', + implode( + ', ', + array_map( + function ($argument) { + return static::convertVisuallyJavaSignature($argument['type']) . str_repeat('[]', $argument['dimensions_of_array']); + }, + $descriptor['arguments'] + ) + ) + ); + + $type = str_replace('/', '.', static::convertVisuallyJavaSignature($descriptor[0]['type'])); + $methodAccessibility = implode(' ', $accessFlags); + + return ltrim("{$methodAccessibility} {$type} {$methodName}({$formattedArguments})"); + } + + public static function convertVisuallyJavaSignature(string $type): string + { + $convertedType = static::convertPHPNamespacesToJava($type); + $typeMap = array_flip(TypeResolver::TYPES_MAP); + return $typeMap[$convertedType] ?? $convertedType; + } + + public static function beatifyOperandStackItems(array $operandStacks = []): string + { + $formattedItems = []; + + foreach ($operandStacks as $operandStack) { + if ($operandStack instanceof JavaClass) { + $formattedItems[] = '< class: ' . ((string) $operandStack->getClassName()) . ' >'; + continue; + } + if ($operandStack instanceof PrimitiveValueInterface) { + $formattedItems[] = '< primitive value: ' . ((string) $operandStack) . ' >'; + continue; + } + if ($operandStack instanceof \ArrayIterator) { + $formattedItems[] = '< array: ' . implode( + ', ', + array_map( + function ($value) { + return static::beatifyOperandStackItems([$value]); + }, + $operandStack->getArrayCopy() + ) + ) . ' >'; + continue; + } + $formattedItems[] = '< unknown: ' . ((string) $operandStack) . ' >'; + } + + return '[' . implode(', ', $formattedItems) . ']'; + } + + public static function getNamespaceAndClassName(string $classPath): array + { + $namespace = explode('.', $classPath); + $className = array_pop($namespace); + return [$namespace, $className]; + } + + public static function convertStringifiedPrimitiveTypeToKernelType(string $type) + { + return TypeResolver::TYPES_MAP[strtolower($type)] ?? $type; + } + + public static function convertPHPPrimitiveTypeToJavaType(string $phpPrimitiveType) + { + $convertedPrimitiveType = static::convertStringifiedPrimitiveTypeToKernelType( + $phpPrimitiveType + ); + if ($phpPrimitiveType !== $convertedPrimitiveType) { + return $convertedPrimitiveType; + } + switch ($phpPrimitiveType) { + case 'string': + return String_::class; + } + return $phpPrimitiveType; + } +} diff --git a/test.java b/test.java deleted file mode 100644 index fe561e60..00000000 --- a/test.java +++ /dev/null @@ -1,288 +0,0 @@ -class Test { - - long z = -22222222222222222L; - static int c = 100; - static String b = "Hello World"; - - /** - * test for "Integer" value - * - * @param value - * @return - */ - public int testInt (int value) { - - System.out.println(this.testPrivateInteger(value)); - return value; - } - - /** - * test for "Short" value - * - * @param value - * @return - */ - public short testShort (short value) { - return value; - } - - /** - * test for "Long" value - * - * @param value - * @return - */ - public long testLong (long value) { - return value; - } - - /** - * test for "Float" value - * - * @param value - * @return - */ - public float testLong (float value) { - return value; - } - - /** - * test for "Double" value - * - * @param value - * @return - */ - public double testLong (double value) { - return value; - } - - /** - * test for "Char" value - * - * @param value - * @return - */ - public char testChar (char value) { - return value; - } - - /** - * test for "Byte" value - * - * @param value - * @return - */ - public byte testByte (byte value) { - return value; - } - - /** - * test for "Boolean" value - * - * @param value - * @return - */ - public boolean testBoolean (boolean value) { - return value; - } - - /** - * test for "String" value - * - * @param value - * @return - */ - public String testString (String value) { - return value; - } - - /** - * main method - * - * @param args - */ - public static void main (String[] args) { - - String x = "String"; - - // new instance - Test _a = new Test(); - - // test call - // _a.javaTest(); - - - int t = 2; - //t *= 1; - //t = ~1; - t <<= 1; - System.out.println(t); - t >>= 1; - System.out.println(t); - t -= 1; - System.out.println(t); - t += 1; - System.out.println(t); - t = 1; - System.out.println(t); - t >>>= 1; - System.out.println(t); - t |= 1; - System.out.println(t); - t &= 1; - System.out.println(t); - t = 345321; - System.out.println((short) t); - - long t1 = 111; - t1 *= 1; - t1 = ~1; - t1 <<= 1; - t1 >>= 1; - t1 -= 1; - t1 += 1; - t1 = 1; - t1 >>>= 1; - t1 |= 1; - t1 &= 1; - - double t2 = 2; - t2 *= 1; - t2 = ~1; - t2 -= 1; - t2 += 1; - t2 = 1; - - /*boolean _b = false; - _b = true && true; - _b = true && false; - _b = true || true; - _b = true || false;*/ - - try { - - for (int i = 0; i < Test.c; i++) { - - StringBuilder b = new StringBuilder(); - - switch (i + 1) { - - case -1: - - b.append("a"); - - break; - case 1: - - b.append("b"); - - break; - case 2: - - b.append("c"); - - break; - - } - - if (!x.equals(i + "")) { - - - System.out.println("Test:" + Test.b + "/" + x + "*****" + i + "/" + b); - - } - - if (i == 10) { - - throw new NullPointerException(); - - } - - } - - } catch (NullPointerException e) { - - System.out.println("ぬるぷっぷー"); - - } - - String[] test = {"4", "5", "6"}; - for (String i : test) { - - System.out.println(i); - - } - - int[] test2 = {1, 2, 3}; - for (int i : test2) { - - System.out.println(i); - - } - - long[] test3 = {1L, 2L, 3L}; - for (long i : test3) { - - System.out.println(i); - - } - - double[] test4 = {3.4, 3.5, 3.6, 81263.12312321, -99}; - for (double i : test4) { - - System.out.println(i); - - } - - } - - public static String test (int n, String m, int l, int i, int v, int k) { - - int j = 1; - - for (; j <= 10; j++) { - - j++; - - } - - return "Java emulate by php " + n + "/" + m + "/" + l + "/" + i + "/" + v + "/" + k + "/" + j; - - } - - private int testPrivateInteger (int value) { - return value + 1 * 2 + 3; - } - - /*public void javaTest () { - - testClass _c = new testClass(); - _c.t(); - - } - - public class testClass { - - public void t () { - - System.out.println("testClass.t method." + Test.this.z); - - testClass2 _c = new testClass2(); - _c.t(); - - - } - - public class testClass2 { - - public void t () { - - System.out.println("testClass2.t method." + Test.this.z); - - } - - } - - }*/ - -} \ No newline at end of file diff --git a/test.php b/test.php deleted file mode 100644 index 48c8c2a8..00000000 --- a/test.php +++ /dev/null @@ -1,81 +0,0 @@ -construct(); - - // 動的メンバコールテスト - var_dump(get_class($invoker->z)); - - // 動的メンバ値変更&コールテスト - $invoker->z = 9999; - - // 格納されている値 - var_dump($invoker->z->getValue()); - - // 実際の値 - var_dump((string) $invoker->z); - - // 静的メンバコールテスト - var_dump(get_class($invoker->b)); - - // toString - var_dump((string) $invoker->b); - - // メインメソッドを呼ぶ - // $invoker->getMethodInvoker()->main(array(999, 888)); - - // testIntを呼ぶ - var_dump($invoker->testInt(1111)); - - // testIntを呼ぶ - var_dump((string) $invoker->testInt(1111)); - - // testString(java/lang/String)を呼ぶ - var_dump($invoker->testString("8888")); - // - // testString(java/lang/String)を呼ぶ - var_dump((string) $invoker->testString("8888")); - - $javaClass->trace(); - -/* - // $a = new JavaArchive('JavaTest/dist/JavaTest.jar'); - - // var_dump($a->getClass('javatest.JavaTest')->main(array(999, 888))); - $manipulator = new JavaManipulator(); - - $invoker = $manipulator->registerClass(new JavaClass('test.class')); - - // call main method - var_dump($invoker->main(array(999, 888))); - - // var_dump($invoker->test(999, 999, 999, 999, 999, 999)); - - $invoker->getClass()->trace();*/ - -} catch (Exception $e) { - - $javaClass->trace(); - - var_dump($e->getMessage(), $e->getFile(), $e->getLine()); - -} \ No newline at end of file diff --git a/tests/Cases/AccessDynamicFieldTest.php b/tests/Cases/AccessDynamicFieldTest.php new file mode 100644 index 00000000..86316fa6 --- /dev/null +++ b/tests/Cases/AccessDynamicFieldTest.php @@ -0,0 +1,38 @@ +getInvoker()->construct(); + $this->assertEquals(5, $constructed->getDynamic()->getFields()->get('number')->getValue()); + $this->assertEquals('Hello World', $constructed->getDynamic()->getFields()->get('string')); + } + + public function testOverwriteField() + { + $constructed = static::$initiatedJavaClasses['AccessDynamicFieldTest']->getInvoker()->construct(); + $constructed->getStatic()->getFields()->set('number', 1000); + $constructed->getStatic()->getFields()->set('string', 'New String!'); + $this->assertEquals(1000, $constructed->getStatic()->getFields()->get('number')); + $this->assertEquals('New String!', $constructed->getStatic()->getFields()->get('string')); + } + + public function testAffectedNewConstructingTest() + { + $constructed = static::$initiatedJavaClasses['AccessDynamicFieldTest']->getInvoker()->construct(); + $constructed->getStatic()->getFields()->set('number', 1000); + $constructed->getStatic()->getFields()->set('string', 'New String!'); + + // affected assertion + $constructed = static::$initiatedJavaClasses['AccessDynamicFieldTest']->getInvoker()->construct(); + $this->assertEquals(5, $constructed->getDynamic()->getFields()->get('number')->getValue()); + $this->assertEquals('Hello World', $constructed->getDynamic()->getFields()->get('string')); + } +} diff --git a/tests/Cases/AccessDynamicMethodTest.php b/tests/Cases/AccessDynamicMethodTest.php new file mode 100644 index 00000000..35070b90 --- /dev/null +++ b/tests/Cases/AccessDynamicMethodTest.php @@ -0,0 +1,59 @@ +getInvoker() + ->construct() + ->getDynamic() + ->getMethods() + ->call( + 'main', + ['Hello', 'World'] + ); + $result = Output::getHeapspace(); + + $this->assertEquals('HelloWorld', $result); + } + + public function testCallMainHavingIntegerArguments() + { + // call main + static::$initiatedJavaClasses['AccessDynamicMethodTest'] + ->getInvoker() + ->construct() + ->getDynamic() + ->getMethods() + ->call( + 'main', + [1234, 5678] + ); + $result = Output::getHeapspace(); + + $this->assertEquals(246811356, $result); + } + + public function testCallReturnTest() + { + // call main + $result = static::$initiatedJavaClasses['AccessDynamicMethodTest'] + ->getInvoker() + ->construct() + ->getDynamic() + ->getMethods() + ->call('returnTest'); + + $this->assertEquals('Return Test.', $result); + } +} diff --git a/tests/Cases/AccessStaticFieldTest.php b/tests/Cases/AccessStaticFieldTest.php new file mode 100644 index 00000000..fa676b18 --- /dev/null +++ b/tests/Cases/AccessStaticFieldTest.php @@ -0,0 +1,25 @@ +assertEquals(5, static::$initiatedJavaClasses['AccessStaticFieldTest']->getInvoker()->getStatic()->getFields()->get('number')->getValue()); + $this->assertEquals('Hello World', static::$initiatedJavaClasses['AccessStaticFieldTest']->getInvoker()->getStatic()->getFields()->get('string')); + } + + public function testOverwriteField() + { + $static = static::$initiatedJavaClasses['AccessStaticFieldTest']->getInvoker()->getStatic(); + $static->getFields()->set('number', 1000); + $static->getFields()->set('string', 'New String!'); + $this->assertEquals(1000, $static->getFields()->get('number')); + $this->assertEquals('New String!', $static->getFields()->get('string')); + } +} diff --git a/tests/Cases/AccessStaticMethodTest.php b/tests/Cases/AccessStaticMethodTest.php new file mode 100644 index 00000000..dcfae093 --- /dev/null +++ b/tests/Cases/AccessStaticMethodTest.php @@ -0,0 +1,56 @@ +getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'main', + ['Hello', 'World'] + ); + $result = Output::getHeapspace(); + + $this->assertEquals('HelloWorld', $result); + } + + public function testCallMainHavingIntegerArguments() + { + // call main + static::$initiatedJavaClasses['AccessStaticMethodTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'main', + [1234, 5678] + ); + $result = Output::getHeapspace(); + + $this->assertEquals(246811356, $result); + } + + public function testCallReturnTest() + { + // call main + $result = static::$initiatedJavaClasses['AccessStaticMethodTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call('returnTest'); + + $this->assertEquals('Return Test.', $result); + } +} diff --git a/tests/Cases/ArrayTest.php b/tests/Cases/ArrayTest.php new file mode 100644 index 00000000..4a3e3832 --- /dev/null +++ b/tests/Cases/ArrayTest.php @@ -0,0 +1,114 @@ +getInvoker() + ->getStatic() + ->getMethods() + ->call($method); + } + + public function testCreateIntArray() + { + $actual = $this->call('createIntArray'); + + $this->assertEquals(3, $actual->count()); + $this->assertEquals(1, $actual->offsetGet(0)->getValue()); + $this->assertEquals(2, $actual->offsetGet(1)->getValue()); + $this->assertEquals(3, $actual->offsetGet(2)->getValue()); + } + + public function testCreateStringArray() + { + $actual = $this->call('createStringArray'); + + $this->assertEquals(3, $actual->count()); + $this->assertEquals('foo', $actual->offsetGet(0)); + $this->assertEquals('bar', $actual->offsetGet(1)); + $this->assertEquals('baz', $actual->offsetGet(2)); + } + + public function testCreateLongArray() + { + $actual = $this->call('createLongArray'); + + $this->assertEquals(3, $actual->count()); + $this->assertEquals('1', $actual->offsetGet(0)); + $this->assertEquals('2', $actual->offsetGet(1)); + $this->assertEquals('3', $actual->offsetGet(2)); + } + + public function testCreateFloatArray() + { + $actual = $this->call('createFloatArray'); + + $this->assertEquals(3, $actual->count()); + $this->assertEquals('1.5', $actual->offsetGet(0)); + $this->assertEquals('2.5', $actual->offsetGet(1)); + $this->assertEquals('3.5', $actual->offsetGet(2)); + } + + public function testCreateDoubleArray() + { + $actual = $this->call('createDoubleArray'); + + $this->assertEquals(3, $actual->count()); + $this->assertEquals('1.5', $actual->offsetGet(0)); + $this->assertEquals('2.5', $actual->offsetGet(1)); + $this->assertEquals('3.5', $actual->offsetGet(2)); + } + + public function testCreateBooleanArray() + { + $actual = $this->call('createBooleanArray'); + + $this->assertEquals(3, $actual->count()); + $this->assertEquals('true', $actual->offsetGet(0)); + $this->assertEquals('false', $actual->offsetGet(1)); + $this->assertEquals('true', $actual->offsetGet(2)); + } + + public function testCreateCharArray() + { + $actual = $this->call('createCharArray'); + + $this->assertEquals(3, $actual->count()); + $this->assertEquals('A', $actual->offsetGet(0)); + $this->assertEquals('B', $actual->offsetGet(1)); + $this->assertEquals('C', $actual->offsetGet(2)); + } + + public function testCreateByteArray() + { + $actual = $this->call('createByteArray'); + + $this->assertEquals(3, $actual->count()); + $this->assertEquals('1', $actual->offsetGet(0)); + $this->assertEquals('2', $actual->offsetGet(1)); + $this->assertEquals('3', $actual->offsetGet(2)); + } + + public function testMultiDimensionArrayWithConstants() + { + $actual = $this->call('multiDimensionArrayWithConstants'); + $this->assertEquals('Hello World!', $actual); + } + + public function testMultiDimensionArrayWithDynamic() + { + $actual = $this->call('multiDimensionArrayWithDynamic'); + $this->assertCount(3, $actual); + $this->assertEquals('Hello', (string) $actual[0]); + $this->assertEquals(' ', (string) $actual[1]); + $this->assertEquals('World!', (string) $actual[2]); + } +} diff --git a/tests/Cases/Base.php b/tests/Cases/Base.php new file mode 100644 index 00000000..ba99e4a6 --- /dev/null +++ b/tests/Cases/Base.php @@ -0,0 +1,63 @@ +fixtures as $fixture) { + if (isset(static::$initiatedJavaClasses[$fixture])) { + continue; + } + exec('javac -classpath ' . $pathRoot . ' -encoding UTF8 ' . $pathRoot . str_replace(['../', './'], '', $fixture) . '.java -d ' . __DIR__ . '/caches'); + static::$initiatedJavaClasses[$fixture] = JavaClass::load( + $fixture + ); + } + } + + public function tearDown(): void + { + Output::clearHeapspace(); + + parent::tearDown(); + } + + public function createJAR($name, $entrypoint, array $fixtures = []) + { + $classes = implode( + ' ', + array_map( + function ($fixture) { + return $fixture . '.class'; + }, + $fixtures + ) + ); + exec('cd ' . __DIR__ . '/caches && jar -cvfe ' . $name . ' ' . $entrypoint . ' ' . $classes); + return $this; + } + + protected function getClassName($fixtureName) + { + return __DIR__ . '/caches/' . $fixtureName . '.class'; + } +} diff --git a/tests/Cases/BigNumberCalculationTest.php b/tests/Cases/BigNumberCalculationTest.php new file mode 100644 index 00000000..9bbd03ac --- /dev/null +++ b/tests/Cases/BigNumberCalculationTest.php @@ -0,0 +1,66 @@ +getInvoker() + ->getStatic() + ->getMethods() + ->call($method, ...$arguments); + } + + public function testAdd() + { + $result = $this->call( + explode('::', __METHOD__)[1], + Long_::get(PHP_INT_MAX - 1), + Long_::get(1) + ); + + $this->assertEquals('9223372036854775807', (string) $result); + } + + public function testSub() + { + $result = $this->call( + explode('::', __METHOD__)[1], + Long_::get((string) BigInteger::of(PHP_INT_MAX)), + Long_::get(1) + ); + + $this->assertEquals('9223372036854775806', (string) $result); + } + + public function testMul() + { + $result = $this->call( + explode('::', __METHOD__)[1], + Long_::get(2147483647), + Long_::get(3) + ); + + $this->assertEquals('6442450941', (string) $result); + } + + public function testDiv() + { + $result = $this->call( + explode('::', __METHOD__)[1], + Long_::get(6442450947), + Long_::get(2147483649) + ); + + $this->assertEquals('3', (string) $result); + } +} diff --git a/tests/Cases/BinaryOperatorTest.php b/tests/Cases/BinaryOperatorTest.php new file mode 100644 index 00000000..6ebbeb27 --- /dev/null +++ b/tests/Cases/BinaryOperatorTest.php @@ -0,0 +1,164 @@ +getInvoker() + ->getStatic() + ->getMethods() + ->call($method, $value1, $value2); + + return $calculatedValue->getValue(); + } + + private function callWithLong($method, $value1, $value2) + { + $calculatedValue = static::$initiatedJavaClasses['BinaryOperatorTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + $method, + Long_::get($value1), + Long_::get($value2) + ); + + return $calculatedValue->getValue(); + } + + public function testIntAdd() + { + $actual = $this->call('intAdd', 5, 3); + $this->assertEquals(8, $actual); + } + + public function testIntSub() + { + $actual = $this->call('intSub', 5, 3); + $this->assertEquals(2, $actual); + } + + public function testIntMul() + { + $actual = $this->call('intMul', 5, 3); + $this->assertEquals(15, $actual); + } + + public function testIntShl() + { + $actual = $this->call('intShl', 3, 2); + $this->assertEquals(12, $actual); + } + + public function testIntShr() + { + $actual = $this->call('intShr', 12, 2); + $this->assertEquals(3, $actual); + } + + public function testIntUshr() + { + $actual = $this->call('intUshr', 12, 2); + $this->assertEquals(3, $actual); + } + + public function testIntAnd() + { + $value1 = (int) base_convert('0011', 2, 10); + $value2 = (int) base_convert('0101', 2, 10); + $expect = (int) base_convert('0001', 2, 10); + $actual = $this->call('intAnd', $value1, $value2); + $this->assertEquals($expect, $actual); + } + + public function testIntOr() + { + $value1 = (int) base_convert('0011', 2, 10); + $value2 = (int) base_convert('0101', 2, 10); + $expect = (int) base_convert('0111', 2, 10); + $actual = $this->call('intOr', $value1, $value2); + $this->assertEquals($expect, $actual); + } + + public function testIntXor() + { + $value1 = (int) base_convert('0011', 2, 10); + $value2 = (int) base_convert('0101', 2, 10); + $expect = (int) base_convert('0110', 2, 10); + $actual = $this->call('intXor', $value1, $value2); + $this->assertEquals($expect, $actual); + } + + public function testLongAdd() + { + $actual = $this->callWithLong('longAdd', 5, 3); + $this->assertEquals(8, $actual); + } + + public function testLongSub() + { + $actual = $this->callWithLong('longSub', 5, 3); + $this->assertEquals(2, $actual); + } + + public function testLongMul() + { + $actual = $this->callWithLong('longMul', 5, 3); + $this->assertEquals(15, $actual); + } + + public function testLongShl() + { + $actual = $this->callWithLong('longShl', 3, 2); + $this->assertEquals(12, $actual); + } + + public function testLongShr() + { + $actual = $this->callWithLong('longShr', 12, 2); + $this->assertEquals(3, $actual); + } + + public function testLongUshr() + { + $actual = $this->callWithLong('longUshr', 12, 2); + $this->assertEquals(3, $actual); + } + + public function testLongAnd() + { + $value1 = (int) base_convert('0011', 2, 10); + $value2 = (int) base_convert('0101', 2, 10); + $expect = (int) base_convert('0001', 2, 10); + $actual = $this->callWithLong('longAnd', $value1, $value2); + $this->assertEquals($expect, $actual); + } + + public function testLongOr() + { + $value1 = (int) base_convert('0011', 2, 10); + $value2 = (int) base_convert('0101', 2, 10); + $expect = (int) base_convert('0111', 2, 10); + $actual = $this->callWithLong('longOr', $value1, $value2); + $this->assertEquals($expect, $actual); + } + + public function testLongXor() + { + $value1 = (int) base_convert('0011', 2, 10); + $value2 = (int) base_convert('0101', 2, 10); + $expect = (int) base_convert('0110', 2, 10); + $actual = $this->callWithLong('longXor', $value1, $value2); + $this->assertEquals($expect, $actual); + } +} diff --git a/tests/Cases/BoundaryValueTypeForBooleanTest.php b/tests/Cases/BoundaryValueTypeForBooleanTest.php new file mode 100644 index 00000000..16fc03dd --- /dev/null +++ b/tests/Cases/BoundaryValueTypeForBooleanTest.php @@ -0,0 +1,91 @@ +assertStaticField( + 'true', + 'b0' + ); + } + + public function testStaticB1() + { + $this->assertStaticField( + 'false', + 'b1' + ); + } + + public function testDynamicB0() + { + $this->assertDynamicField( + 'true', + 'b0' + ); + } + + public function testDynamicB1() + { + $this->assertDynamicField( + 'false', + 'b1' + ); + } + + public function testStaticArrayBooleans() + { + $array = $this->getStaticField('s_a_b'); + $this->assertCount(2, $array); + + $this->assertEquals('true', (string) $array[0]); + $this->assertEquals('false', (string) $array[1]); + } + + public function testDynamicArrayBooleans() + { + $array = $this->getDynamicField('d_a_b'); + $this->assertCount(2, $array); + + $this->assertEquals('true', (string) $array[0]); + $this->assertEquals('false', (string) $array[1]); + } + + public function testStaticMultiDimensionArrayBooleans() + { + $array = $this->getStaticField('s_ma_b'); + $this->assertCount(2, $array); + $this->assertCount(2, $array[0]); + $this->assertCount(2, $array[1]); + + $this->assertEquals('true', (string) $array[0][0]); + $this->assertEquals('false', (string) $array[0][1]); + + $this->assertEquals('false', (string) $array[1][0]); + $this->assertEquals('true', (string) $array[1][1]); + } + + public function testDynamicMultiDimensionArrayBooleans() + { + $array = $this->getDynamicField('d_ma_b'); + $this->assertCount(2, $array); + $this->assertCount(2, $array[0]); + $this->assertCount(2, $array[1]); + + $this->assertEquals('true', (string) $array[0][0]); + $this->assertEquals('false', (string) $array[0][1]); + + $this->assertEquals('false', (string) $array[1][0]); + $this->assertEquals('true', (string) $array[1][1]); + } +} diff --git a/tests/Cases/BoundaryValueTypeForByteTest.php b/tests/Cases/BoundaryValueTypeForByteTest.php new file mode 100644 index 00000000..7049b52d --- /dev/null +++ b/tests/Cases/BoundaryValueTypeForByteTest.php @@ -0,0 +1,187 @@ +assertStaticField( + '0', + 'by0' + ); + } + + public function testStaticBy1() + { + $this->assertStaticField( + '127', + 'by1' + ); + } + + public function testStaticBy2() + { + $this->assertStaticField( + '18', + 'by2' + ); + } + + public function testStaticBy3() + { + $this->assertStaticField( + '-1', + 'by3' + ); + } + + public function testStaticBy4() + { + $this->assertStaticField( + '-127', + 'by4' + ); + } + + public function testStaticBy5() + { + $this->assertStaticField( + '-18', + 'by5' + ); + } + + public function testStaticBy6() + { + $this->assertStaticField( + '-128', + 'by6' + ); + } + + public function testDynamicBy0() + { + $this->assertDynamicField( + '0', + 'by0' + ); + } + + public function testDynamicBy1() + { + $this->assertDynamicField( + '127', + 'by1' + ); + } + + public function testDynamicBy2() + { + $this->assertDynamicField( + '18', + 'by2' + ); + } + + public function testDynamicBy3() + { + $this->assertDynamicField( + '-1', + 'by3' + ); + } + + public function testDynamicBy4() + { + $this->assertDynamicField( + '-127', + 'by4' + ); + } + + public function testDynamicBy5() + { + $this->assertDynamicField( + '-18', + 'by5' + ); + } + + public function testDynamicBy6() + { + $this->assertDynamicField( + '-128', + 'by6' + ); + } + + public function testStaticArrayBytes() + { + $array = $this->getStaticField('s_a_by'); + $this->assertCount(7, $array); + + $this->assertEquals('0', (string) $array[0]); + $this->assertEquals('127', (string) $array[1]); + $this->assertEquals('18', (string) $array[2]); + $this->assertEquals('-1', (string) $array[3]); + $this->assertEquals('-127', (string) $array[4]); + $this->assertEquals('-18', (string) $array[5]); + $this->assertEquals('-128', (string) $array[6]); + } + + public function testDynamicArrayBytes() + { + $array = $this->getDynamicField('d_a_by'); + $this->assertCount(7, $array); + + $this->assertEquals('0', (string) $array[0]); + $this->assertEquals('127', (string) $array[1]); + $this->assertEquals('18', (string) $array[2]); + $this->assertEquals('-1', (string) $array[3]); + $this->assertEquals('-127', (string) $array[4]); + $this->assertEquals('-18', (string) $array[5]); + $this->assertEquals('-128', (string) $array[6]); + } + + public function testStaticMultiDimensionArrayBytes() + { + $array = $this->getStaticField('s_ma_by'); + $this->assertCount(2, $array); + $this->assertCount(3, $array[0]); + $this->assertCount(4, $array[1]); + + $this->assertEquals('0', (string) $array[0][0]); + $this->assertEquals('127', (string) $array[0][1]); + $this->assertEquals('18', (string) $array[0][2]); + + $this->assertEquals('-1', (string) $array[1][0]); + $this->assertEquals('-127', (string) $array[1][1]); + $this->assertEquals('-18', (string) $array[1][2]); + $this->assertEquals('-128', (string) $array[1][3]); + } + + public function testDynamicMultiDimensionArrayBytes() + { + $array = $this->getDynamicField('d_ma_by'); + $this->assertCount(2, $array); + $this->assertCount(3, $array[0]); + $this->assertCount(4, $array[1]); + + $this->assertEquals('0', (string) $array[0][0]); + $this->assertEquals('127', (string) $array[0][1]); + $this->assertEquals('18', (string) $array[0][2]); + + $this->assertEquals('-1', (string) $array[1][0]); + $this->assertEquals('-127', (string) $array[1][1]); + $this->assertEquals('-18', (string) $array[1][2]); + $this->assertEquals('-128', (string) $array[1][3]); + } +} diff --git a/tests/Cases/BoundaryValueTypeForCharTest.php b/tests/Cases/BoundaryValueTypeForCharTest.php new file mode 100644 index 00000000..5b6740c0 --- /dev/null +++ b/tests/Cases/BoundaryValueTypeForCharTest.php @@ -0,0 +1,147 @@ +assertStaticField( + $this->makeUnicodeChar(0x0000), + 'c0' + ); + } + + public function testStaticC1() + { + $this->assertStaticField( + $this->makeUnicodeChar(0xFFFF), + 'c1' + ); + } + + public function testStaticC2() + { + $this->assertStaticField( + $this->makeUnicodeChar(0x7FFF), + 'c2' + ); + } + + public function testStaticC3() + { + $this->assertStaticField( + $this->makeUnicodeChar(0x8000), + 'c3' + ); + } + + public function testStaticC4() + { + $this->assertStaticField( + $this->makeUnicodeChar(0x9123), + 'c4' + ); + } + + public function testDynamicC0() + { + $this->assertDynamicField( + $this->makeUnicodeChar(0x0000), + 'c0' + ); + } + + public function testDynamicC1() + { + $this->assertDynamicField( + $this->makeUnicodeChar(0xFFFF), + 'c1' + ); + } + + public function testDynamicC2() + { + $this->assertDynamicField( + $this->makeUnicodeChar(0x7FFF), + 'c2' + ); + } + + public function testDynamicC3() + { + $this->assertDynamicField( + $this->makeUnicodeChar(0x8000), + 'c3' + ); + } + + public function testDynamicC4() + { + $this->assertDynamicField( + $this->makeUnicodeChar(0x9123), + 'c4' + ); + } + + public function testStaticArrayChars() + { + $array = $this->getStaticField('s_a_c'); + $this->assertCount(5, $array); + + $this->assertEquals($this->makeUnicodeChar(0x0000), (string) $array[0]); + $this->assertEquals($this->makeUnicodeChar(0xFFFF), (string) $array[1]); + $this->assertEquals($this->makeUnicodeChar(0x7FFF), (string) $array[2]); + $this->assertEquals($this->makeUnicodeChar(0x8000), (string) $array[3]); + $this->assertEquals($this->makeUnicodeChar(0x9123), (string) $array[4]); + } + + public function testDynamicArrayChars() + { + $array = $this->getDynamicField('d_a_c'); + $this->assertCount(5, $array); + + $this->assertEquals($this->makeUnicodeChar(0x0000), (string) $array[0]); + $this->assertEquals($this->makeUnicodeChar(0xFFFF), (string) $array[1]); + $this->assertEquals($this->makeUnicodeChar(0x7FFF), (string) $array[2]); + $this->assertEquals($this->makeUnicodeChar(0x8000), (string) $array[3]); + $this->assertEquals($this->makeUnicodeChar(0x9123), (string) $array[4]); + } + + public function testStaticMultiDimensionArrayChars() + { + $array = $this->getStaticField('s_ma_c'); + $this->assertCount(2, $array); + $this->assertCount(3, $array[0]); + $this->assertCount(2, $array[1]); + + $this->assertEquals($this->makeUnicodeChar(0x0000), (string) $array[0][0]); + $this->assertEquals($this->makeUnicodeChar(0xFFFF), (string) $array[0][1]); + $this->assertEquals($this->makeUnicodeChar(0x7FFF), (string) $array[0][2]); + $this->assertEquals($this->makeUnicodeChar(0x8000), (string) $array[1][0]); + $this->assertEquals($this->makeUnicodeChar(0x9123), (string) $array[1][1]); + } + + public function testDynamicMultiDimensionArrayChars() + { + $array = $this->getDynamicField('d_ma_c'); + $this->assertCount(2, $array); + $this->assertCount(3, $array[0]); + $this->assertCount(2, $array[1]); + + $this->assertEquals($this->makeUnicodeChar(0x0000), (string) $array[0][0]); + $this->assertEquals($this->makeUnicodeChar(0xFFFF), (string) $array[0][1]); + $this->assertEquals($this->makeUnicodeChar(0x7FFF), (string) $array[0][2]); + $this->assertEquals($this->makeUnicodeChar(0x8000), (string) $array[1][0]); + $this->assertEquals($this->makeUnicodeChar(0x9123), (string) $array[1][1]); + } +} diff --git a/tests/Cases/BoundaryValueTypeForDoubleTest.php b/tests/Cases/BoundaryValueTypeForDoubleTest.php new file mode 100644 index 00000000..3aff03d0 --- /dev/null +++ b/tests/Cases/BoundaryValueTypeForDoubleTest.php @@ -0,0 +1,379 @@ +assertStaticField( + '0', + 'd0' + ); + } + + public function testStaticD1() + { + $this->assertStaticField( + '1234', + 'd1' + ); + } + + public function testStaticD2() + { + $this->assertStaticField( + '0.1234', + 'd2' + ); + } + + public function testStaticD3() + { + $this->assertStaticField( + '1234', + 'd3' + ); + } + + public function testStaticD4() + { + $this->assertStaticField( + '1234.1234', + 'd4' + ); + } + + public function testStaticD5() + { + $this->assertStaticField( + '-1234', + 'd5' + ); + } + + public function testStaticD6() + { + $this->assertStaticField( + '-0.1234', + 'd6' + ); + } + + public function testStaticD7() + { + $this->assertStaticField( + '-1234', + 'd7' + ); + } + + public function testStaticD8() + { + $this->assertStaticField( + '-1234.1234', + 'd8' + ); + } + + public function testStaticD9() + { + $this->assertStaticField( + '2139095039', + 'd9' + ); + } + + public function testStaticD10() + { + $this->assertStaticField( + '-2139095039', + 'd10' + ); + } + + public function testStaticD11() + { + $this->assertStaticField( + '-8388609', + 'd11' + ); + } + + public function testStaticD12() + { + $this->assertStaticField( + '8388609', + 'd12' + ); + } + + public function testStaticD13() + { + $this->assertStaticField( + '-4.5035996273705E+15', + 'd13' + ); + } + + public function testStaticD14() + { + $this->assertStaticField( + '4.5035996273705E+15', + 'd14' + ); + } + + public function testStaticD15() + { + $this->assertStaticField( + '9.2188684372274E+18', + 'd15' + ); + } + + public function testStaticD16() + { + $this->assertStaticField( + '-9.2188684372274E+18', + 'd16' + ); + } + + public function testDynamicD1() + { + $this->assertDynamicField( + '1234', + 'd1' + ); + } + + public function testDynamicD2() + { + $this->assertDynamicField( + '0.1234', + 'd2' + ); + } + + public function testDynamicD3() + { + $this->assertDynamicField( + '1234', + 'd3' + ); + } + + public function testDynamicD4() + { + $this->assertDynamicField( + '1234.1234', + 'd4' + ); + } + + public function testDynamicD5() + { + $this->assertDynamicField( + '-1234', + 'd5' + ); + } + + public function testDynamicD6() + { + $this->assertDynamicField( + '-0.1234', + 'd6' + ); + } + + public function testDynamicD7() + { + $this->assertDynamicField( + '-1234', + 'd7' + ); + } + + public function testDynamicD8() + { + $this->assertDynamicField( + '-1234.1234', + 'd8' + ); + } + + public function testDynamicD9() + { + $this->assertDynamicField( + '2139095039', + 'd9' + ); + } + + public function testDynamicD10() + { + $this->assertDynamicField( + '-2139095039', + 'd10' + ); + } + + public function testDynamicD11() + { + $this->assertDynamicField( + '-8388609', + 'd11' + ); + } + + public function testDynamicD12() + { + $this->assertDynamicField( + '8388609', + 'd12' + ); + } + + public function testDynamicD13() + { + $this->assertDynamicField( + '-4.5035996273705E+15', + 'd13' + ); + } + + public function testDynamicD14() + { + $this->assertDynamicField( + '4.5035996273705E+15', + 'd14' + ); + } + + public function testDynamicD15() + { + $this->assertDynamicField( + '9.2188684372274E+18', + 'd15' + ); + } + + public function testDynamicD16() + { + $this->assertDynamicField( + '-9.2188684372274E+18', + 'd16' + ); + } + + public function testStaticArrayDoubles() + { + $array = $this->getStaticField('s_a_d'); + $this->assertCount(17, $array); + + $this->assertEquals('0', (string) $array[0]); + $this->assertEquals('1234', (string) $array[1]); + $this->assertEquals('0.1234', (string) $array[2]); + $this->assertEquals('1234', (string) $array[3]); + $this->assertEquals('1234.1234', (string) $array[4]); + $this->assertEquals('-1234', (string) $array[5]); + $this->assertEquals('-0.1234', (string) $array[6]); + $this->assertEquals('-1234', (string) $array[7]); + $this->assertEquals('-1234.1234', (string) $array[8]); + $this->assertEquals('2139095039', (string) $array[9]); + $this->assertEquals('-2139095039', (string) $array[10]); + $this->assertEquals('-8388609', (string) $array[11]); + $this->assertEquals('8388609', (string) $array[12]); + $this->assertEquals('-4.5035996273705E+15', (string) $array[13]); + $this->assertEquals('4.5035996273705E+15', (string) $array[14]); + $this->assertEquals('9.2188684372274E+18', (string) $array[15]); + $this->assertEquals('-9.2188684372274E+18', (string) $array[16]); + } + + public function testDynamicArrayDoubles() + { + $array = $this->getDynamicField('d_a_d'); + $this->assertCount(17, $array); + + $this->assertEquals('0', (string) $array[0]); + $this->assertEquals('1234', (string) $array[1]); + $this->assertEquals('0.1234', (string) $array[2]); + $this->assertEquals('1234', (string) $array[3]); + $this->assertEquals('1234.1234', (string) $array[4]); + $this->assertEquals('-1234', (string) $array[5]); + $this->assertEquals('-0.1234', (string) $array[6]); + $this->assertEquals('-1234', (string) $array[7]); + $this->assertEquals('-1234.1234', (string) $array[8]); + $this->assertEquals('2139095039', (string) $array[9]); + $this->assertEquals('-2139095039', (string) $array[10]); + $this->assertEquals('-8388609', (string) $array[11]); + $this->assertEquals('8388609', (string) $array[12]); + $this->assertEquals('-4.5035996273705E+15', (string) $array[13]); + $this->assertEquals('4.5035996273705E+15', (string) $array[14]); + $this->assertEquals('9.2188684372274E+18', (string) $array[15]); + $this->assertEquals('-9.2188684372274E+18', (string) $array[16]); + } + + public function testStaticMultiDimensionArrayDoubles() + { + $array = $this->getStaticField('s_ma_d'); + $this->assertCount(2, $array); + $this->assertCount(8, $array[0]); + $this->assertCount(9, $array[1]); + + $this->assertEquals('0', (string) $array[0][0]); + $this->assertEquals('1234', (string) $array[0][1]); + $this->assertEquals('0.1234', (string) $array[0][2]); + $this->assertEquals('1234', (string) $array[0][3]); + $this->assertEquals('1234.1234', (string) $array[0][4]); + $this->assertEquals('-1234', (string) $array[0][5]); + $this->assertEquals('-4.5035996273705E+15', (string) $array[0][6]); + $this->assertEquals('4.5035996273705E+15', (string) $array[0][7]); + + $this->assertEquals('-0.1234', (string) $array[1][0]); + $this->assertEquals('-1234', (string) $array[1][1]); + $this->assertEquals('-1234.1234', (string) $array[1][2]); + $this->assertEquals('2139095039', (string) $array[1][3]); + $this->assertEquals('-2139095039', (string) $array[1][4]); + $this->assertEquals('-8388609', (string) $array[1][5]); + $this->assertEquals('8388609', (string) $array[1][6]); + $this->assertEquals('9.2188684372274E+18', (string) $array[1][7]); + $this->assertEquals('-9.2188684372274E+18', (string) $array[1][8]); + } + + public function testDynamicMultiDimensionArrayDoubles() + { + $array = $this->getDynamicField('d_ma_d'); + $this->assertCount(2, $array); + $this->assertCount(8, $array[0]); + $this->assertCount(9, $array[1]); + + $this->assertEquals('0', (string) $array[0][0]); + $this->assertEquals('1234', (string) $array[0][1]); + $this->assertEquals('0.1234', (string) $array[0][2]); + $this->assertEquals('1234', (string) $array[0][3]); + $this->assertEquals('1234.1234', (string) $array[0][4]); + $this->assertEquals('-1234', (string) $array[0][5]); + $this->assertEquals('-4.5035996273705E+15', (string) $array[0][6]); + $this->assertEquals('4.5035996273705E+15', (string) $array[0][7]); + + $this->assertEquals('-0.1234', (string) $array[1][0]); + $this->assertEquals('-1234', (string) $array[1][1]); + $this->assertEquals('-1234.1234', (string) $array[1][2]); + $this->assertEquals('2139095039', (string) $array[1][3]); + $this->assertEquals('-2139095039', (string) $array[1][4]); + $this->assertEquals('-8388609', (string) $array[1][5]); + $this->assertEquals('8388609', (string) $array[1][6]); + $this->assertEquals('9.2188684372274E+18', (string) $array[1][7]); + $this->assertEquals('-9.2188684372274E+18', (string) $array[1][8]); + } +} diff --git a/tests/Cases/BoundaryValueTypeForFloatTest.php b/tests/Cases/BoundaryValueTypeForFloatTest.php new file mode 100644 index 00000000..74330e49 --- /dev/null +++ b/tests/Cases/BoundaryValueTypeForFloatTest.php @@ -0,0 +1,305 @@ +assertStaticField( + '0', + 'f0' + ); + } + + public function testStaticF1() + { + $this->assertStaticField( + '1234', + 'f1' + ); + } + + public function testStaticF2() + { + $this->assertStaticField( + '0.12340000271797', + 'f2' + ); + } + + public function testStaticF3() + { + $this->assertStaticField( + '1234', + 'f3' + ); + } + + public function testStaticF4() + { + $this->assertStaticField( + '1234.1234130859', + 'f4' + ); + } + + public function testStaticF5() + { + $this->assertStaticField( + '-1234', + 'f5' + ); + } + + public function testStaticF6() + { + $this->assertStaticField( + '-0.12340000271797', + 'f6' + ); + } + + public function testStaticF7() + { + $this->assertStaticField( + '-1234', + 'f7' + ); + } + + public function testStaticF8() + { + $this->assertStaticField( + '-1234.1234130859', + 'f8' + ); + } + + public function testStaticF9() + { + $this->assertStaticField( + '2139095040', + 'f9' + ); + } + + public function testStaticF10() + { + $this->assertStaticField( + '-2139095040', + 'f10' + ); + } + + public function testStaticF11() + { + $this->assertStaticField( + '-8388609', + 'f11' + ); + } + + public function testStaticF12() + { + $this->assertStaticField( + '8388609', + 'f12' + ); + } + + public function testDynamicF0() + { + $this->assertDynamicField( + '0', + 'f0' + ); + } + + public function testDynamicF1() + { + $this->assertDynamicField( + '1234', + 'f1' + ); + } + + public function testDynamicF2() + { + $this->assertDynamicField( + '0.12340000271797', + 'f2' + ); + } + + public function testDynamicF3() + { + $this->assertDynamicField( + '1234', + 'f3' + ); + } + + public function testDynamicF4() + { + $this->assertDynamicField( + '1234.1234130859', + 'f4' + ); + } + + public function testDynamicF5() + { + $this->assertDynamicField( + '-1234', + 'f5' + ); + } + + public function testDynamicF6() + { + $this->assertDynamicField( + '-0.12340000271797', + 'f6' + ); + } + + public function testDynamicF7() + { + $this->assertDynamicField( + '-1234', + 'f7' + ); + } + + public function testDynamicF8() + { + $this->assertDynamicField( + '-1234.1234130859', + 'f8' + ); + } + + public function testDynamicF9() + { + $this->assertDynamicField( + '2139095040', + 'f9' + ); + } + + public function testDynamicF10() + { + $this->assertDynamicField( + '-2139095040', + 'f10' + ); + } + + public function testDynamicF11() + { + $this->assertDynamicField( + '-8388609', + 'f11' + ); + } + + public function testDynamicF12() + { + $this->assertDynamicField( + '8388609', + 'f12' + ); + } + + public function testStaticArrayFloats() + { + $array = $this->getStaticField('s_a_f'); + $this->assertCount(13, $array); + + $this->assertEquals('0', (string) $array[0]); + $this->assertEquals('1234', (string) $array[1]); + $this->assertEquals('0.12340000271797', (string) $array[2]); + $this->assertEquals('1234', (string) $array[3]); + $this->assertEquals('1234.1234130859', (string) $array[4]); + $this->assertEquals('-1234', (string) $array[5]); + $this->assertEquals('-0.12340000271797', (string) $array[6]); + $this->assertEquals('-1234', (string) $array[7]); + $this->assertEquals('-1234.1234130859', (string) $array[8]); + $this->assertEquals('2139095040', (string) $array[9]); + $this->assertEquals('-2139095040', (string) $array[10]); + $this->assertEquals('-8388609', (string) $array[11]); + $this->assertEquals('8388609', (string) $array[12]); + } + + public function testDynamicArrayFloats() + { + $array = $this->getDynamicField('d_a_f'); + $this->assertCount(13, $array); + + $this->assertEquals('0', (string) $array[0]); + $this->assertEquals('1234', (string) $array[1]); + $this->assertEquals('0.12340000271797', (string) $array[2]); + $this->assertEquals('1234', (string) $array[3]); + $this->assertEquals('1234.1234130859', (string) $array[4]); + $this->assertEquals('-1234', (string) $array[5]); + $this->assertEquals('-0.12340000271797', (string) $array[6]); + $this->assertEquals('-1234', (string) $array[7]); + $this->assertEquals('-1234.1234130859', (string) $array[8]); + $this->assertEquals('2139095040', (string) $array[9]); + $this->assertEquals('-2139095040', (string) $array[10]); + $this->assertEquals('-8388609', (string) $array[11]); + $this->assertEquals('8388609', (string) $array[12]); + } + + public function testStaticMultiDimensionArrayFloats() + { + $array = $this->getStaticField('s_ma_f'); + $this->assertCount(2, $array); + $this->assertCount(6, $array[0]); + $this->assertCount(7, $array[1]); + + $this->assertEquals('0', (string) $array[0][0]); + $this->assertEquals('1234', (string) $array[0][1]); + $this->assertEquals('0.12340000271797', (string) $array[0][2]); + $this->assertEquals('1234', (string) $array[0][3]); + $this->assertEquals('1234.1234130859', (string) $array[0][4]); + $this->assertEquals('-1234', (string) $array[0][5]); + $this->assertEquals('-0.12340000271797', (string) $array[1][0]); + $this->assertEquals('-1234', (string) $array[1][1]); + $this->assertEquals('-1234.1234130859', (string) $array[1][2]); + $this->assertEquals('2139095040', (string) $array[1][3]); + $this->assertEquals('-2139095040', (string) $array[1][4]); + $this->assertEquals('-8388609', (string) $array[1][5]); + $this->assertEquals('8388609', (string) $array[1][6]); + } + + public function testDynamicMultiDimensionArrayFloats() + { + $array = $this->getDynamicField('d_ma_f'); + $this->assertCount(2, $array); + $this->assertCount(6, $array[0]); + $this->assertCount(7, $array[1]); + + $this->assertEquals('0', (string) $array[0][0]); + $this->assertEquals('1234', (string) $array[0][1]); + $this->assertEquals('0.12340000271797', (string) $array[0][2]); + $this->assertEquals('1234', (string) $array[0][3]); + $this->assertEquals('1234.1234130859', (string) $array[0][4]); + $this->assertEquals('-1234', (string) $array[0][5]); + $this->assertEquals('-0.12340000271797', (string) $array[1][0]); + $this->assertEquals('-1234', (string) $array[1][1]); + $this->assertEquals('-1234.1234130859', (string) $array[1][2]); + $this->assertEquals('2139095040', (string) $array[1][3]); + $this->assertEquals('-2139095040', (string) $array[1][4]); + $this->assertEquals('-8388609', (string) $array[1][5]); + $this->assertEquals('8388609', (string) $array[1][6]); + } +} diff --git a/tests/Cases/BoundaryValueTypeForIntTest.php b/tests/Cases/BoundaryValueTypeForIntTest.php new file mode 100644 index 00000000..4775b513 --- /dev/null +++ b/tests/Cases/BoundaryValueTypeForIntTest.php @@ -0,0 +1,225 @@ +assertStaticField( + '1234', + 'i0' + ); + } + + public function testStaticI1() + { + $this->assertStaticField( + '32767', + 'i1' + ); + } + + public function testStaticI2() + { + $this->assertStaticField( + '32768', + 'i2' + ); + } + + public function testStaticI3() + { + $this->assertStaticField( + '2147483647', + 'i3' + ); + } + + public function testStaticI4() + { + $this->assertStaticField( + '-1', + 'i4' + ); + } + + public function testStaticI5() + { + $this->assertStaticField( + '-1234', + 'i5' + ); + } + + public function testStaticI6() + { + $this->assertStaticField( + '-2147483648', + 'i6' + ); + } + + public function testStaticI7() + { + $this->assertStaticField( + '-32768', + 'i7' + ); + } + + public function testStaticI8() + { + $this->assertStaticField( + '0', + 'i8' + ); + } + + public function testDynamicI0() + { + $this->assertDynamicField( + '1234', + 'i0' + ); + } + + public function testDynamicI1() + { + $this->assertDynamicField( + '32767', + 'i1' + ); + } + + public function testDynamicI2() + { + $this->assertDynamicField( + '32768', + 'i2' + ); + } + + public function testDynamicI3() + { + $this->assertDynamicField( + '2147483647', + 'i3' + ); + } + + public function testDynamicI4() + { + $this->assertDynamicField( + '-1', + 'i4' + ); + } + + public function testDynamicI5() + { + $this->assertDynamicField( + '-1234', + 'i5' + ); + } + + public function testDynamicI6() + { + $this->assertDynamicField( + '-2147483648', + 'i6' + ); + } + + public function testDynamicI7() + { + $this->assertDynamicField( + '-32768', + 'i7' + ); + } + + public function testDynamicI8() + { + $this->assertDynamicField( + '0', + 'i8' + ); + } + + public function testStaticArrayIntegers() + { + $array = $this->getStaticField('s_a_i'); + $this->assertCount(9, $array); + + $this->assertEquals('1234', (string) $array[0]); + $this->assertEquals('32767', (string) $array[1]); + $this->assertEquals('32768', (string) $array[2]); + $this->assertEquals('2147483647', (string) $array[3]); + $this->assertEquals('-1', (string) $array[4]); + $this->assertEquals('-1234', (string) $array[5]); + $this->assertEquals('-2147483648', (string) $array[6]); + $this->assertEquals('-32768', (string) $array[7]); + $this->assertEquals('0', (string) $array[8]); + } + + public function testDynamicArrayIntegers() + { + $array = $this->getDynamicField('d_a_i'); + $this->assertCount(9, $array); + + $this->assertEquals('1234', (string) $array[0]); + $this->assertEquals('32767', (string) $array[1]); + $this->assertEquals('32768', (string) $array[2]); + $this->assertEquals('2147483647', (string) $array[3]); + $this->assertEquals('-1', (string) $array[4]); + $this->assertEquals('-1234', (string) $array[5]); + $this->assertEquals('-2147483648', (string) $array[6]); + $this->assertEquals('-32768', (string) $array[7]); + $this->assertEquals('0', (string) $array[8]); + } + + public function testStaticMultiDimensionArrayIntegers() + { + $array = $this->getStaticField('s_ma_i'); + $this->assertCount(2, $array); + $this->assertCount(4, $array[0]); + $this->assertCount(5, $array[1]); + + $this->assertEquals('1234', (string) $array[0][0]); + $this->assertEquals('32767', (string) $array[0][1]); + $this->assertEquals('32768', (string) $array[0][2]); + $this->assertEquals('2147483647', (string) $array[0][3]); + $this->assertEquals('-1', (string) $array[1][0]); + $this->assertEquals('-1234', (string) $array[1][1]); + $this->assertEquals('-2147483648', (string) $array[1][2]); + $this->assertEquals('-32768', (string) $array[1][3]); + $this->assertEquals('0', (string) $array[1][4]); + } + + public function testDynamicMultiDimensionArrayIntegers() + { + $array = $this->getDynamicField('d_ma_i'); + $this->assertCount(2, $array); + $this->assertCount(4, $array[0]); + $this->assertCount(5, $array[1]); + + $this->assertEquals('1234', (string) $array[0][0]); + $this->assertEquals('32767', (string) $array[0][1]); + $this->assertEquals('32768', (string) $array[0][2]); + $this->assertEquals('2147483647', (string) $array[0][3]); + $this->assertEquals('-1', (string) $array[1][0]); + $this->assertEquals('-1234', (string) $array[1][1]); + $this->assertEquals('-2147483648', (string) $array[1][2]); + $this->assertEquals('-32768', (string) $array[1][3]); + $this->assertEquals('0', (string) $array[1][4]); + } +} diff --git a/tests/Cases/BoundaryValueTypeForLongTest.php b/tests/Cases/BoundaryValueTypeForLongTest.php new file mode 100644 index 00000000..6a6b1121 --- /dev/null +++ b/tests/Cases/BoundaryValueTypeForLongTest.php @@ -0,0 +1,267 @@ +assertStaticField( + '1234', + 'l0' + ); + } + + public function testStaticL1() + { + $this->assertStaticField( + '32767', + 'l1' + ); + } + + public function testStaticL2() + { + $this->assertStaticField( + '32768', + 'l2' + ); + } + + public function testStaticL3() + { + $this->assertStaticField( + '2147483647', + 'l3' + ); + } + + public function testStaticL4() + { + $this->assertStaticField( + '-1', + 'l4' + ); + } + + public function testStaticL5() + { + $this->assertStaticField( + '-1234', + 'l5' + ); + } + + public function testStaticL6() + { + $this->assertStaticField( + '-2147483648', + 'l6' + ); + } + + public function testStaticL7() + { + $this->assertStaticField( + '-32768', + 'l7' + ); + } + + public function testStaticL8() + { + $this->assertStaticField( + '-9223372036854775808', + 'l8' + ); + } + + public function testStaticL9() + { + $this->assertStaticField( + '9223372036854775807', + 'l9' + ); + } + + public function testStaticL10() + { + $this->assertStaticField( + '0', + 'l10' + ); + } + + public function testDynamicL0() + { + $this->assertDynamicField( + '1234', + 'l0' + ); + } + + public function testDynamicL1() + { + $this->assertDynamicField( + '32767', + 'l1' + ); + } + + public function testDynamicL2() + { + $this->assertDynamicField( + '32768', + 'l2' + ); + } + + public function testDynamicL3() + { + $this->assertDynamicField( + '2147483647', + 'l3' + ); + } + + public function testDynamicL4() + { + $this->assertDynamicField( + '-1', + 'l4' + ); + } + + public function testDynamicL5() + { + $this->assertDynamicField( + '-1234', + 'l5' + ); + } + + public function testDynamicL6() + { + $this->assertDynamicField( + '-2147483648', + 'l6' + ); + } + + public function testDynamicL7() + { + $this->assertDynamicField( + '-32768', + 'l7' + ); + } + + public function testDynamicL8() + { + $this->assertDynamicField( + '-9223372036854775808', + 'l8' + ); + } + + public function testDynamicL9() + { + $this->assertDynamicField( + '9223372036854775807', + 'l9' + ); + } + + public function testDynamicL10() + { + $this->assertDynamicField( + '0', + 'l10' + ); + } + + public function testStaticArrayLongs() + { + $array = $this->getStaticField('s_a_l'); + $this->assertCount(11, $array); + + $this->assertEquals('1234', (string) $array[0]); + $this->assertEquals('32767', (string) $array[1]); + $this->assertEquals('32768', (string) $array[2]); + $this->assertEquals('2147483647', (string) $array[3]); + $this->assertEquals('-1', (string) $array[4]); + $this->assertEquals('-1234', (string) $array[5]); + $this->assertEquals('-2147483648', (string) $array[6]); + $this->assertEquals('-32768', (string) $array[7]); + $this->assertEquals('-9223372036854775808', (string) $array[8]); + $this->assertEquals('9223372036854775807', (string) $array[9]); + $this->assertEquals('0', (string) $array[10]); + } + + public function testDynamicArrayLongs() + { + $array = $this->getDynamicField('d_a_l'); + $this->assertCount(11, $array); + + $this->assertEquals('1234', (string) $array[0]); + $this->assertEquals('32767', (string) $array[1]); + $this->assertEquals('32768', (string) $array[2]); + $this->assertEquals('2147483647', (string) $array[3]); + $this->assertEquals('-1', (string) $array[4]); + $this->assertEquals('-1234', (string) $array[5]); + $this->assertEquals('-2147483648', (string) $array[6]); + $this->assertEquals('-32768', (string) $array[7]); + $this->assertEquals('-9223372036854775808', (string) $array[8]); + $this->assertEquals('9223372036854775807', (string) $array[9]); + $this->assertEquals('0', (string) $array[10]); + } + + public function testStaticMultiDimensionArrayLongs() + { + $array = $this->getStaticField('s_ma_l'); + $this->assertCount(2, $array); + $this->assertCount(6, $array[0]); + $this->assertCount(5, $array[1]); + + $this->assertEquals('1234', (string) $array[0][0]); + $this->assertEquals('32767', (string) $array[0][1]); + $this->assertEquals('32768', (string) $array[0][2]); + $this->assertEquals('2147483647', (string) $array[0][3]); + $this->assertEquals('-1', (string) $array[0][4]); + $this->assertEquals('-1234', (string) $array[0][5]); + + $this->assertEquals('-2147483648', (string) $array[1][0]); + $this->assertEquals('-32768', (string) $array[1][1]); + $this->assertEquals('-9223372036854775808', (string) $array[1][2]); + $this->assertEquals('9223372036854775807', (string) $array[1][3]); + $this->assertEquals('0', (string) $array[1][4]); + } + + public function testDynamicMultiDimensionArrayLongs() + { + $array = $this->getDynamicField('d_ma_l'); + $this->assertCount(2, $array); + $this->assertCount(6, $array[0]); + $this->assertCount(5, $array[1]); + + $this->assertEquals('1234', (string) $array[0][0]); + $this->assertEquals('32767', (string) $array[0][1]); + $this->assertEquals('32768', (string) $array[0][2]); + $this->assertEquals('2147483647', (string) $array[0][3]); + $this->assertEquals('-1', (string) $array[0][4]); + $this->assertEquals('-1234', (string) $array[0][5]); + + $this->assertEquals('-2147483648', (string) $array[1][0]); + $this->assertEquals('-32768', (string) $array[1][1]); + $this->assertEquals('-9223372036854775808', (string) $array[1][2]); + $this->assertEquals('9223372036854775807', (string) $array[1][3]); + $this->assertEquals('0', (string) $array[1][4]); + } +} diff --git a/tests/Cases/BoundaryValueTypeForShortTest.php b/tests/Cases/BoundaryValueTypeForShortTest.php new file mode 100644 index 00000000..e2e2c653 --- /dev/null +++ b/tests/Cases/BoundaryValueTypeForShortTest.php @@ -0,0 +1,165 @@ +assertStaticField( + '1234', + 's0' + ); + } + + public function testStaticS1() + { + $this->assertStaticField( + '32767', + 's1' + ); + } + + public function testStaticS2() + { + $this->assertStaticField( + '-1', + 's2' + ); + } + + public function testStaticS3() + { + $this->assertStaticField( + '-1234', + 's3' + ); + } + + public function testStaticS4() + { + $this->assertStaticField( + '-32768', + 's4' + ); + } + + public function testStaticS5() + { + $this->assertStaticField( + '0', + 's5' + ); + } + + public function testDynamicS0() + { + $this->assertDynamicField( + '1234', + 's0' + ); + } + + public function testDynamicS1() + { + $this->assertDynamicField( + '32767', + 's1' + ); + } + + public function testDynamicS2() + { + $this->assertDynamicField( + '-1', + 's2' + ); + } + + public function testDynamicS3() + { + $this->assertDynamicField( + '-1234', + 's3' + ); + } + + public function testDynamicS4() + { + $this->assertDynamicField( + '-32768', + 's4' + ); + } + + public function testDynamicS5() + { + $this->assertDynamicField( + '0', + 's5' + ); + } + + public function testStaticArrayShorts() + { + $array = $this->getStaticField('s_a_s'); + $this->assertCount(6, $array); + + $this->assertEquals('1234', (string) $array[0]); + $this->assertEquals('32767', (string) $array[1]); + $this->assertEquals('-1', (string) $array[2]); + $this->assertEquals('-1234', (string) $array[3]); + $this->assertEquals('-32768', (string) $array[4]); + $this->assertEquals('0', (string) $array[5]); + } + + public function testDynamicArrayShorts() + { + $array = $this->getDynamicField('d_a_s'); + $this->assertCount(6, $array); + + $this->assertEquals('1234', (string) $array[0]); + $this->assertEquals('32767', (string) $array[1]); + $this->assertEquals('-1', (string) $array[2]); + $this->assertEquals('-1234', (string) $array[3]); + $this->assertEquals('-32768', (string) $array[4]); + $this->assertEquals('0', (string) $array[5]); + } + + public function testStaticMultiDimensionArrayShorts() + { + $array = $this->getStaticField('s_ma_s'); + $this->assertCount(2, $array); + $this->assertCount(3, $array[0]); + $this->assertCount(3, $array[1]); + + $this->assertEquals('1234', (string) $array[0][0]); + $this->assertEquals('32767', (string) $array[0][1]); + $this->assertEquals('-1', (string) $array[0][2]); + $this->assertEquals('-1234', (string) $array[1][0]); + $this->assertEquals('-32768', (string) $array[1][1]); + $this->assertEquals('0', (string) $array[1][2]); + } + + public function testDynamicMultiDimensionArrayShorts() + { + $array = $this->getDynamicField('d_ma_s'); + $this->assertCount(2, $array); + $this->assertCount(3, $array[0]); + $this->assertCount(3, $array[1]); + + $this->assertEquals('1234', (string) $array[0][0]); + $this->assertEquals('32767', (string) $array[0][1]); + $this->assertEquals('-1', (string) $array[0][2]); + $this->assertEquals('-1234', (string) $array[1][0]); + $this->assertEquals('-32768', (string) $array[1][1]); + $this->assertEquals('0', (string) $array[1][2]); + } +} diff --git a/tests/Cases/BranchIfTest.php b/tests/Cases/BranchIfTest.php new file mode 100644 index 00000000..7a09f831 --- /dev/null +++ b/tests/Cases/BranchIfTest.php @@ -0,0 +1,95 @@ +getInvoker() + ->getStatic() + ->getMethods() + ->call($method, $value1, $value2); + + return $calculatedValue->getValue(); + } + + public function testIfAcmpEq() + { + $actual = $this->call('ifAcmpEq', 'value1', 'value2'); + $this->assertEquals(0, $actual); + } + + public function testIfAcmpNe() + { + $actual = $this->call('ifAcmpNe', 'value1', 'value2'); + $this->assertEquals(1, $actual); + } + + public function testIfIcmpEq() + { + $actual = $this->call('ifIcmpEq', 5, 3); + $this->assertEquals(0, $actual); + } + + public function testIfIcmpNe() + { + $actual = $this->call('ifIcmpNe', 5, 3); + $this->assertEquals(1, $actual); + } + + public function testIfIcmpLt() + { + $actual = $this->call('ifIcmpLt', 5, 3); + $this->assertEquals(0, $actual); + } + + public function testIfIcmpGe() + { + $actual = $this->call('ifIcmpGe', 5, 3); + $this->assertEquals(1, $actual); + } + + public function testIfIcmpGt() + { + $actual = $this->call('ifIcmpGt', 5, 3); + $this->assertEquals(1, $actual); + } + + public function testIfIcmpLe() + { + $actual = $this->call('ifIcmpLe', 5, 3); + $this->assertEquals(0, $actual); + } + + public function testIfLcmpGraterThan() + { + $actual = $this->call('ifLcmpGraterThan', Long_::get(5), Long_::get(3)); + $this->assertEquals(0, $actual); + } + + public function testIfLcmpLessThan() + { + $actual = $this->call('ifLcmpLessThan', Long_::get(5), Long_::get(3)); + $this->assertEquals(1, $actual); + } + + public function testIfLcmpNotEqualsTo() + { + $actual = $this->call('ifLcmpEqualsTo', Long_::get(5), Long_::get(3)); + $this->assertEquals(1, $actual); + } + + public function testIfLcmpEqualsTo() + { + $actual = $this->call('ifLcmpEqualsTo', Long_::get(5), Long_::get(5)); + $this->assertEquals(0, $actual); + } +} diff --git a/tests/Cases/BubbleSortTest.php b/tests/Cases/BubbleSortTest.php new file mode 100644 index 00000000..ec3ef94d --- /dev/null +++ b/tests/Cases/BubbleSortTest.php @@ -0,0 +1,74 @@ +getInvoker() + ->getStatic() + ->getMethods() + ->call('asc'); + $result = Output::getHeapspace(); + $this->assertEquals( + file_get_contents(__DIR__ . '/templates/BubbleSortAsc.txt'), + $result + ); + } + + public function testBubbleSortDesc() + { + $calculatedValue = static::$initiatedJavaClasses['BubbleSortTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call('desc'); + $result = Output::getHeapspace(); + $this->assertEquals( + file_get_contents(__DIR__ . '/templates/BubbleSortDesc.txt'), + $result + ); + } + + public function testBubbleSortAscByParam() + { + $calculatedValue = static::$initiatedJavaClasses['BubbleSortTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'ascByParam', + [-14, 5, 111, 44, 2, 9999, 99, 123, 1, -10, 33, -50] + ); + $result = Output::getHeapspace(); + $this->assertEquals( + file_get_contents(__DIR__ . '/templates/BubbleSortAsc.txt'), + $result + ); + } + + public function testBubbleSortDescByParam() + { + $calculatedValue = static::$initiatedJavaClasses['BubbleSortTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'descByParam', + [-14, 5, 111, 44, 2, 9999, 99, 123, 1, -10, 33, -50] + ); + $result = Output::getHeapspace(); + $this->assertEquals( + file_get_contents(__DIR__ . '/templates/BubbleSortDesc.txt'), + $result + ); + } +} diff --git a/tests/Cases/CallToAmbiguousMethodsTest.php b/tests/Cases/CallToAmbiguousMethodsTest.php new file mode 100644 index 00000000..193dfe4c --- /dev/null +++ b/tests/Cases/CallToAmbiguousMethodsTest.php @@ -0,0 +1,149 @@ + false, + ] + ); + $this->ambiguousInitiatedClass = new \PHPJava\Core\JavaClass( + new JavaCompiledClass( + new \PHPJava\Core\Stream\Reader\FileReader( + $this->getClassName($this->fixtures[0]) + ) + ) + ); + } + + private function call($name, ...$parameters) + { + return static::$initiatedJavaClasses['CallToAmbiguousMethodsTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + $name, + ...$parameters + ); + } + + private function callWithAmbiguous($name, ...$parameters) + { + return $this->ambiguousInitiatedClass + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + $name, + ...$parameters + ); + } + + public function testCallToMethodIncludingLongParamsByStrictMode() + { + $this->call( + 'longMethod', + new Long_(1234) + ); + + $result = (int) Output::getHeapspace(); + $this->assertEquals(1234, $result); + } + + public function testCallToMethodIncludingByteParamsByStrictMode() + { + $this->call( + 'byteMethod', + new Byte_(32) + ); + + $result = (int) Output::getHeapspace(); + $this->assertEquals(32, $result); + } + + public function testCallToMethodIncludingCharParamsByStrictMode() + { + $this->call( + 'charMethod', + new Char_('a') + ); + + $result = rtrim(Output::getHeapspace()); + $this->assertEquals('a', $result); + } + + public function testCallToMethodIncludingDoubleParamsByStrictMode() + { + $this->call( + 'doubleMethod', + new Double_(0.01) + ); + + $result = rtrim(Output::getHeapspace()); + $this->assertEquals('0.01', $result); + } + + public function testCallToMethodIncludingLongParamsByAmbiguousMode() + { + $this->callWithAmbiguous( + 'longMethod', + 1234 + ); + + $result = (int) Output::getHeapspace(); + $this->assertEquals(1234, $result); + } + + public function testCallToMethodIncludingByteParamsByAmbiguousMode() + { + $this->callWithAmbiguous( + 'byteMethod', + 32 + ); + + $result = (int) Output::getHeapspace(); + $this->assertEquals(32, $result); + } + + public function testCallToMethodIncludingCharParamsByAmbiguousMode() + { + $this->callWithAmbiguous( + 'charMethod', + 'a' + ); + + $result = rtrim(Output::getHeapspace()); + $this->assertEquals('a', $result); + } + + public function testCallToMethodIncludingDoubleParamsByAmbiguousMode() + { + $this->callWithAmbiguous( + 'doubleMethod', + 0.01 + ); + + $result = rtrim(Output::getHeapspace()); + $this->assertEquals('0.01', $result); + } +} diff --git a/tests/Cases/CastTest.php b/tests/Cases/CastTest.php new file mode 100644 index 00000000..29a0f584 --- /dev/null +++ b/tests/Cases/CastTest.php @@ -0,0 +1,240 @@ +getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'testIntToShort', + new Int_(1234) + ); + + $this->assertInstanceOf(Short_::class, $result); + $this->assertEquals(1234, $result->getValue()); + } + + public function testIntToDouble() + { + $result = static::$initiatedJavaClasses['CastTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'testIntToDouble', + new Int_(1234) + ); + + // check type + $this->assertInstanceOf(Double_::class, $result); + $this->assertEquals(1234, $result->getValue()); + } + + public function testIntToFloat() + { + $result = static::$initiatedJavaClasses['CastTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'testIntToFloat', + new Int_(1234) + ); + + // check type + $this->assertInstanceOf(Float_::class, $result); + $this->assertEquals(1234, $result->getValue()); + } + + public function testIntToByte() + { + // Byte processing is special. + $result = static::$initiatedJavaClasses['CastTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'testIntToByte', + new Int_(123) + ); + + // check type + $this->assertInstanceOf(Byte_::class, $result); + $this->assertEquals(123, $result->getValue()); + } + + public function testIntToChar() + { + // Char processing is special. + $result = static::$initiatedJavaClasses['CastTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'testIntToChar', + new Int_(123) + ); + + // check type + $this->assertInstanceOf(Char_::class, $result); + $this->assertEquals('{', (string) $result); + } + + public function testLongToDouble() + { + $result = static::$initiatedJavaClasses['CastTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'testLongToDouble', + new Long_(1234) + ); + + $this->assertInstanceOf(Double_::class, $result); + $this->assertEquals(1234, $result->getValue()); + } + + public function testLongToFloat() + { + $result = static::$initiatedJavaClasses['CastTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'testLongToFloat', + new Long_(1234) + ); + + // check type + $this->assertInstanceOf(Float_::class, $result); + $this->assertEquals(1234, $result->getValue()); + } + + public function testLongToInt() + { + $result = static::$initiatedJavaClasses['CastTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'testLongToInt', + new Long_(1234) + ); + + // check type + $this->assertInstanceOf(Int_::class, $result); + $this->assertEquals(1234, $result->getValue()); + } + + public function testDoubleToFloat() + { + $result = static::$initiatedJavaClasses['CastTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'testDoubleToFloat', + new Double_(1234) + ); + + $this->assertInstanceOf(Float_::class, $result); + $this->assertEquals(1234, $result->getValue()); + } + + public function testDoubleToInt() + { + $result = static::$initiatedJavaClasses['CastTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'testDoubleToInt', + new Double_(1234) + ); + + // check type + $this->assertInstanceOf(Int_::class, $result); + $this->assertEquals(1234, $result->getValue()); + } + + public function testDoubleToLong() + { + $result = static::$initiatedJavaClasses['CastTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'testDoubleToLong', + new Double_(1234) + ); + + // check type + $this->assertInstanceOf(Long_::class, $result); + $this->assertEquals(1234, $result->getValue()); + } + + public function testFloatToDouble() + { + $result = static::$initiatedJavaClasses['CastTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'testFloatToDouble', + new Float_(1234) + ); + + $this->assertInstanceOf(Double_::class, $result); + $this->assertEquals(1234, $result->getValue()); + } + + public function testFloatToInt() + { + $result = static::$initiatedJavaClasses['CastTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'testFloatToInt', + new Float_(1234) + ); + + // check type + $this->assertInstanceOf(Int_::class, $result); + $this->assertEquals(1234, $result->getValue()); + } + + public function testFloatToLong() + { + $result = static::$initiatedJavaClasses['CastTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'testFloatToLong', + new Float_(1234) + ); + + // check type + $this->assertInstanceOf(Long_::class, $result); + $this->assertEquals(1234, $result->getValue()); + } +} diff --git a/tests/Cases/Compiler/CallMethodTest.php b/tests/Cases/Compiler/CallMethodTest.php new file mode 100644 index 00000000..72d60e26 --- /dev/null +++ b/tests/Cases/Compiler/CallMethodTest.php @@ -0,0 +1,76 @@ +runJavaTest(__METHOD__); + $this->assertSame('Hello World!', $output[0]); + } + + public function TestCallStaticMethodsWithArguments() + { + [$output, $return] = $this->runJavaTest(__METHOD__); + $this->assertSame('Hello World!', $output[0]); + } + + public function testCallStaticMethodsWithNonArgumentsAndNamespace() + { + [$output, $return] = $this->runJavaTest( + __METHOD__, + 'PHPJava.CompilerMethodCallTest' + ); + $this->assertSame('Hello World!', $output[0]); + } + + public function testCallStaticMethodsWithArgumentsAndNamespace() + { + [$output, $return] = $this->runJavaTest( + __METHOD__, + 'PHPJava.CompilerMethodCallTest' + ); + $this->assertSame('Hello World!', $output[0]); + } + + public function testCallStaticMethodsWithTypeHintedArguments() + { + [$output, $return] = $this->runJavaTest(__METHOD__); + $this->assertSame('Hello World!', $output[0]); + } + + public function testCallStaticMethodsWithPrimitiveTypeHintedArguments() + { + [$output, $return] = $this->runJavaTest(__METHOD__); + $this->assertSame('1234', $output[0]); + } + + public function testCallStaticMethodsMultiplePattern1() + { + [$output, $return] = $this->runJavaTest(__METHOD__); + $this->assertSame('Hello World!', $output[0]); + } + + public function testCallStaticMethodsMultiplePattern2() + { + [$output, $return] = $this->runJavaTest(__METHOD__); + $this->assertSame('Hello World!', $output[0]); + } + + public function testCallStaticMethodsWithMultipleArguments() + { + [$output, $return] = $this->runJavaTest(__METHOD__); + $this->assertSame('Hello World!', $output[0]); + } + + public function testCallDynamicMethodsWithNonArguments() + { + [$output, $return] = $this->runJavaTest(__METHOD__); + $this->assertSame('Hello World!', $output[0]); + } +} diff --git a/tests/Cases/Compiler/Codes/TestCallDynamicMethodsWithNonArguments.php b/tests/Cases/Compiler/Codes/TestCallDynamicMethodsWithNonArguments.php new file mode 100644 index 00000000..398d1e88 --- /dev/null +++ b/tests/Cases/Compiler/Codes/TestCallDynamicMethodsWithNonArguments.php @@ -0,0 +1,18 @@ +doSomethingMethod(); + } +} diff --git a/tests/Cases/Compiler/Codes/TestCallOutsideStatementOfClasses.php b/tests/Cases/Compiler/Codes/TestCallOutsideStatementOfClasses.php new file mode 100644 index 00000000..e1a57b25 --- /dev/null +++ b/tests/Cases/Compiler/Codes/TestCallOutsideStatementOfClasses.php @@ -0,0 +1,14 @@ +runJavaEntryPointTest(__METHOD__); + $this->assertSame( + 0, + $return + ); + $this->assertSame( + 'Hello World!', + $output[0] + ); + } + + public function testCallOutsideStatementOfClasses() + { + [$output, $return] = $this->runJavaEntryPointTest(__METHOD__); + $this->assertSame( + 0, + $return + ); + $this->assertSame( + 'Hello World!', + $output[0] + ); + } + + public function testCallOutsideStatementOfClassesWithNamespace() + { + [$output, $return] = $this->runJavaEntryPointTest( + __METHOD__, + 'PHPJava.Test.Entrypoint' + ); + $this->assertSame( + 0, + $return + ); + $this->assertSame( + 'Hello World!', + $output[0] + ); + } +} diff --git a/tests/Cases/Compiler/FinderTest.php b/tests/Cases/Compiler/FinderTest.php new file mode 100644 index 00000000..c3e40d2f --- /dev/null +++ b/tests/Cases/Compiler/FinderTest.php @@ -0,0 +1,176 @@ +add(new Utf8Info('Hello World')); + + /** + * @var EntryMap $entry + */ + $entry = $finder->find(Utf8Info::class, 'Hello World')->getResult(); + $this->assertSame(1, $entry->getEntryIndex()); + $this->assertInstanceOf(Utf8Info::class, $entry->getEntry()); + } + + public function testSuccessfullyStringInfoFind() + { + $constantPool = new ConstantPool(); + $finder = new ConstantPoolFinder($constantPool); + $constantPool + ->add(new Utf8Info('Hello World')) + ->add(new StringInfo( + $finder->find(Utf8Info::class, 'Hello World') + )); + + /** + * @var EntryMap $entry + */ + $entry = $finder->find(StringInfo::class, 'Hello World')->getResult(); + $this->assertSame(2, $entry->getEntryIndex()); + $this->assertInstanceOf(StringInfo::class, $entry->getEntry()); + } + + public function testSuccessfullyClassInfoInfoFind() + { + $constantPool = new ConstantPool(); + $finder = new ConstantPoolFinder($constantPool); + $constantPool + ->add(new Utf8Info('HelloWorld')) + ->add(new ClassInfo( + $finder->find(Utf8Info::class, 'HelloWorld') + )); + + /** + * @var EntryMap $entry + */ + $entry = $finder->find(ClassInfo::class, 'HelloWorld')->getResult(); + $this->assertSame(2, $entry->getEntryIndex()); + $this->assertInstanceOf(ClassInfo::class, $entry->getEntry()); + } + + public function testSuccessfullyNameAndTypeInfoFind() + { + $constantPool = new ConstantPool(); + $finder = new ConstantPoolFinder($constantPool); + $constantPool + ->add(new Utf8Info('main')) + ->add(new Utf8Info('()V')) + ->add(new NameAndTypeInfo( + $finder->find(Utf8Info::class, 'main'), + $finder->find(Utf8Info::class, '()V') + )); + + /** + * @var EntryMap $entry + */ + $entry = $finder->find(NameAndTypeInfo::class, 'main', '()V')->getResult(); + $this->assertSame(3, $entry->getEntryIndex()); + $this->assertInstanceOf(NameAndTypeInfo::class, $entry->getEntry()); + } + + public function testSuccessfullyMethodrefInfoFind() + { + $constantPool = new ConstantPool(); + $finder = new ConstantPoolFinder($constantPool); + $constantPool + ->add(new Utf8Info('HelloWorld')) + ->add(new Utf8Info('main')) + ->add(new Utf8Info('()V')) + ->add(new ClassInfo( + $finder->find(Utf8Info::class, 'HelloWorld') + )) + ->add(new NameAndTypeInfo( + $finder->find(Utf8Info::class, 'main'), + $finder->find(Utf8Info::class, '()V') + )) + ->add( + new MethodrefInfo( + $finder->find(ClassInfo::class, 'HelloWorld'), + $finder->find(NameAndTypeInfo::class, 'main', '()V') + ) + ); + + /** + * @var EntryMap $entry + */ + $entry = $finder->find(MethodrefInfo::class, 'HelloWorld', 'main', '()V')->getResult(); + $this->assertSame(6, $entry->getEntryIndex()); + $this->assertInstanceOf(MethodrefInfo::class, $entry->getEntry()); + } + + public function testSuccessfullyFieldrefInfoFind() + { + $constantPool = new ConstantPool(); + $finder = new ConstantPoolFinder($constantPool); + $constantPool + ->add(new Utf8Info('java/lang/System')) + ->add(new Utf8Info('out')) + ->add(new Utf8Info('Ljava/io/PrintStream;')) + ->add(new ClassInfo( + $finder->find(Utf8Info::class, 'java/lang/System') + )) + ->add(new NameAndTypeInfo( + $finder->find(Utf8Info::class, 'out'), + $finder->find(Utf8Info::class, 'Ljava/io/PrintStream;') + )) + ->add( + new MethodrefInfo( + $finder->find(ClassInfo::class, 'java/lang/System'), + $finder->find(NameAndTypeInfo::class, 'out', 'Ljava/io/PrintStream;') + ) + ); + + /** + * @var EntryMap $entry + */ + $entry = $finder->find(MethodrefInfo::class, 'java/lang/System', 'out', 'Ljava/io/PrintStream;')->getResult(); + $this->assertSame(6, $entry->getEntryIndex()); + $this->assertInstanceOf(MethodrefInfo::class, $entry->getEntry()); + } + + public function testFailedFindPattern1() + { + $constantPool = new ConstantPool(); + $finder = new ConstantPoolFinder($constantPool); + $constantPool + ->add(new Utf8Info('Hello World')); + + /** + * @var EntryMap $entry + */ + $this->expectException(FinderException::class); + $finder->find(Utf8Info::class, ':thinking:')->getResult(); + } + + public function testFailedFindPattern2() + { + $constantPool = new ConstantPool(); + $finder = new ConstantPoolFinder($constantPool); + $constantPool + ->add(new Utf8Info('Hello World')); + + /** + * @var EntryMap $entry + */ + $this->expectException(FinderException::class); + $finder->find(StringInfo::class, 'Hello World')->getResult(); + } +} diff --git a/tests/Cases/Compiler/FizzBuzzTest.php b/tests/Cases/Compiler/FizzBuzzTest.php new file mode 100644 index 00000000..897692c9 --- /dev/null +++ b/tests/Cases/Compiler/FizzBuzzTest.php @@ -0,0 +1,21 @@ +runJavaTest(__METHOD__); + + $this->assertSame(0, $return); + $this->assertEquals( + file_get_contents(__DIR__ . '/../templates/FizzBuzzTest.txt'), + implode("\n", $output) . "\n" + ); + } +} diff --git a/tests/Cases/Compiler/HelloWorldTest.php b/tests/Cases/Compiler/HelloWorldTest.php new file mode 100644 index 00000000..264dfdca --- /dev/null +++ b/tests/Cases/Compiler/HelloWorldTest.php @@ -0,0 +1,193 @@ +addString('Hello PHPJava Compiler!') + ->addClass(Object_::class) + ->addClass($className) + ->addClass(System::class) + ->addClass(PrintStream::class) + ->addFieldref( + System::class, + 'out', + (new Descriptor()) + ->addArgument(PrintStream::class) + ->make() + ) + ->addMethodref( + PrintStream::class, + 'println', + (new Descriptor()) + ->addArgument(String_::class) + ->setReturn(Void_::class) + ->make() + ) + ->addNameAndType( + 'main', + (new Descriptor()) + ->addArgument(String_::class, 1) + ->setReturn(Void_::class) + ->make() + ); + + $compiler = new Compiler( + (new ClassFileStructure()) + ->setMinorVersion($minorVersion) + ->setMajorVersion($majorVersion) + ->setAccessFlags( + (new ClassAccessFlag()) + ->enableSuper() + ->make() + ) + ->setThisClass($enhancedConstantPool->findClass($className)) + ->setSuperClass($enhancedConstantPool->findClass(Object_::class)) + ->setMethods( + (new Methods()) + ->add( + (new Method( + (new MethodAccessFlag()) + ->enablePublic() + ->enableStatic() + ->make(), + $className, + 'main', + (new Descriptor()) + ->addArgument(String_::class, 1) + ->setReturn(Void_::class) + ->make() + )) + ->setConstantPool($constantPool) + ->setConstantPoolFinder($finder) + ->setAttributes( + (new Attributes()) + ->add( + (new Code($enhancedConstantPool->findUtf8('Code'))) + ->setConstantPool($constantPool) + ->setConstantPoolFinder($finder) + ->setCode( + [ + \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_getstatic, + Operand::factory( + Uint16::class, + $enhancedConstantPool->findField( + System::class, + 'out', + (new Descriptor()) + ->addArgument(PrintStream::class) + ->make() + ) + ) + ), + \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_ldc, + Operand::factory( + Uint8::class, + $enhancedConstantPool->findString('Hello PHPJava Compiler!') + ) + ), + \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_invokevirtual, + Operand::factory( + Uint16::class, + $enhancedConstantPool->findMethod( + PrintStream::class, + 'println', + (new Descriptor()) + ->addArgument(String_::class) + ->setReturn(Void_::class) + ->make() + ) + ) + ), + \PHPJava\Compiler\Builder\Generator\Operation\Operation::create( + OpCode::_return + ), + ] + ) + ->beginPreparation() + ) + ->toArray() + ) + ) + ->toArray() + ) + ->setConstantPool($constantPool->toArray()) + ); + + $compiler->compile($source); + rewind($source); + + $javaClass = new JavaClass( + new JavaCompiledClass( + new InlineReader( + $className, + stream_get_contents($source) + ) + ) + ); + + $javaClass + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'main', + [] + ); + + $result = trim(Output::getHeapspace()); + + $this->assertSame('Hello PHPJava Compiler!', $result); + } +} diff --git a/tests/Cases/Compiler/IfStatementTest.php b/tests/Cases/Compiler/IfStatementTest.php new file mode 100644 index 00000000..8e9e913b --- /dev/null +++ b/tests/Cases/Compiler/IfStatementTest.php @@ -0,0 +1,106 @@ +runJavaTest(__METHOD__); + + $this->assertSame(0, $return); + $this->assertCount(2, $output); + + $this->assertSame('Hello World!', $output[0]); + $this->assertSame('Hello World!', $output[1]); + } + + public function testIfStatementNotAvailableIf() + { + [$output, $return] = $this->runJavaTest(__METHOD__); + + $this->assertSame(0, $return); + $this->assertCount(0, $output); + } + + public function testIfElseIfStatementAvailableIf() + { + [$output, $return] = $this->runJavaTest(__METHOD__); + + $this->assertSame(0, $return); + $this->assertCount(2, $output); + + $this->assertSame('Hello World!', $output[0]); + $this->assertSame('Hello World!', $output[1]); + } + + public function testIfElseIfStatementAvailableElseIf() + { + [$output, $return] = $this->runJavaTest(__METHOD__); + + $this->assertSame(0, $return); + $this->assertCount(2, $output); + + $this->assertSame('Hello World 2', $output[0]); + $this->assertSame('Hello World 2', $output[1]); + } + + public function testIfElseStatementAvailableIf() + { + [$output, $return] = $this->runJavaTest(__METHOD__); + + $this->assertSame(0, $return); + $this->assertCount(2, $output); + + $this->assertSame('Hello World 1', $output[0]); + $this->assertSame('Hello World 1', $output[1]); + } + + public function testIfElseStatementAvailableElse() + { + [$output, $return] = $this->runJavaTest(__METHOD__); + + $this->assertSame(0, $return); + $this->assertCount(2, $output); + + $this->assertSame('Hello World 2', $output[0]); + $this->assertSame('Hello World 2', $output[1]); + } + + public function testIfElseIfElseStatementAvailableIf() + { + [$output, $return] = $this->runJavaTest(__METHOD__); + + $this->assertSame(0, $return); + $this->assertCount(2, $output); + + $this->assertSame('Hello World 1', $output[0]); + $this->assertSame('Hello World 1', $output[1]); + } + + public function testIfElseIfElseStatementAvailableElseIf() + { + [$output, $return] = $this->runJavaTest(__METHOD__); + + $this->assertSame(0, $return); + $this->assertCount(2, $output); + + $this->assertSame('Hello World 2', $output[0]); + $this->assertSame('Hello World 2', $output[1]); + } + + public function testIfElseIfElseStatementAvailableElse() + { + [$output, $return] = $this->runJavaTest(__METHOD__); + + $this->assertSame(0, $return); + $this->assertCount(2, $output); + + $this->assertSame('Hello World 3', $output[0]); + $this->assertSame('Hello World 3', $output[1]); + } +} diff --git a/tests/Cases/Compiler/JavaRunnable.php b/tests/Cases/Compiler/JavaRunnable.php new file mode 100644 index 00000000..1b209522 --- /dev/null +++ b/tests/Cases/Compiler/JavaRunnable.php @@ -0,0 +1,61 @@ +setDistributeDirectory($this->getDistributeDirectory()); + } + + private function runJavaTest(string $testName, ?string $namespace = null): array + { + return $this->runJavaWithClassFileNameTest( + $testName, + null, + $namespace + ); + } + + private function runJavaEntryPointTest(string $testName, ?string $namespace = null): array + { + return $this->runJavaWithClassFileNameTest( + $testName, + Runtime::PHP_ENTRY_POINT_CLASS_NAME, + $namespace + ); + } + + private function runJavaWithClassFileNameTest(string $testName, string $classFileName = null, ?string $namespace = null) + { + $name = $this->getClassNameByTestName($testName); + (new PackageAssembler($this->getFileStream($name))) + ->assemble(); + + $classPath = ltrim(($namespace ?? '') . '.' . ($classFileName ?? $name), '.'); + exec( + 'cd ' . $this->getDistributeDirectory() . ' && java ' . $classPath, + $output, + $return + ); + + return [$output, $return]; + } +} diff --git a/tests/Cases/Compiler/MagicConstantTest.php b/tests/Cases/Compiler/MagicConstantTest.php new file mode 100644 index 00000000..8c9a5bab --- /dev/null +++ b/tests/Cases/Compiler/MagicConstantTest.php @@ -0,0 +1,60 @@ +runJavaTest(__METHOD__); + + $this->assertSame( + 'TestMagicConstMethod::main', + $output[0] + ); + } + + public function testMagicConstClass() + { + [$output, $return] = $this->runJavaTest(__METHOD__); + + $this->assertSame( + 'TestMagicConstClass', + $output[0] + ); + } + + public function testMagicConstLine() + { + [$output, $return] = $this->runJavaTest(__METHOD__); + + $this->assertSame( + '10', + $output[0] + ); + } + + public function testMagicConstDir() + { + [$output, $return] = $this->runJavaTest(__METHOD__); + + $this->assertMatchesRegularExpression( + '#php-java/tests/Cases/Compiler/Codes$#', + $output[0] + ); + } + + public function testMagicConstFile() + { + [$output, $return] = $this->runJavaTest(__METHOD__); + + $this->assertMatchesRegularExpression( + '#php-java/tests/Cases/Compiler/Codes/TestMagicConstFile.php$#', + $output[0] + ); + } +} diff --git a/tests/Cases/Compiler/NamespaceTest.php b/tests/Cases/Compiler/NamespaceTest.php new file mode 100644 index 00000000..45f67c30 --- /dev/null +++ b/tests/Cases/Compiler/NamespaceTest.php @@ -0,0 +1,30 @@ +runJavaTest(__METHOD__, 'PHPJava'); + + $this->assertSame( + 'Hello World!', + $output[0] + ); + } + + public function testNamespacePattern2() + { + [$output, $return] = $this->runJavaTest(__METHOD__, 'PHPJava.Test'); + + $this->assertSame( + 'Hello World!', + $output[0] + ); + } +} diff --git a/tests/Cases/Compiler/StructureTest.php b/tests/Cases/Compiler/StructureTest.php new file mode 100644 index 00000000..6339c495 --- /dev/null +++ b/tests/Cases/Compiler/StructureTest.php @@ -0,0 +1,50 @@ +runJavaTest(__METHOD__); + + $this->assertSame( + 'Hello World!', + $output[0] + ); + } + + public function testUseStatementWithNamespace() + { + [$output, $return] = $this->runJavaTest(__METHOD__, 'PHPJava.UseStatement'); + + $this->assertSame( + 'Hello World!', + $output[0] + ); + } + + public function testDeclareStatement() + { + [$output, $return] = $this->runJavaTest(__METHOD__); + + $this->assertSame( + 'Hello World!', + $output[0] + ); + } + + public function testDeclareStatementWithNamespace() + { + [$output, $return] = $this->runJavaTest(__METHOD__, 'PHPJava.DeclareStatement'); + + $this->assertSame( + 'Hello World!', + $output[0] + ); + } +} diff --git a/tests/Cases/ConstructTest.php b/tests/Cases/ConstructTest.php new file mode 100644 index 00000000..9ad5ff9b --- /dev/null +++ b/tests/Cases/ConstructTest.php @@ -0,0 +1,98 @@ +getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'main', + [] + ); + $result = Output::getHeapspace(); + $this->assertEquals("Hello World!\n", $result); + } + + public function testConstructorWithParametersPattern2() + { + $result = static::$initiatedJavaClasses['ConstructorWithParametersTest'] + ->getInvoker() + ->construct('Hello World!') + ->getDynamic() + ->getMethods() + ->call('entrypoint'); + $result = Output::getHeapspace(); + $this->assertEquals("Hello World!\nEntrypoint\n", $result); + } + + public function testConstructorNoParameterPattern1() + { + $result = static::$initiatedJavaClasses['ConstructorNoParameterTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'main', + [] + ); + $result = Output::getHeapspace(); + $this->assertEquals("Hello World!\n", $result); + } + + public function testConstructorNoParameterPattern2() + { + $result = static::$initiatedJavaClasses['ConstructorNoParameterTest'] + ->getInvoker() + ->construct() + ->getDynamic() + ->getMethods() + ->call('entrypoint'); + $result = Output::getHeapspace(); + $this->assertEquals("Hello World!\nEntrypoint\n", $result); + } + + public function testDynamicField() + { + $text = static::$initiatedJavaClasses['ConstructTest'] + ->getInvoker() + ->construct() + ->getDynamic() + ->getFields() + ->get('text'); + + $this->assertEquals('Default Text', $text); + + $text = static::$initiatedJavaClasses['ConstructTest'] + ->getInvoker() + ->getDynamic() + ->getFields() + ->set('text', 'New Text') + ->get('text'); + + $this->assertEquals('New Text', $text); + + // Re-construction will be changed to default text + + $text = static::$initiatedJavaClasses['ConstructTest'] + ->getInvoker() + ->construct() + ->getDynamic() + ->getFields() + ->get('text'); + + $this->assertEquals('Default Text', $text); + } +} diff --git a/tests/Cases/DefaultSyntaxInInterfaceTest.php b/tests/Cases/DefaultSyntaxInInterfaceTest.php new file mode 100644 index 00000000..b67a4f95 --- /dev/null +++ b/tests/Cases/DefaultSyntaxInInterfaceTest.php @@ -0,0 +1,67 @@ +getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'defaultInterfaceMethodTest1' + ); + $result = Output::getHeapspace(); + + $this->assertEquals( + "Hello World!\n", + $result + ); + } + + public function testDefaultInterfaceCallsInterfaceMethod() + { + static::$initiatedJavaClasses['DefaultInterfaceCallsInterfaceMethodTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'main', + [] + ); + $result = Output::getHeapspace(); + + $this->assertEquals( + "Hello World!\n", + $result + ); + } + + public function DefaultInterfaceGetCalledByMethodLookupTest() + { + static::$initiatedJavaClasses['DefaultInterfaceGetCalledByMethodLookupTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'main', + [] + ); + $result = Output::getHeapspace(); + + $this->assertEquals( + "Hello World!\n", + $result + ); + } +} diff --git a/tests/Cases/DoubleCalculationTest.php b/tests/Cases/DoubleCalculationTest.php new file mode 100644 index 00000000..216c9e80 --- /dev/null +++ b/tests/Cases/DoubleCalculationTest.php @@ -0,0 +1,216 @@ +getInvoker() + ->getStatic() + ->getMethods() + ->call( + $name, + ...$parameters + ); + } + + public function testDoubleAdd() + { + $this->assertEquals( + '30', + $this->call( + 'doubleAdd', + Double_::get(10), + Double_::get(20) + ) + ); + } + + public function testDoubleSub() + { + $this->assertEquals( + '5', + $this->call( + 'doubleSub', + Double_::get(10), + Double_::get(5) + ) + ); + } + + public function testDoubleNegativeSub() + { + $this->assertEquals( + '-10', + $this->call( + 'doubleSub', + Double_::get(10), + Double_::get(20) + ) + ); + } + + public function testDoubleMul() + { + $this->assertEquals( + '50', + $this->call( + 'doubleMul', + Double_::get(10), + Double_::get(5) + ) + ); + } + + public function testDoubleAddFromOtherMethod() + { + $this->assertEquals( + '30', + $this->call( + 'doubleAddFromOtherMethod', + Double_::get(10), + Double_::get(20) + ) + ); + } + + public function testDoubleSubFromOtherMethod() + { + $this->assertEquals( + '5', + $this->call( + 'doubleSubFromOtherMethod', + Double_::get(10), + Double_::get(5) + ) + ); + } + + public function testDoubleNegativeSubFromOtherMethod() + { + $this->assertEquals( + '-10', + $this->call( + 'doubleSubFromOtherMethod', + Double_::get(10), + Double_::get(20) + ) + ); + } + + public function testDoubleMulFromOtherMethod() + { + $this->assertEquals( + '50', + $this->call( + 'doubleMulFromOtherMethod', + Double_::get(10), + Double_::get(5) + ) + ); + } + + public function testDoublePointAdd() + { + $this->assertEquals( + '3.0', + $this->call( + 'doubleAdd', + Double_::get(1.5), + Double_::get(1.5) + ) + ); + } + + public function testDoublePointSub() + { + $this->assertEquals( + '0.0', + $this->call( + 'doubleSub', + Double_::get(1.5), + Double_::get(1.5) + ) + ); + } + + public function testDoubleNegativePointSub() + { + $this->assertEquals( + '-1.0', + $this->call( + 'doubleSub', + Double_::get(1.5), + Double_::get(2.5) + ) + ); + } + + public function testDoublePointMul() + { + $this->assertEquals( + '7.5', + $this->call( + 'doubleMul', + Double_::get(5), + Double_::get(1.5) + ) + ); + } + + public function testDoublePointAddFromOtherMethod() + { + $this->assertEquals( + '3.0', + $this->call( + 'doubleAddFromOtherMethod', + Double_::get(1.5), + Double_::get(1.5) + ) + ); + } + + public function testDoublePointSubFromOtherMethod() + { + $this->assertEquals( + '0.0', + $this->call( + 'doubleSubFromOtherMethod', + Double_::get(1.5), + Double_::get(1.5) + ) + ); + } + + public function testDoubleNegativePointSubFromOtherMethod() + { + $this->assertEquals( + '-1.0', + $this->call( + 'doubleSubFromOtherMethod', + Double_::get(1.5), + Double_::get(2.5) + ) + ); + } + + public function testDoubleMulPointFromOtherMethod() + { + $this->assertEquals( + '7.5', + $this->call( + 'doubleMulFromOtherMethod', + Double_::get(5), + Double_::get(1.5) + ) + ); + } +} diff --git a/tests/Cases/DoubleOfTypesCompreingTest.php b/tests/Cases/DoubleOfTypesCompreingTest.php new file mode 100644 index 00000000..fbfbe159 --- /dev/null +++ b/tests/Cases/DoubleOfTypesCompreingTest.php @@ -0,0 +1,69 @@ +getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'l', + new Double_(-1.0) + ); + $result = Output::getHeapspace(); + $this->assertEquals("Hello World!\n", $result); + } + + public function testLessThanAndEquals() + { + static::$initiatedJavaClasses['DoubleOfTypesComparingTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'le', + new Double_(0) + ); + $result = Output::getHeapspace(); + $this->assertEquals("Hello World!\n", $result); + } + + public function testGraterThan() + { + static::$initiatedJavaClasses['DoubleOfTypesComparingTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'g', + new Double_(2.0) + ); + $result = Output::getHeapspace(); + $this->assertEquals("Hello World!\n", $result); + } + + public function testGraterThanAndEquals() + { + static::$initiatedJavaClasses['DoubleOfTypesComparingTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'ge', + new Double_(0) + ); + $result = Output::getHeapspace(); + $this->assertEquals("Hello World!\n", $result); + } +} diff --git a/tests/Cases/EnclosingMethodTest.php b/tests/Cases/EnclosingMethodTest.php new file mode 100644 index 00000000..ddbfb171 --- /dev/null +++ b/tests/Cases/EnclosingMethodTest.php @@ -0,0 +1,26 @@ +getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'main', + [] + ); + $result = Output::getHeapspace(); + $this->assertEquals("Hello World!\n", $result); + } +} diff --git a/tests/Cases/ExtendingClassTest.php b/tests/Cases/ExtendingClassTest.php new file mode 100644 index 00000000..02f1bb02 --- /dev/null +++ b/tests/Cases/ExtendingClassTest.php @@ -0,0 +1,42 @@ +getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'main', + [] + ); + $result = Output::getHeapspace(); + $this->assertEquals("99999\n", $result); + } + + public function testExtendingClassPattern2() + { + static::$initiatedJavaClasses['ExtendingClassTest2'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'main', + [] + ); + $result = Output::getHeapspace(); + $this->assertEquals("12345\n", $result); + } +} diff --git a/tests/Cases/FizzBuzzTest.php b/tests/Cases/FizzBuzzTest.php new file mode 100644 index 00000000..bc38ac86 --- /dev/null +++ b/tests/Cases/FizzBuzzTest.php @@ -0,0 +1,29 @@ +getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'main', + ['100'] + ); + $result = Output::getHeapspace(); + $this->assertEquals( + file_get_contents(__DIR__ . '/templates/FizzBuzzTest.txt'), + $result + ); + } +} diff --git a/tests/Cases/FloatCalculationTest.php b/tests/Cases/FloatCalculationTest.php new file mode 100644 index 00000000..2c76f2e7 --- /dev/null +++ b/tests/Cases/FloatCalculationTest.php @@ -0,0 +1,216 @@ +getInvoker() + ->getStatic() + ->getMethods() + ->call( + $name, + ...$parameters + ); + } + + public function testFloatAdd() + { + $this->assertEquals( + '30', + $this->call( + 'floatAdd', + Float_::get(10), + Float_::get(20) + ) + ); + } + + public function testFloatSub() + { + $this->assertEquals( + '5', + $this->call( + 'floatSub', + Float_::get(10), + Float_::get(5) + ) + ); + } + + public function testFloatNegativeSub() + { + $this->assertEquals( + '-10', + $this->call( + 'floatSub', + Float_::get(10), + Float_::get(20) + ) + ); + } + + public function testFloatMul() + { + $this->assertEquals( + '50', + $this->call( + 'floatMul', + Float_::get(10), + Float_::get(5) + ) + ); + } + + public function testFloatAddFromOtherMethod() + { + $this->assertEquals( + '30', + $this->call( + 'floatAddFromOtherMethod', + Float_::get(10), + Float_::get(20) + ) + ); + } + + public function testFloatSubFromOtherMethod() + { + $this->assertEquals( + '5', + $this->call( + 'floatSubFromOtherMethod', + Float_::get(10), + Float_::get(5) + ) + ); + } + + public function testFloatNegativeSubFromOtherMethod() + { + $this->assertEquals( + '-10', + $this->call( + 'floatSubFromOtherMethod', + Float_::get(10), + Float_::get(20) + ) + ); + } + + public function testFloatMulFromOtherMethod() + { + $this->assertEquals( + '50', + $this->call( + 'floatMulFromOtherMethod', + Float_::get(10), + Float_::get(5) + ) + ); + } + + public function testFloatPointAdd() + { + $this->assertEquals( + '3', + $this->call( + 'floatAdd', + Float_::get(1.5), + Float_::get(1.5) + ) + ); + } + + public function testFloatPointSub() + { + $this->assertEquals( + '0', + $this->call( + 'floatSub', + Float_::get(1.5), + Float_::get(1.5) + ) + ); + } + + public function testFloatNegativePointSub() + { + $this->assertEquals( + '-1', + $this->call( + 'floatSub', + Float_::get(1.5), + Float_::get(2.5) + ) + ); + } + + public function testFloatPointMul() + { + $this->assertEquals( + '7.5', + $this->call( + 'floatMul', + Float_::get(5), + Float_::get(1.5) + ) + ); + } + + public function testFloatPointAddFromOtherMethod() + { + $this->assertEquals( + '3', + $this->call( + 'floatAddFromOtherMethod', + Float_::get(1.5), + Float_::get(1.5) + ) + ); + } + + public function testFloatPointSubFromOtherMethod() + { + $this->assertEquals( + '0', + $this->call( + 'floatSubFromOtherMethod', + Float_::get(1.5), + Float_::get(1.5) + ) + ); + } + + public function testFloatNegativePointSubFromOtherMethod() + { + $this->assertEquals( + '-1', + $this->call( + 'floatSubFromOtherMethod', + Float_::get(1.5), + Float_::get(2.5) + ) + ); + } + + public function testFloatMulPointFromOtherMethod() + { + $this->assertEquals( + '7.5', + $this->call( + 'floatMulFromOtherMethod', + Float_::get(5), + Float_::get(1.5) + ) + ); + } +} diff --git a/tests/Cases/FloatOfTypesCompreingTest.php b/tests/Cases/FloatOfTypesCompreingTest.php new file mode 100644 index 00000000..051c8411 --- /dev/null +++ b/tests/Cases/FloatOfTypesCompreingTest.php @@ -0,0 +1,69 @@ +getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'l', + new Float_(-1.0) + ); + $result = Output::getHeapspace(); + $this->assertEquals("Hello World!\n", $result); + } + + public function testLessThanAndEquals() + { + static::$initiatedJavaClasses['FloatOfTypesComparingTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'le', + new Float_(0) + ); + $result = Output::getHeapspace(); + $this->assertEquals("Hello World!\n", $result); + } + + public function testGraterThan() + { + static::$initiatedJavaClasses['FloatOfTypesComparingTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'g', + new Float_(2.0) + ); + $result = Output::getHeapspace(); + $this->assertEquals("Hello World!\n", $result); + } + + public function testGraterThanAndEquals() + { + static::$initiatedJavaClasses['FloatOfTypesComparingTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'ge', + new Float_(0) + ); + $result = Output::getHeapspace(); + $this->assertEquals("Hello World!\n", $result); + } +} diff --git a/tests/Cases/GetFieldTest.php b/tests/Cases/GetFieldTest.php new file mode 100644 index 00000000..3c2d00de --- /dev/null +++ b/tests/Cases/GetFieldTest.php @@ -0,0 +1,22 @@ +getInvoker() + ->getStatic() + ->getMethods() + ->call('getField') + ->getValue(); + + $this->assertEquals(1, $actual); + } +} diff --git a/tests/Cases/InnerClassTest.php b/tests/Cases/InnerClassTest.php new file mode 100644 index 00000000..61b1bc53 --- /dev/null +++ b/tests/Cases/InnerClassTest.php @@ -0,0 +1,28 @@ +getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'main', + ['Hello', 'World'] + ); + $result = Output::getHeapspace(); + + $this->assertEquals('Hello World', $result); + } +} diff --git a/tests/Cases/InsertionSortTest.php b/tests/Cases/InsertionSortTest.php new file mode 100644 index 00000000..153c7329 --- /dev/null +++ b/tests/Cases/InsertionSortTest.php @@ -0,0 +1,74 @@ +getInvoker() + ->getStatic() + ->getMethods() + ->call('asc'); + $result = Output::getHeapspace(); + $this->assertEquals( + file_get_contents(__DIR__ . '/templates/InsertionSortAsc.txt'), + $result + ); + } + + public function testInsertionSortDesc() + { + $calculatedValue = static::$initiatedJavaClasses['InsertionSortTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call('desc'); + $result = Output::getHeapspace(); + $this->assertEquals( + file_get_contents(__DIR__ . '/templates/InsertionSortDesc.txt'), + $result + ); + } + + public function testInsertionSortAscByParam() + { + $calculatedValue = static::$initiatedJavaClasses['InsertionSortTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'ascByParam', + [-14, 5, 111, 44, 2, 9999, 99, 123, 1, -10, 33, -50] + ); + $result = Output::getHeapspace(); + $this->assertEquals( + file_get_contents(__DIR__ . '/templates/InsertionSortAsc.txt'), + $result + ); + } + + public function testInsertionSortDescByParam() + { + $calculatedValue = static::$initiatedJavaClasses['InsertionSortTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'descByParam', + [-14, 5, 111, 44, 2, 9999, 99, 123, 1, -10, 33, -50] + ); + $result = Output::getHeapspace(); + $this->assertEquals( + file_get_contents(__DIR__ . '/templates/InsertionSortDesc.txt'), + $result + ); + } +} diff --git a/tests/Cases/InstanceOfTest.php b/tests/Cases/InstanceOfTest.php new file mode 100644 index 00000000..d250bd97 --- /dev/null +++ b/tests/Cases/InstanceOfTest.php @@ -0,0 +1,41 @@ +getInvoker() + ->getStatic() + ->getMethods() + ->call($method); + } + + public function testInstanceOfString() + { + $this->call('instanceOfString'); + $result = Output::getHeapspace(); + $this->assertEquals( + "Hello World\n", + $result + ); + } + + public function testInstanceOfObject() + { + $this->call('instanceOfObject'); + $result = Output::getHeapspace(); + $this->assertEquals( + "Hello World\n", + $result + ); + } +} diff --git a/tests/Cases/IntConstTest.php b/tests/Cases/IntConstTest.php new file mode 100644 index 00000000..6b5e5a9d --- /dev/null +++ b/tests/Cases/IntConstTest.php @@ -0,0 +1,69 @@ +getInvoker() + ->getStatic() + ->getMethods() + ->call($method); + + return $calculatedValue->getValue(); + } + + public function testIntConst0() + { + $actual = $this->call('intConst0'); + $this->assertEquals(0, $actual); + } + + public function testIntConst1() + { + $actual = $this->call('intConst1'); + $this->assertEquals(1, $actual); + } + + public function testIntConst2() + { + $actual = $this->call('intConst2'); + $this->assertEquals(2, $actual); + } + + public function testIntConst3() + { + $actual = $this->call('intConst3'); + $this->assertEquals(3, $actual); + } + + public function testIntConst4() + { + $actual = $this->call('intConst4'); + $this->assertEquals(4, $actual); + } + + public function testIntConst5() + { + $actual = $this->call('intConst5'); + $this->assertEquals(5, $actual); + } + + public function testIntConstM1() + { + $actual = $this->call('intConstM1'); + $this->assertEquals(-1, $actual); + } + + public function testIntPush123() + { + $actual = $this->call('intPush123'); + $this->assertEquals(123, $actual); + } +} diff --git a/tests/Cases/IntegerCalculationTest.php b/tests/Cases/IntegerCalculationTest.php new file mode 100644 index 00000000..3f3e335d --- /dev/null +++ b/tests/Cases/IntegerCalculationTest.php @@ -0,0 +1,118 @@ +getInvoker() + ->getStatic() + ->getMethods() + ->call( + $name, + ...$parameters + ); + } + + public function testIntAdd() + { + $this->assertEquals( + '30', + $this->call( + 'intAdd', + 10, + 20 + ) + ); + } + + public function testIntSub() + { + $this->assertEquals( + '5', + $this->call( + 'intSub', + 10, + 5 + ) + ); + } + + public function testIntNegativeSub() + { + $this->assertEquals( + '-10', + $this->call( + 'intSub', + 10, + 20 + ) + ); + } + + public function testIntMul() + { + $this->assertEquals( + '50', + $this->call( + 'intMul', + 10, + 5 + ) + ); + } + + public function testIntAddFromOtherMethod() + { + $this->assertEquals( + '30', + $this->call( + 'intAddFromOtherMethod', + 10, + 20 + ) + ); + } + + public function testIntSubFromOtherMethod() + { + $this->assertEquals( + '5', + $this->call( + 'intSubFromOtherMethod', + 10, + 5 + ) + ); + } + + public function testIntNegativeSubFromOtherMethod() + { + $this->assertEquals( + '-10', + $this->call( + 'intSubFromOtherMethod', + 10, + 20 + ) + ); + } + + public function testIntMulFromOtherMethod() + { + $this->assertEquals( + '50', + $this->call( + 'intMulFromOtherMethod', + 10, + 5 + ) + ); + } +} diff --git a/tests/Cases/InvokeOperationsTest.php b/tests/Cases/InvokeOperationsTest.php new file mode 100644 index 00000000..41022c9d --- /dev/null +++ b/tests/Cases/InvokeOperationsTest.php @@ -0,0 +1,26 @@ +getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'main', + [] + ); + $getHelloWorld = Output::getHeapspace(); + $this->assertEquals("Hello World!\n", $getHelloWorld); + } +} diff --git a/tests/Cases/JarTest.php b/tests/Cases/JarTest.php new file mode 100644 index 00000000..dbaf726f --- /dev/null +++ b/tests/Cases/JarTest.php @@ -0,0 +1,106 @@ +createJAR( + 'JarTest.jar', + 'OuterClassTest', + [ + 'OuterClassTest', + ] + ) + ->createJAR( + 'JarCallTest.jar', + 'JarCallerTest', + [ + 'JarCalleeTest', + 'JarCallerTest', + ] + ) + ->createJAR( + 'CallEnclosingMethodInJarTest.jar', + 'CallerEnclosingMethodInJarTest', + [ + 'CalleeEnclosingMethodInJarTest', + 'CallerEnclosingMethodInJarTest', + ] + ); + } + + public function testCallWithEntryPoint() + { + (new JavaArchive(__DIR__ . '/caches/JarTest.jar'))->execute([]); + $result = Output::getHeapspace(); + + $this->assertEquals( + 'Called Static Method AND Called Dynamic Method', + $result + ); + } + + public function testCallWithTargetedMethod() + { + (new JavaArchive(__DIR__ . '/caches/JarTest.jar')) + ->getClassByName('OuterClassTest') + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'main', + [] + ); + $result = Output::getHeapspace(); + + $this->assertEquals( + 'Called Static Method AND Called Dynamic Method', + $result + ); + } + + public function testSamePlaceClass() + { + (new JavaArchive(__DIR__ . '/caches/JarCallTest.jar')) + ->getClassByName('JarCallerTest') + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'main', + [] + ); + $result = Output::getHeapspace(); + $this->assertEquals("TEST\n", $result); + } + + public function testEnclosingMethodInJar() + { + (new JavaArchive(__DIR__ . '/caches/CallEnclosingMethodInJarTest.jar')) + ->getClassByName('CallerEnclosingMethodInJarTest') + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'main', + [] + ); + $result = Output::getHeapspace(); + $this->assertEquals("Hello World!\n", $result); + } +} diff --git a/tests/Cases/KotlinTest.php b/tests/Cases/KotlinTest.php new file mode 100644 index 00000000..28458fec --- /dev/null +++ b/tests/Cases/KotlinTest.php @@ -0,0 +1,37 @@ +hasKotlin()) { + return; + } + exec('kotlinc ' . __DIR__ . '/kotlin/HelloWorldKotlin.kt -include-runtime -d ' . __DIR__ . '/caches/HelloWorldKotlin.jar'); + } + + public function testHelloWorldOnKotlin() + { + if (!$this->hasKotlin()) { + $this->markTestSkipped('Not installed kotlin in your environment.'); + return; + } + + $jar = new JavaArchive(__DIR__ . '/caches/HelloWorldKotlin.jar'); + $jar->execute([]); + $result = rtrim(Output::getHeapspace()); + + $this->assertEquals('Hello World!', $result); + } + + private function hasKotlin() + { + exec('which kotlinc', $output); + return count($output) > 0; + } +} diff --git a/tests/Cases/LambdaSyntaxTest.php b/tests/Cases/LambdaSyntaxTest.php new file mode 100644 index 00000000..b182ca34 --- /dev/null +++ b/tests/Cases/LambdaSyntaxTest.php @@ -0,0 +1,30 @@ +getInvoker() + ->getStatic() + ->getMethods() + ->call($method); + } + + public function testParameterWithLambdaSyntax() + { + $this->call( + 'testParameterWithLambdaSyntax' + ); + $result = Output::getHeapspace(); + $this->assertEquals("Hello World!\n", $result); + } +} diff --git a/tests/Cases/LongCalculationTest.php b/tests/Cases/LongCalculationTest.php new file mode 100644 index 00000000..dec71ca9 --- /dev/null +++ b/tests/Cases/LongCalculationTest.php @@ -0,0 +1,120 @@ +getInvoker() + ->getStatic() + ->getMethods() + ->call( + $name, + ...$parameters + ); + } + + public function testLongAdd() + { + $this->assertEquals( + '30', + $this->call( + 'longAdd', + Long_::get(10), + Long_::get(20) + ) + ); + } + + public function testLongSub() + { + $this->assertEquals( + '5', + $this->call( + 'longSub', + Long_::get(10), + Long_::get(5) + ) + ); + } + + public function testLongNegativeSub() + { + $this->assertEquals( + '-10', + $this->call( + 'longSub', + Long_::get(10), + Long_::get(20) + ) + ); + } + + public function testLongMul() + { + $this->assertEquals( + '50', + $this->call( + 'longMul', + Long_::get(10), + Long_::get(5) + ) + ); + } + + public function testLongAddFromOtherMethod() + { + $this->assertEquals( + '30', + $this->call( + 'longAddFromOtherMethod', + Long_::get(10), + Long_::get(20) + ) + ); + } + + public function testLongSubFromOtherMethod() + { + $this->assertEquals( + '5', + $this->call( + 'longSubFromOtherMethod', + Long_::get(10), + Long_::get(5) + ) + ); + } + + public function testLongNegativeSubFromOtherMethod() + { + $this->assertEquals( + '-10', + $this->call( + 'longSubFromOtherMethod', + Long_::get(10), + Long_::get(20) + ) + ); + } + + public function testLongMulFromOtherMethod() + { + $this->assertEquals( + '50', + $this->call( + 'longMulFromOtherMethod', + Long_::get(10), + Long_::get(5) + ) + ); + } +} diff --git a/tests/Cases/LoopTest.php b/tests/Cases/LoopTest.php new file mode 100644 index 00000000..56bea9d7 --- /dev/null +++ b/tests/Cases/LoopTest.php @@ -0,0 +1,56 @@ +getInvoker() + ->getStatic() + ->getMethods() + ->call('calculateByFor', 10); + $this->assertEquals( + '45', + (string) $calculatedValue + ); + + $calculatedValue = static::$initiatedJavaClasses['LoopTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call('calculateByFor', 20); + $this->assertEquals( + '190', + (string) $calculatedValue + ); + } + + public function testCallCalculateByWhile() + { + $calculatedValue = static::$initiatedJavaClasses['LoopTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call('calculateByWhile', 10); + $this->assertEquals( + '45', + (string) $calculatedValue + ); + + $calculatedValue = static::$initiatedJavaClasses['LoopTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call('calculateByWhile', 20); + $this->assertEquals( + '190', + (string) $calculatedValue + ); + } +} diff --git a/tests/Cases/MethodParameterTest.php b/tests/Cases/MethodParameterTest.php new file mode 100644 index 00000000..abaffeb3 --- /dev/null +++ b/tests/Cases/MethodParameterTest.php @@ -0,0 +1,71 @@ +getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'main' + ); + } catch (NoSuchMethodException $e) { + $e = get_class($e); + } + + $this->assertEquals( + NoSuchMethodException::class, + $e + ); + } + + public function testMethodParameterIsEmpty() + { + $value = static::$initiatedJavaClasses['MethodParameterTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'noParameterMethod' + ); + $result = Output::getHeapspace(); + + $this->assertEquals('Hello World!', $result); + } + + public function testPassParameterToNoParameterMethod() + { + $e = null; + try { + $value = static::$initiatedJavaClasses['MethodParameterTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'noParameterMethod', + 1234, + 5678 + ); + } catch (NoSuchMethodException $e) { + $e = get_class($e); + } + + $this->assertEquals( + NoSuchMethodException::class, + $e + ); + } +} diff --git a/tests/Cases/NegationTest.php b/tests/Cases/NegationTest.php new file mode 100644 index 00000000..ee1d0da8 --- /dev/null +++ b/tests/Cases/NegationTest.php @@ -0,0 +1,137 @@ +getInvoker() + ->getStatic() + ->getMethods() + ->call( + $method, + ...$arguments + ); + + return $calculatedValue->getValue(); + } + + public function testNegateIntPattern1() + { + $actual = $this->call( + 'negateInt', + Int_::get(123) + ); + $this->assertEquals('-123', $actual); + } + + public function testNegateIntPattern2() + { + $actual = $this->call( + 'negateInt', + Int_::get(-123) + ); + $this->assertEquals('123', $actual); + } + + public function testNegateDoublePattern1() + { + $actual = $this->call( + 'negateDouble', + Double_::get(123) + ); + $this->assertEquals('-123', $actual); + } + + public function testNegateDoublePattern2() + { + $actual = $this->call( + 'negateDouble', + Double_::get(-123) + ); + $this->assertEquals('123', $actual); + } + + public function testNegateDoublePattern3() + { + $actual = $this->call( + 'negateDouble', + Double_::get(123.5) + ); + $this->assertEquals('-123.5', $actual); + } + + public function testNegateDoublePattern4() + { + $actual = $this->call( + 'negateDouble', + Double_::get(-123.5) + ); + $this->assertEquals('123.5', $actual); + } + + public function testNegateFloatPattern1() + { + $actual = $this->call( + 'negateFloat', + Float_::get(123) + ); + $this->assertEquals('-123', $actual); + } + + public function testNegateFloatPattern2() + { + $actual = $this->call( + 'negateFloat', + Float_::get(-123) + ); + $this->assertEquals('123', $actual); + } + + public function testNegateFloatPattern3() + { + $actual = $this->call( + 'negateFloat', + Float_::get(123.5) + ); + $this->assertEquals('-123.5', $actual); + } + + public function testNegateFloatPattern4() + { + $actual = $this->call( + 'negateFloat', + Float_::get(-123.5) + ); + $this->assertEquals('123.5', $actual); + } + + public function testNegateLongPattern1() + { + $actual = $this->call( + 'negateLong', + Long_::get(123) + ); + $this->assertEquals('-123', $actual); + } + + public function testNegateLongPattern2() + { + $actual = $this->call( + 'negateLong', + Long_::get(-123) + ); + $this->assertEquals('123', $actual); + } +} diff --git a/tests/Cases/ObjectCompareTest.php b/tests/Cases/ObjectCompareTest.php new file mode 100644 index 00000000..90e9fb1c --- /dev/null +++ b/tests/Cases/ObjectCompareTest.php @@ -0,0 +1,29 @@ +getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'compareInitiatedObjects' + ); + $result = trim(Output::getHeapspace()); + + $this->assertEquals( + 'NotSame', + $result + ); + } +} diff --git a/tests/Cases/OuterClassTest.php b/tests/Cases/OuterClassTest.php new file mode 100644 index 00000000..b939b3fa --- /dev/null +++ b/tests/Cases/OuterClassTest.php @@ -0,0 +1,31 @@ +getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'main', + [] + ); + $result = Output::getHeapspace(); + + $this->assertEquals( + 'Called Static Method AND Called Dynamic Method', + $result + ); + } +} diff --git a/tests/Cases/OutputDebugTraceTest.php b/tests/Cases/OutputDebugTraceTest.php new file mode 100644 index 00000000..23aeaf38 --- /dev/null +++ b/tests/Cases/OutputDebugTraceTest.php @@ -0,0 +1,30 @@ +getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'main', + ['Hello', ' ', 'World'] + ); + + ob_start(); + static::$initiatedJavaClasses['OutputDebugTraceTest']->debug(); + $result = ob_get_clean(); + $this->assertEquals( + file_get_contents(__DIR__ . '/templates/DebugTraceTest.txt'), + $result + ); + } +} diff --git a/tests/Cases/Packages/JavaIoPrintStreamClassTest.php b/tests/Cases/Packages/JavaIoPrintStreamClassTest.php new file mode 100644 index 00000000..d1027d16 --- /dev/null +++ b/tests/Cases/Packages/JavaIoPrintStreamClassTest.php @@ -0,0 +1,155 @@ +getInvoker() + ->getStatic() + ->getMethods() + ->call( + $method, + ...$arguments + ); + return Output::getHeapspace(); + } + + private function callWithExpectingException($method, ...$arguments) + { + try { + return $this->call($method, ...$arguments); + } catch (UncaughtException $e) { + $this->expectedSpecialException = get_class($e->getPrevious()); + } + return Output::getHeapspace(); + } + + public function testPrintlnWithoutParams() + { + $result = $this->call(explode('::', __METHOD__)[1]); + $this->assertEquals("\n", $result); + } + + public function testPrintlnWithStringParams() + { + $result = $this->call(explode('::', __METHOD__)[1]); + $this->assertEquals("Hello World\n", $result); + } + + public function testPrintlnWithNullStringParams() + { + $result = $this->call(explode('::', __METHOD__)[1]); + $this->assertEquals("null\n", $result); + } + + public function testPrintlnWithCharParams() + { + $result = $this->call(explode('::', __METHOD__)[1]); + $this->assertEquals("A\n", $result); + } + + public function testPrintlnWithCharArrayParams() + { + $result = $this->call(explode('::', __METHOD__)[1]); + $this->assertEquals("ABC\n", $result); + } + + public function testPrintlnWithNullCharArrayParams() + { + $result = $this->callWithExpectingException(explode('::', __METHOD__)[1]); + $this->assertEquals("\n", $result); + $this->assertSame( + NullPointerException::class, + $this->expectedSpecialException + ); + } + + public function testPrintlnWithFloatParams() + { + $result = $this->call(explode('::', __METHOD__)[1]); + $this->assertStringContainsString('0.123', $result); + } + + public function testPrintlnWithDoubleParams() + { + $result = $this->call(explode('::', __METHOD__)[1]); + $this->assertEquals("0.123\n", $result); + } + + public function testPrintlnWithBooleanParams() + { + $result = $this->call(explode('::', __METHOD__)[1] . '_true'); + $this->assertEquals("true\n", $result); + + $result = $this->call(explode('::', __METHOD__)[1] . '_false'); + $this->assertEquals("false\n", $result); + } + + public function testPrintWithStringParams() + { + $result = $this->call(explode('::', __METHOD__)[1]); + $this->assertEquals('Hello World', $result); + } + + public function testPrintWithNullStringParams() + { + $result = $this->call(explode('::', __METHOD__)[1]); + $this->assertEquals('null', $result); + } + + public function testPrintWithCharParams() + { + $result = $this->call(explode('::', __METHOD__)[1]); + $this->assertEquals('A', $result); + } + + public function testPrintWithCharArrayParams() + { + $result = $this->call(explode('::', __METHOD__)[1]); + $this->assertEquals('ABC', $result); + } + + public function testPrintWithNullCharArrayParams() + { + $result = $this->callWithExpectingException(explode('::', __METHOD__)[1]); + $this->assertEquals('', $result); + $this->assertSame( + NullPointerException::class, + $this->expectedSpecialException + ); + } + + public function testPrintWithFloatParams() + { + $result = $this->call(explode('::', __METHOD__)[1]); + $this->assertStringContainsString('0.123', $result); + } + + public function testPrintWithDoubleParams() + { + $result = $this->call(explode('::', __METHOD__)[1]); + $this->assertEquals('0.123', $result); + } + + public function testPrintWithBooleanParams() + { + $result = $this->call(explode('::', __METHOD__)[1] . '_true'); + $this->assertEquals('true', $result); + + $result = $this->call(explode('::', __METHOD__)[1] . '_false'); + $this->assertEquals('false', $result); + } +} diff --git a/tests/Cases/Packages/JavaLangMathTest.php b/tests/Cases/Packages/JavaLangMathTest.php new file mode 100644 index 00000000..cee21788 --- /dev/null +++ b/tests/Cases/Packages/JavaLangMathTest.php @@ -0,0 +1,505 @@ +getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'abs', + 0.5 + ); + $value = (float) Output::getHeapspace(); + $this->assertEquals(0.5, $value); + } + + public function testAcos() + { + static::$initiatedJavaClasses['JavaLangMathTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'acos', + 0.5 + ); + $value = (float) Output::getHeapspace(); + $this->assertEquals(1.0471975511966, $value); + } + + public function testAddExact() + { + static::$initiatedJavaClasses['JavaLangMathTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'addExact', + 1, + 2 + ); + $value = (int) Output::getHeapspace(); + $this->assertEquals(3, $value); + } + + public function testAsin() + { + static::$initiatedJavaClasses['JavaLangMathTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'asin', + 0.5 + ); + $value = (float) Output::getHeapspace(); + $this->assertEquals(0.5235987755983, $value); + } + + public function testAtan() + { + static::$initiatedJavaClasses['JavaLangMathTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'atan', + 0.5 + ); + $value = (float) Output::getHeapspace(); + $this->assertEquals(0.46364760900081, $value); + } + + public function testAtan2() + { + static::$initiatedJavaClasses['JavaLangMathTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'atan2', + 1.0, + 2.0 + ); + $value = (float) Output::getHeapspace(); + $this->assertEquals(0.46364760900081, $value); + } + + public function testCbrt() + { + static::$initiatedJavaClasses['JavaLangMathTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'cbrt', + 0.5 + ); + $value = (float) Output::getHeapspace(); + $this->assertEquals(0.7937005259841, $value); + } + + public function testCeil() + { + static::$initiatedJavaClasses['JavaLangMathTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'ceil', + 0.5 + ); + $value = (int) Output::getHeapspace(); + $this->assertEquals(1, $value); + } + + public function testCopySign() + { + static::$initiatedJavaClasses['JavaLangMathTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'copySign', + 0.5, + 1.0 + ); + $value = (float) Output::getHeapspace(); + $this->assertEquals(0.5, $value); + } + + public function testCos() + { + static::$initiatedJavaClasses['JavaLangMathTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'cos', + 0.5 + ); + $value = (float) Output::getHeapspace(); + $this->assertEquals(0.87758256189037, $value); + } + + public function testCosh() + { + static::$initiatedJavaClasses['JavaLangMathTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'cosh', + 0.5 + ); + $value = (float) Output::getHeapspace(); + $this->assertEquals(1.1276259652064, $value); + } + + public function testDecrementExact() + { + static::$initiatedJavaClasses['JavaLangMathTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'decrementExact', + 1234 + ); + $value = (int) Output::getHeapspace(); + $this->assertEquals(1233, $value); + } + + public function testExp() + { + static::$initiatedJavaClasses['JavaLangMathTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'exp', + 0.5 + ); + $value = (float) Output::getHeapspace(); + $this->assertEquals(1.6487212707001, $value); + } + + public function testExpm1() + { + static::$initiatedJavaClasses['JavaLangMathTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'expm1', + 0.5 + ); + $value = (float) Output::getHeapspace(); + $this->assertEquals(0.64872127070013, $value); + } + + public function testFloor() + { + static::$initiatedJavaClasses['JavaLangMathTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'floor', + 0.5 + ); + $value = (int) Output::getHeapspace(); + $this->assertEquals(0, $value); + } + + // TODO: Java 9 + // public function testFloorDiv() + // { + // // static::$initiatedJavaClasses['JavaLangMathTest'] + // ->getInvoker() + // ->getStatic() + // ->getMethods() + // ->call( + // 'floorDiv', + // -4, + // 3 + // ); + // $value = (int) PrintTool::getHeapspace(); + // $this->assertEquals(-2, $value); + // } + + // TODO: Java 9 + // public function testFloorMod() + // { + // // static::$initiatedJavaClasses['JavaLangMathTest'] + // ->getInvoker() + // ->getStatic() + // ->getMethods() + // ->call( + // 'floorMod', + // 4, + // -3 + // ); + // $value = (int) PrintTool::getHeapspace(); + // $this->assertEquals(-2, $value); + // } + + public function testIncrementExact() + { + static::$initiatedJavaClasses['JavaLangMathTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'incrementExact', + 1234 + ); + $value = (int) Output::getHeapspace(); + $this->assertEquals(1235, $value); + } + + public function testLog() + { + static::$initiatedJavaClasses['JavaLangMathTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'log', + 0.5 + ); + $value = (float) Output::getHeapspace(); + $this->assertEquals(-0.69314718055995, $value); + } + + public function testLog10() + { + static::$initiatedJavaClasses['JavaLangMathTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'log10', + 0.5 + ); + $value = (float) Output::getHeapspace(); + $this->assertEquals(-0.30102999566398, $value); + } + + public function testLog1p() + { + static::$initiatedJavaClasses['JavaLangMathTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'log1p', + 0.5 + ); + $value = (float) Output::getHeapspace(); + $this->assertEquals(0.40546510810816, $value); + } + + public function testMax() + { + static::$initiatedJavaClasses['JavaLangMathTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'max', + 1234, + 5678 + ); + $value = (int) Output::getHeapspace(); + $this->assertEquals(5678, $value); + } + + public function testMin() + { + static::$initiatedJavaClasses['JavaLangMathTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'min', + 1234, + 5678 + ); + $value = (int) Output::getHeapspace(); + $this->assertEquals(1234, $value); + } + + // TODO: Java 9 + // public function testMultiplyExact() + // { + // // static::$initiatedJavaClasses['JavaLangMathTest'] + // ->getInvoker() + // ->getStatic() + // ->getMethods() + // ->call( + // 'multiplyExact', + // 1234, + // 5678 + // ); + // $value = (int) PrintTool::getHeapspace(); + // $this->assertEquals(7006652, $value); + // } + + // TODO: Java 9 + // public function testMultiplyFull() + // { + // // static::$initiatedJavaClasses['JavaLangMathTest'] + // ->getInvoker() + // ->getStatic() + // ->getMethods() + // ->call( + // 'multiplyFull', + // 1234, + // 5678 + // ); + // $value = (int) PrintTool::getHeapspace(); + // $this->assertEquals(7006652, $value); + // } + + public function testPow() + { + static::$initiatedJavaClasses['JavaLangMathTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'pow', + 1234, + 3 + ); + $value = (int) Output::getHeapspace(); + $this->assertEquals(1879080904, $value); + } + + public function testRandom() + { + mt_srand(1234); + + static::$initiatedJavaClasses['JavaLangMathTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'random' + ); + $value = (float) Output::getHeapspace(); + $this->assertEquals(0.19151945001982, $value); + } + + public function testRound() + { + static::$initiatedJavaClasses['JavaLangMathTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'round', + 0.5 + ); + $value = (float) Output::getHeapspace(); + $this->assertEquals(1.0, $value); + } + + public function testSin() + { + static::$initiatedJavaClasses['JavaLangMathTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'sin', + 0.5 + ); + $value = (float) Output::getHeapspace(); + $this->assertEquals(0.4794255386042, $value); + } + + public function testSinh() + { + static::$initiatedJavaClasses['JavaLangMathTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'sinh', + 0.5 + ); + $value = (float) Output::getHeapspace(); + $this->assertEquals(0.52109530549375, $value); + } + + public function testSqrt() + { + static::$initiatedJavaClasses['JavaLangMathTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'sqrt', + 4.0 + ); + $value = (float) Output::getHeapspace(); + $this->assertEquals(2.0, $value); + } + + public function testSubtractExact() + { + static::$initiatedJavaClasses['JavaLangMathTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'subtractExact', + 5678, + 1234 + ); + $value = (int) Output::getHeapspace(); + $this->assertEquals(4444, $value); + } + + public function testTan() + { + static::$initiatedJavaClasses['JavaLangMathTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'tan', + 0.5 + ); + $value = (float) Output::getHeapspace(); + $this->assertEquals(0.54630248984379, $value); + } + + public function testTanh() + { + static::$initiatedJavaClasses['JavaLangMathTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'tanh', + 0.5 + ); + $value = (float) Output::getHeapspace(); + $this->assertEquals(0.46211715726001, $value); + } +} diff --git a/tests/Cases/Packages/JavaLangStringTest.php b/tests/Cases/Packages/JavaLangStringTest.php new file mode 100644 index 00000000..2f5e4391 --- /dev/null +++ b/tests/Cases/Packages/JavaLangStringTest.php @@ -0,0 +1,204 @@ +getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'charAtIndex', + 'abc', + 1 + ); + $value = Output::getHeapspace(); + $this->assertEquals('b', $value); + } + + public function testThrowsCharAtNegativeIndex() + { + $this->expectException(IndexOutOfBoundsException::class); + $this->expectExceptionMessage('String index out of range: -1'); + + try { + static::$initiatedJavaClasses['JavaLangStringTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'charAtIndex', + 'abc', + -1 + ); + } catch (UncaughtException $e) { + throw $e->getPrevious(); + } + } + + public function testThrowsCharAtOutOfRangeIndex() + { + $this->expectException(IndexOutOfBoundsException::class); + $this->expectExceptionMessage('String index out of range: 3'); + + try { + static::$initiatedJavaClasses['JavaLangStringTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'charAtIndex', + 'abc', + 3 + ); + } catch (UncaughtException $e) { + throw $e->getPrevious(); + } + } + + public function testConcat() + { + static::$initiatedJavaClasses['JavaLangStringTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'concat', + 'abc', + 'def' + ); + $value = Output::getHeapspace(); + $this->assertEquals('abcdef', $value); + } + + public function testHashCode() + { + static::$initiatedJavaClasses['JavaLangStringTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'testHashCode' + ); + + $values = array_filter(explode("\n", Output::getHeapspace())); + $this->assertCount(3, $values); + $this->assertSame('-640608884', $values[0]); + $this->assertSame('-640608884', $values[1]); + $this->assertSame('-640608884', $values[2]); + } + + public function testIntern() + { + static::$initiatedJavaClasses['JavaLangStringTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'intern' + ); + [ $intern, $literal ] = array_filter(explode("\n", Output::getHeapspace())); + + $this->assertIsNumeric($intern); + $this->assertIsNumeric($literal); + + $this->assertSame($intern, $literal); + } + + public function testNotInterned() + { + static::$initiatedJavaClasses['JavaLangStringTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'notInterned' + ); + [ $intern, $literal ] = array_filter(explode("\n", Output::getHeapspace())); + + $this->assertIsNumeric($intern); + $this->assertIsNumeric($literal); + + $this->assertNotSame($intern, $literal); + } + + public function testNotInternedAfterLiteral() + { + // This test need dynamic loading. + static::$initiatedJavaClasses['JavaLangStringTest'] = JavaClass::load('JavaLangStringTest'); + + static::$initiatedJavaClasses['JavaLangStringTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'notInternedAfterLiteral' + ); + [ $intern, $literal1, $literal2 ] = array_filter(explode("\n", Output::getHeapspace())); + + $this->assertIsNumeric($intern); + $this->assertIsNumeric($literal1); + $this->assertIsNumeric($literal2); + + $this->assertNotSame($intern, $literal1); + $this->assertNotSame($intern, $literal2); + $this->assertSame($literal1, $literal2); + } + + public function testReplace() + { + static::$initiatedJavaClasses['JavaLangStringTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'replace', + 'aabbccaabbcc', + 'bb', + 'cc' + ); + $value = Output::getHeapspace(); + $this->assertEquals('aaccccaacccc', $value); + } + + public function testToLowerCase() + { + static::$initiatedJavaClasses['JavaLangStringTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'toLowerCase', + 'Hello, World' + ); + $value = Output::getHeapspace(); + $this->assertEquals('hello, world', $value); + } + + public function testToUpperCase() + { + static::$initiatedJavaClasses['JavaLangStringTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'toUpperCase', + 'Hello, World' + ); + $value = Output::getHeapspace(); + $this->assertEquals('HELLO, WORLD', $value); + } +} diff --git a/tests/Cases/Packages/JavaLangSystemTest.php b/tests/Cases/Packages/JavaLangSystemTest.php new file mode 100644 index 00000000..007837f9 --- /dev/null +++ b/tests/Cases/Packages/JavaLangSystemTest.php @@ -0,0 +1,35 @@ +getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'identityHashCode' + ); + + $values = array_filter(explode("\n", Output::getHeapspace())); + $this->assertCount(2, $values); + + $hashCodes = []; + + foreach ($values as $value) { + $this->assertIsNumeric($value); + $this->assertNotContains($value, $hashCodes); + $hashCodes[] = $value; + } + } +} diff --git a/tests/Cases/Packages/JavaUtilObjectsTest.php b/tests/Cases/Packages/JavaUtilObjectsTest.php new file mode 100644 index 00000000..5cf10bba --- /dev/null +++ b/tests/Cases/Packages/JavaUtilObjectsTest.php @@ -0,0 +1,30 @@ +getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'testHashCode' + ); + + $values = array_filter(explode("\n", Output::getHeapspace())); + $this->assertCount(3, $values); + $this->assertSame('-640608884', $values[0]); + $this->assertSame('-640608884', $values[1]); + $this->assertSame('-640608884', $values[2]); + } +} diff --git a/tests/Cases/Packages/PHPJava/Extended/_Object.php b/tests/Cases/Packages/PHPJava/Extended/_Object.php new file mode 100644 index 00000000..e6b88e4e --- /dev/null +++ b/tests/Cases/Packages/PHPJava/Extended/_Object.php @@ -0,0 +1,16 @@ +expectException(CloneNotSupportedException::class); + + clone new class() { + use \PHPJava\Packages\PHPJava\Extended\Object_; + }; + } +} diff --git a/tests/Cases/QuickSortTest.php b/tests/Cases/QuickSortTest.php new file mode 100644 index 00000000..03e63536 --- /dev/null +++ b/tests/Cases/QuickSortTest.php @@ -0,0 +1,46 @@ +getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'asc', + [-14, 5, 111, 44, 2, 9999, 99, 123, 1, -10, 33, -50] + ); + $result = Output::getHeapspace(); + $this->assertEquals( + file_get_contents(__DIR__ . '/templates/QuickSortAsc.txt'), + $result + ); + } + + public function testQuickSortDesc() + { + $calculatedValue = static::$initiatedJavaClasses['QuickSortTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'desc', + [-14, 5, 111, 44, 2, 9999, 99, 123, 1, -10, 33, -50] + ); + $result = Output::getHeapspace(); + $this->assertEquals( + file_get_contents(__DIR__ . '/templates/QuickSortDesc.txt'), + $result + ); + } +} diff --git a/tests/Cases/SwitchTest.php b/tests/Cases/SwitchTest.php new file mode 100644 index 00000000..48b7868a --- /dev/null +++ b/tests/Cases/SwitchTest.php @@ -0,0 +1,120 @@ +getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'tableswitch', + -1 + ); + $result = Output::getHeapspace(); + + $this->assertEquals( + 'Cat', + $result + ); + } + + public function testTableswitchPattern2() + { + $calculatedValue = static::$initiatedJavaClasses['SwitchTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'tableswitch', + 0 + ); + $result = Output::getHeapspace(); + + $this->assertEquals( + 'Dog', + $result + ); + } + + public function testTableswitchPattern3() + { + $calculatedValue = static::$initiatedJavaClasses['SwitchTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'tableswitch', + 1 + ); + $result = Output::getHeapspace(); + + $this->assertEquals( + 'Hamster', + $result + ); + } + + public function testCallLookupswitchPattern1() + { + $calculatedValue = static::$initiatedJavaClasses['SwitchTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'lookupswitch', + 1234 + ); + $result = Output::getHeapspace(); + + $this->assertEquals( + 'Lion', + $result + ); + } + + public function testCallLookupswitchPattern2() + { + $calculatedValue = static::$initiatedJavaClasses['SwitchTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'lookupswitch', + 5678 + ); + $result = Output::getHeapspace(); + + $this->assertEquals( + 'Panda', + $result + ); + } + + public function testCallLookupswitchPattern3() + { + $calculatedValue = static::$initiatedJavaClasses['SwitchTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'lookupswitch', + 9999 + ); + $result = Output::getHeapspace(); + + $this->assertEquals( + 'Elephant', + $result + ); + } +} diff --git a/tests/Cases/TryCatchTest.php b/tests/Cases/TryCatchTest.php new file mode 100644 index 00000000..86364d28 --- /dev/null +++ b/tests/Cases/TryCatchTest.php @@ -0,0 +1,72 @@ +getInvoker() + ->getStatic() + ->getMethods() + ->call('testPassthroughTryStatement'); + + $this->assertEquals( + '1', + $result + ); + } + + public function testPassthroughCatchStatement() + { + $result = static::$initiatedJavaClasses['TryCatchTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call('testPassthroughCatchStatement'); + + $this->assertEquals( + '-1', + $result + ); + } + + public function testImitationThrowException() + { + $result = static::$initiatedJavaClasses['TryCatchTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call('testImitationThrowException'); + + $this->assertEquals( + '-1', + $result + ); + } + + public function testImitationThrownExceptionHasPreviousException() + { + $this->expectException(IndexOutOfBoundsException::class); + $this->expectExceptionMessage('String index out of range: -1'); + + try { + $result = static::$initiatedJavaClasses['TryCatchTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call('testImitationThrownExceptionHasPreviousException'); + } catch (UncaughtException $e) { + // Unwrap the original exception + throw $e->getPrevious(); + } + } +} diff --git a/nbproject/private/config.properties b/tests/Cases/caches/.gitkeep similarity index 100% rename from nbproject/private/config.properties rename to tests/Cases/caches/.gitkeep diff --git a/tests/Cases/caches/compiler/.gitkeep b/tests/Cases/caches/compiler/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/tests/Cases/fixtures/java/AccessDynamicFieldTest.java b/tests/Cases/fixtures/java/AccessDynamicFieldTest.java new file mode 100644 index 00000000..3cb262a5 --- /dev/null +++ b/tests/Cases/fixtures/java/AccessDynamicFieldTest.java @@ -0,0 +1,5 @@ +class AccessDynamicFieldTest +{ + public int number = 5; + public String string = "Hello World"; +} diff --git a/tests/Cases/fixtures/java/AccessDynamicMethodTest.java b/tests/Cases/fixtures/java/AccessDynamicMethodTest.java new file mode 100644 index 00000000..269ca9ef --- /dev/null +++ b/tests/Cases/fixtures/java/AccessDynamicMethodTest.java @@ -0,0 +1,19 @@ +class AccessDynamicMethodTest +{ + public void main(String[] args) + { + System.out.print(args[0]); + System.out.print(args[1]); + } + + public void main(int[] args) + { + System.out.print(args[0] * 2); + System.out.print(args[1] * 2); + } + + public String returnTest() + { + return "Return Test."; + } +} diff --git a/tests/Cases/fixtures/java/AccessStaticFieldTest.java b/tests/Cases/fixtures/java/AccessStaticFieldTest.java new file mode 100644 index 00000000..3784ac17 --- /dev/null +++ b/tests/Cases/fixtures/java/AccessStaticFieldTest.java @@ -0,0 +1,5 @@ +class AccessStaticFieldTest +{ + public static int number = 5; + public static String string = "Hello World"; +} diff --git a/tests/Cases/fixtures/java/AccessStaticMethodTest.java b/tests/Cases/fixtures/java/AccessStaticMethodTest.java new file mode 100644 index 00000000..68de6427 --- /dev/null +++ b/tests/Cases/fixtures/java/AccessStaticMethodTest.java @@ -0,0 +1,19 @@ +class AccessStaticMethodTest +{ + public static void main(String[] args) + { + System.out.print(args[0]); + System.out.print(args[1]); + } + + public static void main(int[] args) + { + System.out.print(args[0] * 2); + System.out.print(args[1] * 2); + } + + public static String returnTest() + { + return "Return Test."; + } +} diff --git a/tests/Cases/fixtures/java/ArrayTest.java b/tests/Cases/fixtures/java/ArrayTest.java new file mode 100644 index 00000000..a321a02b --- /dev/null +++ b/tests/Cases/fixtures/java/ArrayTest.java @@ -0,0 +1,61 @@ +class ArrayTest +{ + public static int[] createIntArray() + { + return new int[] { 1, 2, 3 }; + } + + public static String[] createStringArray() + { + return new String[] { "foo", "bar", "baz" }; + } + + public static long[] createLongArray() + { + return new long[] { 1, 2, 3 }; + } + + public static float[] createFloatArray() + { + return new float[] { 1.5F, 2.5F, 3.5F }; + } + + public static double[] createDoubleArray() + { + return new double[] { 1.5, 2.5, 3.5 }; + } + + public static boolean[] createBooleanArray() + { + return new boolean[] { true, false, true }; + } + + public static char[] createCharArray() + { + return new char[] { 'A', 'B', 'C' }; + } + + public static byte[] createByteArray() + { + return new byte[] { 0x01, 0x02, 0x03 }; + } + + public static String multiDimensionArrayWithConstants() + { + String[][] a = { + {"Hello", " ", "World!"} + }; + + return a[0][0] + "" + a[0][1] + "" + a[0][2]; + } + + public static String[] multiDimensionArrayWithDynamic() + { + String[][] a = new String[1][3]; + a[0][0] = "Hello"; + a[0][1] = " "; + a[0][2] = "World!"; + + return a[0]; + } +} diff --git a/tests/Cases/fixtures/java/BigNumberCalculationTest.java b/tests/Cases/fixtures/java/BigNumberCalculationTest.java new file mode 100644 index 00000000..fdacb292 --- /dev/null +++ b/tests/Cases/fixtures/java/BigNumberCalculationTest.java @@ -0,0 +1,23 @@ +class BigNumberCalculationTest +{ + public static long testAdd(long a, long b) + { + return a + b; + } + + public static long testSub(long a, long b) + { + return a - b; + } + + public static long testMul(long a, long b) + { + return a * b; + } + + public static long testDiv(long a, long b) + { + return a / b; + } + +} diff --git a/tests/Cases/fixtures/java/BinaryOperatorTest.java b/tests/Cases/fixtures/java/BinaryOperatorTest.java new file mode 100644 index 00000000..e2bf12f3 --- /dev/null +++ b/tests/Cases/fixtures/java/BinaryOperatorTest.java @@ -0,0 +1,92 @@ +class BinaryOperatorTest +{ + public static int intAdd(int value1, int value2) + { + return value1 + value2; + } + + public static int intSub(int value1, int value2) + { + return value1 - value2; + } + + public static int intMul(int value1, int value2) + { + return value1 * value2; + } + + public static int intShl(int value1, int value2) + { + return value1 << value2; + } + + public static int intShr(int value1, int value2) + { + return value1 >> value2; + } + + public static int intUshr(int value1, int value2) + { + return value1 >>> value2; + } + + public static int intAnd(int value1, int value2) + { + return value1 & value2; + } + + public static int intOr(int value1, int value2) + { + return value1 | value2; + } + + public static int intXor(int value1, int value2) + { + return value1 ^ value2; + } + + public static long longAdd(long value1, long value2) + { + return value1 + value2; + } + + public static long longSub(long value1, long value2) + { + return value1 - value2; + } + + public static long longMul(long value1, long value2) + { + return value1 * value2; + } + + public static long longShl(long value1, long value2) + { + return value1 << value2; + } + + public static long longShr(long value1, long value2) + { + return value1 >> value2; + } + + public static long longUshr(long value1, long value2) + { + return value1 >>> value2; + } + + public static long longAnd(long value1, long value2) + { + return value1 & value2; + } + + public static long longOr(long value1, long value2) + { + return value1 | value2; + } + + public static long longXor(long value1, long value2) + { + return value1 ^ value2; + } +} \ No newline at end of file diff --git a/tests/Cases/fixtures/java/BoundaryValueTypeForBooleanTest.java b/tests/Cases/fixtures/java/BoundaryValueTypeForBooleanTest.java new file mode 100644 index 00000000..572cf96e --- /dev/null +++ b/tests/Cases/fixtures/java/BoundaryValueTypeForBooleanTest.java @@ -0,0 +1,36 @@ +class BoundaryValueTypeForBooleanTest +{ + // boundary value tests for Boolean + public static boolean s_b0 = true; + public static boolean s_b1 = false; + public boolean d_b0 = true; + public boolean d_b1 = false; + public static boolean[] s_a_b = { + true, + false, + }; + public boolean[] d_a_b = { + true, + false, + }; + public static boolean[][] s_ma_b = { + { + true, + false, + }, + { + false, + true, + }, + }; + public boolean[][] d_ma_b = { + { + true, + false, + }, + { + false, + true, + }, + }; +} diff --git a/tests/Cases/fixtures/java/BoundaryValueTypeForByteTest.java b/tests/Cases/fixtures/java/BoundaryValueTypeForByteTest.java new file mode 100644 index 00000000..de5e53b3 --- /dev/null +++ b/tests/Cases/fixtures/java/BoundaryValueTypeForByteTest.java @@ -0,0 +1,62 @@ +class BoundaryValueTypeForByteTest +{ + // boundary value tests for Byte + public static byte s_by0 = 0x00; + public static byte s_by1 = 0x7F; + public static byte s_by2 = 0x12; + public static byte s_by3 = -0x01; + public static byte s_by4 = -0x7F; + public static byte s_by5 = -0x12; + public static byte s_by6 = -0x80; + public byte d_by0 = 0x00; + public byte d_by1 = 0x7F; + public byte d_by2 = 0x12; + public byte d_by3 = -0x01; + public byte d_by4 = -0x7F; + public byte d_by5 = -0x12; + public byte d_by6 = -0x80; + public static byte[] s_a_by = { + 0x00, + 0x7F, + 0x12, + -0x01, + -0x7F, + -0x12, + -0x80, + }; + public byte[] d_a_by = { + 0x00, + 0x7F, + 0x12, + -0x01, + -0x7F, + -0x12, + -0x80, + }; + public static byte[][] s_ma_by = { + { + 0x00, + 0x7F, + 0x12, + }, + { + -0x01, + -0x7F, + -0x12, + -0x80, + }, + }; + public byte[][] d_ma_by = { + { + 0x00, + 0x7F, + 0x12, + }, + { + -0x01, + -0x7F, + -0x12, + -0x80, + }, + }; +} diff --git a/tests/Cases/fixtures/java/BoundaryValueTypeForCharTest.java b/tests/Cases/fixtures/java/BoundaryValueTypeForCharTest.java new file mode 100644 index 00000000..6f61ec2a --- /dev/null +++ b/tests/Cases/fixtures/java/BoundaryValueTypeForCharTest.java @@ -0,0 +1,50 @@ +class BoundaryValueTypeForCharTest +{ + // boundary value tests for Char + public static char s_c0 = '\u0000'; + public static char s_c1 = '\uFFFF'; + public static char s_c2 = '\u7FFF'; + public static char s_c3 = '\u8000'; + public static char s_c4 = '\u9123'; + public char d_c0 = '\u0000'; + public char d_c1 = '\uFFFF'; + public char d_c2 = '\u7FFF'; + public char d_c3 = '\u8000'; + public char d_c4 = '\u9123'; + public static char[] s_a_c = { + '\u0000', + '\uFFFF', + '\u7FFF', + '\u8000', + '\u9123', + }; + public char[] d_a_c = { + '\u0000', + '\uFFFF', + '\u7FFF', + '\u8000', + '\u9123', + }; + public static char[][] s_ma_c = { + { + '\u0000', + '\uFFFF', + '\u7FFF', + }, + { + '\u8000', + '\u9123', + }, + }; + public char[][] d_ma_c = { + { + '\u0000', + '\uFFFF', + '\u7FFF', + }, + { + '\u8000', + '\u9123', + }, + }; +} diff --git a/tests/Cases/fixtures/java/BoundaryValueTypeForDoubleTest.java b/tests/Cases/fixtures/java/BoundaryValueTypeForDoubleTest.java new file mode 100644 index 00000000..393c37cf --- /dev/null +++ b/tests/Cases/fixtures/java/BoundaryValueTypeForDoubleTest.java @@ -0,0 +1,287 @@ +class BoundaryValueTypeForDoubleTest +{ + // boundary value tests for loats + public static double s_d0 = 0; + public static double s_d1 = 1234; + public static double s_d2 = 0.1234; + public static double s_d3 = 1234.0; + public static double s_d4 = 1234.1234; + public static double s_d5 = -1234; + public static double s_d6 = -0.1234; + public static double s_d7 = -1234.0; + public static double s_d8 = -1234.1234; + public static double s_d9 = 0x7f7fffff; + public static double s_d10 = -0x7f7fffff; + public static double s_d11 = 0xff7fffff; + public static double s_d12 = -0xff7fffff; + public static double s_d13 = 0xffefffffffffffffL; + public static double s_d14 = -0xffefffffffffffffL; + public static double s_d15 = 0x7fefffffffffffffL; + public static double s_d16 = -0x7fefffffffffffffL; + public double d_d0 = 0; + public double d_d1 = 1234; + public double d_d2 = 0.1234; + public double d_d3 = 1234.0; + public double d_d4 = 1234.1234; + public double d_d5 = -1234; + public double d_d6 = -0.1234; + public double d_d7 = -1234.0; + public double d_d8 = -1234.1234; + public double d_d9 = 0x7f7fffff; + public double d_d10 = -0x7f7fffff; + public double d_d11 = 0xff7fffff; + public double d_d12 = -0xff7fffff; + public double d_d13 = 0xffefffffffffffffL; + public double d_d14 = -0xffefffffffffffffL; + public double d_d15 = 0x7fefffffffffffffL; + public double d_d16 = -0x7fefffffffffffffL; + public static double[] s_a_d = { + 0, + 1234, + 0.1234, + 1234.0, + 1234.1234, + -1234, + -0.1234, + -1234.0, + -1234.1234, + 0x7f7fffff, + -0x7f7fffff, + 0xff7fffff, + -0xff7fffff, + 0xffefffffffffffffL, + -0xffefffffffffffffL, + 0x7fefffffffffffffL, + -0x7fefffffffffffffL, + }; + public double[] d_a_d = { + 0, + 1234, + 0.1234, + 1234.0, + 1234.1234, + -1234, + -0.1234, + -1234.0, + -1234.1234, + 0x7f7fffff, + -0x7f7fffff, + 0xff7fffff, + -0xff7fffff, + 0xffefffffffffffffL, + -0xffefffffffffffffL, + 0x7fefffffffffffffL, + -0x7fefffffffffffffL, + }; + public static double[][] s_ma_d = { + { + 0, + 1234, + 0.1234, + 1234.0, + 1234.1234, + -1234, + 0xffefffffffffffffL, + -0xffefffffffffffffL, + }, + { + -0.1234, + -1234.0, + -1234.1234, + 0x7f7fffff, + -0x7f7fffff, + 0xff7fffff, + -0xff7fffff, + 0x7fefffffffffffffL, + -0x7fefffffffffffffL, + }, + }; + public double[][] d_ma_d = { + { + 0, + 1234, + 0.1234, + 1234.0, + 1234.1234, + -1234, + 0xffefffffffffffffL, + -0xffefffffffffffffL, + }, + { + -0.1234, + -1234.0, + -1234.1234, + 0x7f7fffff, + -0x7f7fffff, + 0xff7fffff, + -0xff7fffff, + 0x7fefffffffffffffL, + -0x7fefffffffffffffL, + }, + }; + + // NaN values + public static double s_nd0 = 0x7f800001; + public static double s_nd1 = 0x7f8ffff1; + public static double s_nd2 = 0xff800001; + public static double s_nd3 = 0xff8ffff1; + public static double s_nd4 = 0xffffffff; + public static double s_nd5 = 0x7ff0000000000001L; + public static double s_nd6 = 0x7ffffffffffffff1L; + public static double s_nd7 = 0x7fffffffffffffffL; + public static double s_nd8 = 0xfff0000000000001L; + public static double s_nd9 = 0xfffffffffffffff1L; + public static double s_nd10 = 0xffffffffffffffffL; + + public double d_nd0 = 0x7f800001; + public double d_nd1 = 0x7f8ffff1; + public double d_nd2 = 0xff800001; + public double d_nd3 = 0xff8ffff1; + public double d_nd4 = 0xffffffff; + public double d_nd5 = 0x7ff0000000000001L; + public double d_nd6 = 0x7ffffffffffffff1L; + public double d_nd7 = 0x7fffffffffffffffL; + public double d_nd8 = 0xfff0000000000001L; + public double d_nd9 = 0xfffffffffffffff1L; + public double d_nd10 = 0xffffffffffffffffL; + + public static double[] s_a_nd = { + 0x7f800001, + 0x7f8ffff1, + 0xff800001, + 0xff8ffff1, + 0xffffffff, + 0x7ff0000000000001L, + 0x7ffffffffffffff1L, + 0x7fffffffffffffffL, + 0xfff0000000000001L, + 0xfffffffffffffff1L, + 0xffffffffffffffffL, + }; + public double[] d_a_nd = { + 0x7f800001, + 0x7f8ffff1, + 0xff800001, + 0xff8ffff1, + 0xffffffff, + 0x7ff0000000000001L, + 0x7ffffffffffffff1L, + 0x7fffffffffffffffL, + 0xfff0000000000001L, + 0xfffffffffffffff1L, + 0xffffffffffffffffL, + }; + public static double[][] s_ma_nd = { + { + 0x7f800001, + 0x7f8ffff1, + 0xff800001, + 0x7ff0000000000001L, + 0x7ffffffffffffff1L, + 0x7fffffffffffffffL, + }, + { + 0xff8ffff1, + 0xffffffff, + 0xfff0000000000001L, + 0xfffffffffffffff1L, + 0xffffffffffffffffL, + }, + }; + public double[][] d_ma_nd = { + { + 0x7f800001, + 0x7f8ffff1, + 0xff800001, + 0x7ff0000000000001L, + 0x7ffffffffffffff1L, + 0x7fffffffffffffffL, + }, + { + 0xff8ffff1, + 0xffffffff, + 0xfff0000000000001L, + 0xfffffffffffffff1L, + 0xffffffffffffffffL, + }, + }; + + + // Indinity values (Positive) + public static double s_ipd0 = 0x7f800000; + public static double s_ipd1 = 0x7ff0000000000000L; + public double d_ipd0 = 0x7f800000; + public double d_ipd1 = 0x7ff0000000000000L; + public static double[] s_a_ipd = { + 0x7f800000, + 0x7f800000, + 0x7ff0000000000000L, + 0x7ff0000000000000L, + }; + public double[] d_a_ipd = { + 0x7f800000, + 0x7f800000, + 0x7ff0000000000000L, + 0x7ff0000000000000L, + }; + public static double[][] s_ma_ipd = { + { + 0x7f800000, + 0x7ff0000000000000L, + }, + { + 0x7f800000, + 0x7ff0000000000000L, + }, + }; + public double[][] d_ma_ipd = { + { + 0x7f800000, + 0x7ff0000000000000L, + }, + { + 0x7f800000, + 0x7ff0000000000000L, + }, + }; + + // Infinity values (Negative) + public static double s_ind0 = 0xff800000; + public static double s_ind1 = 0xfff0000000000000L; + public double d_ind0 = 0xff800000; + public double d_ind1 = 0xfff0000000000000L; + public static double[] s_a_ind = { + 0x7f800000, + 0xff800000, + 0xfff0000000000000L, + }; + public double[] d_a_ind = { + 0x7f800000, + 0xff800000, + 0xfff0000000000000L, + }; + public static double[][] s_ma_ind = { + { + 0x7f800000, + 0xff800000, + 0xfff0000000000000L, + }, + { + 0x7f800000, + 0xff800000, + 0xfff0000000000000L, + } + }; + public double[][] d_ma_ind = { + { + 0x7f800000, + 0xff800000, + 0xfff0000000000000L, + }, + { + 0x7f800000, + 0xff800000, + 0xfff0000000000000L, + } + }; +} diff --git a/tests/Cases/fixtures/java/BoundaryValueTypeForFloatTest.java b/tests/Cases/fixtures/java/BoundaryValueTypeForFloatTest.java new file mode 100644 index 00000000..00924500 --- /dev/null +++ b/tests/Cases/fixtures/java/BoundaryValueTypeForFloatTest.java @@ -0,0 +1,207 @@ +class BoundaryValueTypeForFloatTest +{ + // boundary value tests for Floats + public static float s_f0 = 0; + public static float s_f1 = 1234F; + public static float s_f2 = 0.1234F; + public static float s_f3 = 1234.0F; + public static float s_f4 = 1234.1234F; + public static float s_f5 = -1234F; + public static float s_f6 = -0.1234F; + public static float s_f7 = -1234.0F; + public static float s_f8 = -1234.1234F; + public static float s_f9 = 0x7f7fffff; + public static float s_f10 = -0x7f7fffff; + public static float s_f11 = 0xff7fffff; + public static float s_f12 = -0xff7fffff; + public float d_f0 = 0; + public float d_f1 = 1234F; + public float d_f2 = 0.1234F; + public float d_f3 = 1234.0F; + public float d_f4 = 1234.1234F; + public float d_f5 = -1234F; + public float d_f6 = -0.1234F; + public float d_f7 = -1234.0F; + public float d_f8 = -1234.1234F; + public float d_f9 = 0x7f7fffff; + public float d_f10 = -0x7f7fffff; + public float d_f11 = 0xff7fffff; + public float d_f12 = -0xff7fffff; + public static float[] s_a_f = { + 0, + 1234F, + 0.1234F, + 1234.0F, + 1234.1234F, + -1234F, + -0.1234F, + -1234.0F, + -1234.1234F, + 0x7f7fffff, + -0x7f7fffff, + 0xff7fffff, + -0xff7fffff, + }; + public float[] d_a_f = { + 0, + 1234F, + 0.1234F, + 1234.0F, + 1234.1234F, + -1234F, + -0.1234F, + -1234.0F, + -1234.1234F, + 0x7f7fffff, + -0x7f7fffff, + 0xff7fffff, + -0xff7fffff, + }; + public static float[][] s_ma_f = { + { + 0, + 1234F, + 0.1234F, + 1234.0F, + 1234.1234F, + -1234F, + }, + { + -0.1234F, + -1234.0F, + -1234.1234F, + 0x7f7fffff, + -0x7f7fffff, + 0xff7fffff, + -0xff7fffff, + }, + }; + public float[][] d_ma_f = { + { + 0, + 1234F, + 0.1234F, + 1234.0F, + 1234.1234F, + -1234F, + }, + { + -0.1234F, + -1234.0F, + -1234.1234F, + 0x7f7fffff, + -0x7f7fffff, + 0xff7fffff, + -0xff7fffff, + }, + }; + + // NaN values + public static float s_nf0 = 0x7f800001; + public static float s_nf1 = 0x7f8ffff1; + public static float s_nf2 = 0xff800001; + public static float s_nf3 = 0xff8ffff1; + public static float s_nf4 = 0xffffffff; + public float d_nf0 = 0x7f800001; + public float d_nf1 = 0x7f8ffff1; + public float d_nf2 = 0xff800001; + public float d_nf3 = 0xff8ffff1; + public float d_nf4 = 0xffffffff; + public static float[] s_a_nf = { + 0x7f800001, + 0x7f8ffff1, + 0xff800001, + 0xff8ffff1, + 0xffffffff, + }; + public float[] d_a_nf = { + 0x7f800001, + 0x7f8ffff1, + 0xff800001, + 0xff8ffff1, + 0xffffffff, + }; + public static float[][] s_ma_nf = { + { + 0x7f800001, + 0x7f8ffff1, + 0xff800001, + }, + { + 0xff8ffff1, + 0xffffffff, + }, + }; + public float[][] d_ma_nf = { + { + 0x7f800001, + 0x7f8ffff1, + 0xff800001, + }, + { + 0xff8ffff1, + 0xffffffff, + }, + }; + + + // Infinity values (Positive) + public static float s_ipf0 = 0x7f800000; + public float d_ipf0 = 0x7f800000; + public static float[] s_a_ipf = { + 0x7f800000, + 0x7f800000, + }; + public float[] d_a_ipf = { + 0x7f800000, + 0x7f800000, + }; + public static float[][] s_ma_ipf = { + { + 0x7f800000, + }, + { + 0x7f800000, + }, + }; + public float[][] d_ma_ipf = { + { + 0x7f800000, + }, + { + 0x7f800000, + }, + }; + + // Infinity values (Negative) + public static float s_inf0 = 0xff800000; + public float d_inf0 = 0xff800000; + public static float[] s_a_inf = { + 0x7f800000, + 0xff800000, + }; + public float[] d_a_inf = { + 0x7f800000, + 0xff800000, + }; + public static float[][] s_ma_inf = { + { + 0x7f800000, + 0xff800000, + }, + { + 0x7f800000, + 0xff800000, + }, + }; + public float[][] d_ma_inf = { + { + 0x7f800000, + 0xff800000, + }, + { + 0x7f800000, + 0xff800000, + }, + }; +} diff --git a/tests/Cases/fixtures/java/BoundaryValueTypeForIntTest.java b/tests/Cases/fixtures/java/BoundaryValueTypeForIntTest.java new file mode 100644 index 00000000..f133969d --- /dev/null +++ b/tests/Cases/fixtures/java/BoundaryValueTypeForIntTest.java @@ -0,0 +1,74 @@ +class BoundaryValueTypeForIntTest +{ + // boundary value tests for Integers + public static int s_i0 = 1234; + public static int s_i1 = 32767; + public static int s_i2 = 32768; + public static int s_i3 = 2147483647; + public static int s_i4 = -1; + public static int s_i5 = -1234; + public static int s_i6 = -2147483648; + public static int s_i7 = -32768; + public static int s_i8 = 0; + public int d_i0 = 1234; + public int d_i1 = 32767; + public int d_i2 = 32768; + public int d_i3 = 2147483647; + public int d_i4 = -1; + public int d_i5 = -1234; + public int d_i6 = -2147483648; + public int d_i7 = -32768; + public int d_i8 = 0; + public static int[] s_a_i = { + 1234, + 32767, + 32768, + 2147483647, + -1, + -1234, + -2147483648, + -32768, + 0, + }; + public int[] d_a_i = { + 1234, + 32767, + 32768, + 2147483647, + -1, + -1234, + -2147483648, + -32768, + 0, + }; + public static int[][] s_ma_i = { + { + 1234, + 32767, + 32768, + 2147483647, + }, + { + -1, + -1234, + -2147483648, + -32768, + 0, + }, + }; + public int[][] d_ma_i = { + { + 1234, + 32767, + 32768, + 2147483647, + }, + { + -1, + -1234, + -2147483648, + -32768, + 0, + }, + }; +} diff --git a/tests/Cases/fixtures/java/BoundaryValueTypeForLongTest.java b/tests/Cases/fixtures/java/BoundaryValueTypeForLongTest.java new file mode 100644 index 00000000..a11fc768 --- /dev/null +++ b/tests/Cases/fixtures/java/BoundaryValueTypeForLongTest.java @@ -0,0 +1,86 @@ +class BoundaryValueTypeForLongTest +{ + // boundary value tests for Longs + public static long s_l0 = 1234L; + public static long s_l1 = 32767L; + public static long s_l2 = 32768L; + public static long s_l3 = 2147483647L; + public static long s_l4 = -1L; + public static long s_l5 = -1234L; + public static long s_l6 = -2147483648L; + public static long s_l7 = -32768L; + public static long s_l8 = -9223372036854775808L; + public static long s_l9 = 9223372036854775807L; + public static long s_l10 = 0L; + public long d_l0 = 1234L; + public long d_l1 = 32767L; + public long d_l2 = 32768L; + public long d_l3 = 2147483647L; + public long d_l4 = -1L; + public long d_l5 = -1234L; + public long d_l6 = -2147483648L; + public long d_l7 = -32768L; + public long d_l8 = -9223372036854775808L; + public long d_l9 = 9223372036854775807L; + public long d_l10 = 0L; + public static long[] s_a_l = { + 1234L, + 32767L, + 32768L, + 2147483647L, + -1L, + -1234L, + -2147483648L, + -32768L, + -9223372036854775808L, + 9223372036854775807L, + 0L, + }; + public long[] d_a_l = { + 1234L, + 32767L, + 32768L, + 2147483647L, + -1L, + -1234L, + -2147483648L, + -32768L, + -9223372036854775808L, + 9223372036854775807L, + 0L, + }; + public static long[][] s_ma_l = { + { + 1234L, + 32767L, + 32768L, + 2147483647L, + -1L, + -1234L, + }, + { + -2147483648L, + -32768L, + -9223372036854775808L, + 9223372036854775807L, + 0L, + }, + }; + public long[][] d_ma_l = { + { + 1234L, + 32767L, + 32768L, + 2147483647L, + -1L, + -1234L, + }, + { + -2147483648L, + -32768L, + -9223372036854775808L, + 9223372036854775807L, + 0L, + }, + }; +} diff --git a/tests/Cases/fixtures/java/BoundaryValueTypeForShortTest.java b/tests/Cases/fixtures/java/BoundaryValueTypeForShortTest.java new file mode 100644 index 00000000..caf90c68 --- /dev/null +++ b/tests/Cases/fixtures/java/BoundaryValueTypeForShortTest.java @@ -0,0 +1,56 @@ +class BoundaryValueTypeForShortTest +{ + // boundary value tests for Shorts + public static short s_s0 = 1234; + public static short s_s1 = 32767; + public static short s_s2 = -1; + public static short s_s3 = -1234; + public static short s_s4 = -32768; + public static short s_s5 = 0; + public short d_s0 = 1234; + public short d_s1 = 32767; + public short d_s2 = -1; + public short d_s3 = -1234; + public short d_s4 = -32768; + public short d_s5 = 0; + public static short[] s_a_s = { + 1234, + 32767, + -1, + -1234, + -32768, + 0, + }; + public short[] d_a_s = { + 1234, + 32767, + -1, + -1234, + -32768, + 0, + }; + public static short[][] s_ma_s = { + { + 1234, + 32767, + -1, + }, + { + -1234, + -32768, + 0, + }, + }; + public short[][] d_ma_s = { + { + 1234, + 32767, + -1, + }, + { + -1234, + -32768, + 0, + }, + }; +} diff --git a/tests/Cases/fixtures/java/BranchIfTest.java b/tests/Cases/fixtures/java/BranchIfTest.java new file mode 100644 index 00000000..47087647 --- /dev/null +++ b/tests/Cases/fixtures/java/BranchIfTest.java @@ -0,0 +1,57 @@ +class BranchIfTest +{ + public static int ifAcmpEq(String value1, String value2) + { + return value1 != value2 ? 0 : 1; + } + + public static int ifAcmpNe(String value1, String value2) + { + return value1 == value2 ? 0 : 1; + } + + public static int ifIcmpEq(int value1, int value2) + { + return value1 != value2 ? 0 : 1; + } + + public static int ifIcmpNe(int value1, int value2) + { + return value1 == value2 ? 0 : 1; + } + + public static int ifIcmpLt(int value1, int value2) + { + return value1 >= value2 ? 0 : 1; + } + + public static int ifIcmpGe(int value1, int value2) + { + return value1 < value2 ? 0 : 1; + } + + public static int ifIcmpGt(int value1, int value2) + { + return value1 <= value2 ? 0 : 1; + } + + public static int ifIcmpLe(int value1, int value2) + { + return value1 > value2 ? 0 : 1; + } + + public static int ifLcmpGraterThan(long value1, long value2) + { + return value1 > value2 ? 0 : 1; + } + + public static int ifLcmpLessThan(long value1, long value2) + { + return value1 < value2 ? 0 : 1; + } + + public static int ifLcmpEqualsTo(long value1, long value2) + { + return value1 == value2 ? 0 : 1; + } +} diff --git a/tests/Cases/fixtures/java/BubbleSortTest.java b/tests/Cases/fixtures/java/BubbleSortTest.java new file mode 100644 index 00000000..ec0dbb02 --- /dev/null +++ b/tests/Cases/fixtures/java/BubbleSortTest.java @@ -0,0 +1,74 @@ +class BubbleSortTest +{ + public static void asc() + { + int[] array = {-14, 5, 111, 44, 2, 9999, 99, 123, 1, -10, 33, -50}; + for (int i = 0; i < array.length; i++) { + for (int j = 0; j < array.length; j++) { + if (array[i] < array[j]) { + int tmp = array[i]; + array[i] = array[j]; + array[j] = tmp; + } + } + } + + for (int i = 0; i < array.length; i++) { + System.out.println(array[i]); + } + } + + + public static void desc() + { + int[] array = {-14, 5, 111, 44, 2, 9999, 99, 123, 1, -10, 33, -50}; + for (int i = 0; i < array.length; i++) { + for (int j = 0; j < array.length; j++) { + if (array[i] > array[j]) { + int tmp = array[i]; + array[i] = array[j]; + array[j] = tmp; + } + } + } + + for (int i = 0; i < array.length; i++) { + System.out.println(array[i]); + } + } + + public static void ascByParam(int[] array) + { + for (int i = 0; i < array.length; i++) { + for (int j = 0; j < array.length; j++) { + if (array[i] < array[j]) { + int tmp = array[i]; + array[i] = array[j]; + array[j] = tmp; + } + } + } + + for (int i = 0; i < array.length; i++) { + System.out.println(array[i]); + } + } + + + public static void descByParam(int[] array) + { + for (int i = 0; i < array.length; i++) { + for (int j = 0; j < array.length; j++) { + if (array[i] > array[j]) { + int tmp = array[i]; + array[i] = array[j]; + array[j] = tmp; + } + } + } + + for (int i = 0; i < array.length; i++) { + System.out.println(array[i]); + } + } +} \ No newline at end of file diff --git a/tests/Cases/fixtures/java/CallToAmbiguousMethodsTest.java b/tests/Cases/fixtures/java/CallToAmbiguousMethodsTest.java new file mode 100644 index 00000000..fdcd04c5 --- /dev/null +++ b/tests/Cases/fixtures/java/CallToAmbiguousMethodsTest.java @@ -0,0 +1,22 @@ +class CallToAmbiguousMethodsTest +{ + public static void charMethod(char n) + { + System.out.println(n); + } + + public static void byteMethod(byte n) + { + System.out.println(n); + } + + public static void longMethod(long n) + { + System.out.println(n); + } + + public static void doubleMethod(double n) + { + System.out.println(n); + } +} diff --git a/tests/Cases/fixtures/java/CalleeEnclosingMethodInJarTest.java b/tests/Cases/fixtures/java/CalleeEnclosingMethodInJarTest.java new file mode 100644 index 00000000..ec1c8ef3 --- /dev/null +++ b/tests/Cases/fixtures/java/CalleeEnclosingMethodInJarTest.java @@ -0,0 +1,9 @@ +class CalleeEnclosingMethodInJarTest +{ + public String text; + + public void callHelloWorld() + { + System.out.println(this.text); + } +} diff --git a/tests/Cases/fixtures/java/CallerEnclosingMethodInJarTest.java b/tests/Cases/fixtures/java/CallerEnclosingMethodInJarTest.java new file mode 100644 index 00000000..c80cc3be --- /dev/null +++ b/tests/Cases/fixtures/java/CallerEnclosingMethodInJarTest.java @@ -0,0 +1,10 @@ +class CallerEnclosingMethodInJarTest +{ + public static void main(String[] args) + { + new CalleeEnclosingMethodInJarTest() {{ + this.text = "Hello World!"; + callHelloWorld(); + }}; + } +} diff --git a/tests/Cases/fixtures/java/CastTest.java b/tests/Cases/fixtures/java/CastTest.java new file mode 100644 index 00000000..88b16799 --- /dev/null +++ b/tests/Cases/fixtures/java/CastTest.java @@ -0,0 +1,79 @@ +class CastTest +{ + /** Integers */ + public static short testIntToShort(int value) + { + return (short) value; + } + + public static double testIntToDouble(int value) + { + return (double) value; + } + + public static float testIntToFloat(int value) + { + return (float) value; + } + + public static byte testIntToByte(int value) + { + return (byte) value; + } + + public static char testIntToChar(int value) + { + return (char) value; + } + + /** Longs */ + + public static double testLongToDouble(long value) + { + return (double) value; + } + + public static float testLongToFloat(long value) + { + return (float) value; + } + + public static int testLongToInt(long value) + { + return (int) value; + } + + /** Doubles */ + + public static float testDoubleToFloat(double value) + { + return (float) value; + } + + public static int testDoubleToInt(double value) + { + return (int) value; + } + + public static long testDoubleToLong(double value) + { + return (long) value; + } + + /** Floats */ + + public static double testFloatToDouble(float value) + { + return (double) value; + } + + public static int testFloatToInt(float value) + { + return (int) value; + } + + public static long testFloatToLong(float value) + { + return (long) value; + } +} diff --git a/tests/Cases/fixtures/java/ConstructTest.java b/tests/Cases/fixtures/java/ConstructTest.java new file mode 100644 index 00000000..4c01f6e1 --- /dev/null +++ b/tests/Cases/fixtures/java/ConstructTest.java @@ -0,0 +1,4 @@ +class ConstructTest +{ + public String text = "Default Text"; +} diff --git a/tests/Cases/fixtures/java/ConstructorNoParameterTest.java b/tests/Cases/fixtures/java/ConstructorNoParameterTest.java new file mode 100644 index 00000000..351fbf7e --- /dev/null +++ b/tests/Cases/fixtures/java/ConstructorNoParameterTest.java @@ -0,0 +1,17 @@ +class ConstructorNoParameterTest +{ + public ConstructorNoParameterTest() + { + System.out.println("Hello World!"); + } + + public void entrypoint() + { + System.out.println("Entrypoint"); + } + + public static void main(String[] args) + { + new ConstructorNoParameterTest(); + } +} \ No newline at end of file diff --git a/tests/Cases/fixtures/java/ConstructorWithParametersTest.java b/tests/Cases/fixtures/java/ConstructorWithParametersTest.java new file mode 100644 index 00000000..ca86f0ce --- /dev/null +++ b/tests/Cases/fixtures/java/ConstructorWithParametersTest.java @@ -0,0 +1,17 @@ +class ConstructorWithParametersTest +{ + public ConstructorWithParametersTest(String p) + { + System.out.println(p); + } + + public void entrypoint() + { + System.out.println("Entrypoint"); + } + + public static void main(String[] args) + { + new ConstructorWithParametersTest("Hello World!"); + } +} \ No newline at end of file diff --git a/tests/Cases/fixtures/java/DefaultInterfaceCallsInterfaceMethodTest.java b/tests/Cases/fixtures/java/DefaultInterfaceCallsInterfaceMethodTest.java new file mode 100644 index 00000000..1f2d131b --- /dev/null +++ b/tests/Cases/fixtures/java/DefaultInterfaceCallsInterfaceMethodTest.java @@ -0,0 +1,22 @@ +interface DefaultInterfaceCallsInterfaceMethodTest +{ + public abstract void callee(); + + default void callDefaultMethod(DefaultInterfaceCallsInterfaceMethodTest dicimt) + { + dicimt.callee(); + } + + public static void main(String[] args) + { + DefaultInterfaceCallsInterfaceMethodTest dicimt = new DefaultInterfaceCallsInterfaceMethodTest() { + @java.lang.Override + public void callee() + { + System.out.println("Hello World!"); + } + }; + + dicimt.callDefaultMethod(dicimt); + } +} diff --git a/tests/Cases/fixtures/java/DefaultInterfaceGetCalledByMethodLookupTest.java b/tests/Cases/fixtures/java/DefaultInterfaceGetCalledByMethodLookupTest.java new file mode 100644 index 00000000..777680a7 --- /dev/null +++ b/tests/Cases/fixtures/java/DefaultInterfaceGetCalledByMethodLookupTest.java @@ -0,0 +1,23 @@ +import java.lang.invoke.*; + +interface DefaultInterfaceGetCalledByMethodLookupTest +{ + public abstract void test(); + + public static void main(String[] args) throws Throwable + { + DefaultInterfaceGetCalledByMethodLookupTest digcbmlt = new DefaultInterfaceGetCalledByMethodLookupTest() { + @java.lang.Override + public void test() { + System.out.println("Hello World!"); + } + }; + + MethodHandle mh = MethodHandles.lookup().findVirtual( + DefaultInterfaceGetCalledByMethodLookupTest.class, + "test", + MethodType.methodType(void.class) + ); + mh.invokeExact(digcbmlt); + } +} diff --git a/tests/Cases/fixtures/java/DefaultSyntaxInInterfaceTest.java b/tests/Cases/fixtures/java/DefaultSyntaxInInterfaceTest.java new file mode 100644 index 00000000..fbdd8e17 --- /dev/null +++ b/tests/Cases/fixtures/java/DefaultSyntaxInInterfaceTest.java @@ -0,0 +1,18 @@ +class DefaultSyntaxInInterfaceTest +{ + public interface DefaultSyntaxInInterfaceTestInterface + { + default public void defaultInterfaceMethodTest1() + { + System.out.println("Hello World!"); + } + } + + public static void defaultInterfaceMethodTest1() + { + DefaultSyntaxInInterfaceTestInterface instance = new DefaultSyntaxInInterfaceTestInterface() { + // Does not override. + }; + instance.defaultInterfaceMethodTest1(); + } +} diff --git a/tests/Cases/fixtures/java/DoubleCalculationTest.java b/tests/Cases/fixtures/java/DoubleCalculationTest.java new file mode 100644 index 00000000..e2924ce6 --- /dev/null +++ b/tests/Cases/fixtures/java/DoubleCalculationTest.java @@ -0,0 +1,38 @@ +class DoubleCalculationTest +{ + private static double returnDouble(double n) + { + return n; + } + + public static double doubleAdd(double value1, double value2) + { + return value1 + value2; + } + + public static double doubleSub(double value1, double value2) + { + return value1 - value2; + } + + public static double doubleMul(double value1, double value2) + { + return value1 * value2; + } + + public static double doubleAddFromOtherMethod(double value1, double value2) + { + return returnDouble(value1) + returnDouble(value2); + } + + public static double doubleSubFromOtherMethod(double value1, double value2) + { + return returnDouble(value1) - returnDouble(value2); + } + + public static double doubleMulFromOtherMethod(double value1, double value2) + { + return returnDouble(value1) * returnDouble(value2); + } + +} diff --git a/tests/Cases/fixtures/java/DoubleOfTypesComparingTest.java b/tests/Cases/fixtures/java/DoubleOfTypesComparingTest.java new file mode 100644 index 00000000..771ac215 --- /dev/null +++ b/tests/Cases/fixtures/java/DoubleOfTypesComparingTest.java @@ -0,0 +1,38 @@ +class DoubleOfTypesComparingTest +{ + public static void l(double a) + { + if (a < 0) { + System.out.println("Hello World!"); + return; + } + System.out.println(":thinking_face:"); + } + + public static void le(double a) + { + if (a <= 0) { + System.out.println("Hello World!"); + return; + } + System.out.println(":thinking_face:"); + } + + public static void g(double a) + { + if (a > 0) { + System.out.println("Hello World!"); + return; + } + System.out.println(":thinking_face:"); + } + + public static void ge(double a) + { + if (a >= 0) { + System.out.println("Hello World!"); + return; + } + System.out.println(":thinking_face:"); + } +} \ No newline at end of file diff --git a/tests/Cases/fixtures/java/EnclosingMethodTest.java b/tests/Cases/fixtures/java/EnclosingMethodTest.java new file mode 100644 index 00000000..4293e012 --- /dev/null +++ b/tests/Cases/fixtures/java/EnclosingMethodTest.java @@ -0,0 +1,17 @@ +class EnclosingMethodTest +{ + public String text; + + public static void main(String[] args) + { + new EnclosingMethodTest() {{ + this.text = "Hello World!"; + callHelloWorld(); + }}; + } + + public void callHelloWorld() + { + System.out.println(this.text); + } +} diff --git a/tests/Cases/fixtures/java/ExtendingClassTest1.java b/tests/Cases/fixtures/java/ExtendingClassTest1.java new file mode 100644 index 00000000..81789523 --- /dev/null +++ b/tests/Cases/fixtures/java/ExtendingClassTest1.java @@ -0,0 +1,10 @@ +class ExtendingClassTest1 extends SuperExtendingClassTest +{ + public static void main(String[] args) + { + ExtendingClassTest1 test = new ExtendingClassTest1(); + + // Expected 99999 from SuperExtendingClassTest + System.out.println(test.value); + } +} \ No newline at end of file diff --git a/tests/Cases/fixtures/java/ExtendingClassTest2.java b/tests/Cases/fixtures/java/ExtendingClassTest2.java new file mode 100644 index 00000000..da966acd --- /dev/null +++ b/tests/Cases/fixtures/java/ExtendingClassTest2.java @@ -0,0 +1,12 @@ +class ExtendingClassTest2 extends SuperExtendingClassTest +{ + protected int value = 12345; + + public static void main(String[] args) + { + ExtendingClassTest2 test = new ExtendingClassTest2(); + + // Expected 12345 + System.out.println(test.value); + } +} \ No newline at end of file diff --git a/tests/Cases/fixtures/java/FizzBuzzTest.java b/tests/Cases/fixtures/java/FizzBuzzTest.java new file mode 100644 index 00000000..e8f64f93 --- /dev/null +++ b/tests/Cases/fixtures/java/FizzBuzzTest.java @@ -0,0 +1,19 @@ +class FizzBuzzTest +{ + public static void main(String[] args) + { + for (int i = 1; i <= Integer.parseInt(args[0]); i++) { + StringBuilder text = new StringBuilder(); + if ((i % 3) == 0) { + text.append("Fizz"); + } + if ((i % 5) == 0) { + text.append("Buzz"); + } + if (text.toString().isEmpty()) { + text.append(i); + } + System.out.println(text); + } + } +} \ No newline at end of file diff --git a/tests/Cases/fixtures/java/FloatCalculationTest.java b/tests/Cases/fixtures/java/FloatCalculationTest.java new file mode 100644 index 00000000..af65bc3b --- /dev/null +++ b/tests/Cases/fixtures/java/FloatCalculationTest.java @@ -0,0 +1,38 @@ +class FloatCalculationTest +{ + private static float returnFloat(float n) + { + return n; + } + + public static float floatAdd(float value1, float value2) + { + return value1 + value2; + } + + public static float floatSub(float value1, float value2) + { + return value1 - value2; + } + + public static float floatMul(float value1, float value2) + { + return value1 * value2; + } + + public static float floatAddFromOtherMethod(float value1, float value2) + { + return returnFloat(value1) + returnFloat(value2); + } + + public static float floatSubFromOtherMethod(float value1, float value2) + { + return returnFloat(value1) - returnFloat(value2); + } + + public static float floatMulFromOtherMethod(float value1, float value2) + { + return returnFloat(value1) * returnFloat(value2); + } + +} diff --git a/tests/Cases/fixtures/java/FloatOfTypesComparingTest.java b/tests/Cases/fixtures/java/FloatOfTypesComparingTest.java new file mode 100644 index 00000000..2d1bb982 --- /dev/null +++ b/tests/Cases/fixtures/java/FloatOfTypesComparingTest.java @@ -0,0 +1,38 @@ +class FloatOfTypesComparingTest +{ + public static void l(float a) + { + if (a < 0) { + System.out.println("Hello World!"); + return; + } + System.out.println(":thinking_face:"); + } + + public static void le(float a) + { + if (a <= 0) { + System.out.println("Hello World!"); + return; + } + System.out.println(":thinking_face:"); + } + + public static void g(float a) + { + if (a > 0) { + System.out.println("Hello World!"); + return; + } + System.out.println(":thinking_face:"); + } + + public static void ge(float a) + { + if (a >= 0) { + System.out.println("Hello World!"); + return; + } + System.out.println(":thinking_face:"); + } +} \ No newline at end of file diff --git a/tests/Cases/fixtures/java/GetFieldTest.java b/tests/Cases/fixtures/java/GetFieldTest.java new file mode 100644 index 00000000..cdb930c3 --- /dev/null +++ b/tests/Cases/fixtures/java/GetFieldTest.java @@ -0,0 +1,9 @@ +class GetFieldTest +{ + public int value = 1; + + public static int getField() { + GetFieldTest instance = new GetFieldTest(); + return instance.value; + } +} diff --git a/tests/Cases/fixtures/java/InnerClassTest.java b/tests/Cases/fixtures/java/InnerClassTest.java new file mode 100644 index 00000000..4a7f0139 --- /dev/null +++ b/tests/Cases/fixtures/java/InnerClassTest.java @@ -0,0 +1,20 @@ +class InnerClassTest +{ + public static void main(String[] args) + { + (new InnerClassTest()).callInnerClass(args[0], args[1]); + } + + public void callInnerClass(String a, String b) + { + System.out.print((new InnerClassTestInnerClass()).helloWorld(a, b)); + } + + public class InnerClassTestInnerClass + { + public String helloWorld(String a, String b) + { + return a + " " + b; + } + } +} diff --git a/tests/Cases/fixtures/java/InsertionSortTest.java b/tests/Cases/fixtures/java/InsertionSortTest.java new file mode 100644 index 00000000..6b2f983d --- /dev/null +++ b/tests/Cases/fixtures/java/InsertionSortTest.java @@ -0,0 +1,78 @@ +class InsertionSortTest +{ + public static void asc() + { + int[] array = {-14, 5, 111, 44, 2, 9999, 99, 123, 1, -10, 33, -50}; + for(int i = 1; i < array.length; i++){ + int j = i; + while(j >= 1 && array[j-1] > array[j]) { + int tmp = array[j]; + array[j] = array[j - 1]; + array[j - 1] = tmp; + j--; + + } + } + + for (int i = 0; i < array.length; i++) { + System.out.println(array[i]); + } + } + + + public static void desc() + { + int[] array = {-14, 5, 111, 44, 2, 9999, 99, 123, 1, -10, 33, -50}; + for(int i = 1; i < array.length; i++){ + int j = i; + while(j >= 1 && array[j - 1] < array[j]) { + int tmp = array[j]; + array[j] = array[j - 1]; + array[j - 1] = tmp; + j--; + + } + } + + for (int i = 0; i < array.length; i++) { + System.out.println(array[i]); + } + } + + public static void ascByParam(int[] array) + { + for(int i = 1; i < array.length; i++) { + int j = i; + while(j >= 1 && array[j - 1] > array[j]) { + int tmp = array[j]; + array[j] = array[j - 1]; + array[j - 1] = tmp; + j--; + + } + } + + for (int i = 0; i < array.length; i++) { + System.out.println(array[i]); + } + } + + + public static void descByParam(int[] array) + { + for(int i = 1; i < array.length; i++){ + int j = i; + while(j >= 1 && array[j - 1] < array[j]) { + int tmp = array[j]; + array[j] = array[j - 1]; + array[j - 1] = tmp; + j--; + + } + } + + for (int i = 0; i < array.length; i++) { + System.out.println(array[i]); + } + } +} \ No newline at end of file diff --git a/tests/Cases/fixtures/java/InstanceOfTest.java b/tests/Cases/fixtures/java/InstanceOfTest.java new file mode 100644 index 00000000..9aa36f0f --- /dev/null +++ b/tests/Cases/fixtures/java/InstanceOfTest.java @@ -0,0 +1,22 @@ +class InstanceOfTest +{ + public static void instanceOfString() + { + String a = "Hello World"; + if (a instanceof String) { + System.out.println(a); + return; + } + System.out.println("Unreachable here."); + } + + public static void instanceOfObject() + { + String a = "Hello World"; + if (a instanceof Object) { + System.out.println(a); + return; + } + System.out.println("Unreachable here."); + } +} diff --git a/tests/Cases/fixtures/java/IntConstTest.java b/tests/Cases/fixtures/java/IntConstTest.java new file mode 100644 index 00000000..a91707a6 --- /dev/null +++ b/tests/Cases/fixtures/java/IntConstTest.java @@ -0,0 +1,42 @@ +class IntConstTest +{ + public static int intConst0() + { + return 0; + } + + public static int intConst1() + { + return 1; + } + + public static int intConst2() + { + return 2; + } + + public static int intConst3() + { + return 3; + } + + public static int intConst4() + { + return 4; + } + + public static int intConst5() + { + return 5; + } + + public static int intConstM1() + { + return -1; + } + + public static int intPush123() + { + return 123; + } +} diff --git a/tests/Cases/fixtures/java/IntegerCalculationTest.java b/tests/Cases/fixtures/java/IntegerCalculationTest.java new file mode 100644 index 00000000..a91d8b35 --- /dev/null +++ b/tests/Cases/fixtures/java/IntegerCalculationTest.java @@ -0,0 +1,38 @@ +class IntegerCalculationTest +{ + private static int returnInt(int n) + { + return n; + } + + public static int intAdd(int value1, int value2) + { + return value1 + value2; + } + + public static int intSub(int value1, int value2) + { + return value1 - value2; + } + + public static int intMul(int value1, int value2) + { + return value1 * value2; + } + + public static int intAddFromOtherMethod(int value1, int value2) + { + return returnInt(value1) + returnInt(value2); + } + + public static int intSubFromOtherMethod(int value1, int value2) + { + return returnInt(value1) - returnInt(value2); + } + + public static int intMulFromOtherMethod(int value1, int value2) + { + return returnInt(value1) * returnInt(value2); + } + +} diff --git a/tests/Cases/fixtures/java/InvokeDynamicTest.java b/tests/Cases/fixtures/java/InvokeDynamicTest.java new file mode 100644 index 00000000..69da4337 --- /dev/null +++ b/tests/Cases/fixtures/java/InvokeDynamicTest.java @@ -0,0 +1,13 @@ +class InvokeDynamicTest +{ + interface InterfaceTest + { + public String callee(String name); + } + + public static void main(String[] args) + { + InterfaceTest it = v -> { return "Hello" + v; }; + System.out.println(it.callee(" World!")); + } +} \ No newline at end of file diff --git a/tests/Cases/fixtures/java/JarCalleeTest.java b/tests/Cases/fixtures/java/JarCalleeTest.java new file mode 100644 index 00000000..382f0d9a --- /dev/null +++ b/tests/Cases/fixtures/java/JarCalleeTest.java @@ -0,0 +1,7 @@ +class JarCalleeTest +{ + public JarCalleeTest(String a) + { + System.out.println(a); + } +} diff --git a/tests/Cases/fixtures/java/JarCallerTest.java b/tests/Cases/fixtures/java/JarCallerTest.java new file mode 100644 index 00000000..85ad5b9b --- /dev/null +++ b/tests/Cases/fixtures/java/JarCallerTest.java @@ -0,0 +1,8 @@ +class JarCallerTest +{ + public static void main(String[] args) + { + new JarCalleeTest("TEST"); + } +} + diff --git a/tests/Cases/fixtures/java/JavaIoPrintStreamClassTest.java b/tests/Cases/fixtures/java/JavaIoPrintStreamClassTest.java new file mode 100644 index 00000000..6fa96296 --- /dev/null +++ b/tests/Cases/fixtures/java/JavaIoPrintStreamClassTest.java @@ -0,0 +1,115 @@ +class JavaIoPrintStreamClassTest +{ + public static void testPrintlnWithoutParams() + { + System.out.println(); + } + + public static void testPrintlnWithStringParams() + { + String x = "Hello World"; + System.out.println(x); + } + + public static void testPrintlnWithNullStringParams() + { + String x = null; + System.out.println(x); + } + + public static void testPrintlnWithCharParams() + { + char x = 'A'; + System.out.println(x); + } + + public static void testPrintlnWithCharArrayParams() + { + char[] x = {'A', 'B', 'C'}; + System.out.println(x); + } + + public static void testPrintlnWithNullCharArrayParams() + { + char[] x = null; + System.out.println(x); + } + + public static void testPrintlnWithFloatParams() + { + float x = (float) 0.123; + System.out.println(x); + } + + public static void testPrintlnWithDoubleParams() + { + double x = 0.123; + System.out.println(x); + } + + public static void testPrintlnWithBooleanParams_true() + { + boolean x = true; + System.out.println(x); + } + + public static void testPrintlnWithBooleanParams_false() + { + boolean x = false; + System.out.println(x); + } + + public static void testPrintWithStringParams() + { + String x = "Hello World"; + System.out.print(x); + } + + public static void testPrintWithNullStringParams() + { + String x = null; + System.out.print(x); + } + + public static void testPrintWithCharParams() + { + char x = 'A'; + System.out.print(x); + } + + public static void testPrintWithCharArrayParams() + { + char[] x = {'A', 'B', 'C'}; + System.out.print(x); + } + + public static void testPrintWithNullCharArrayParams() + { + char[] x = null; + System.out.print(x); + } + + public static void testPrintWithFloatParams() + { + float x = (float) 0.123; + System.out.print(x); + } + + public static void testPrintWithDoubleParams() + { + double x = 0.123; + System.out.print(x); + } + + public static void testPrintWithBooleanParams_true() + { + boolean x = true; + System.out.print(x); + } + + public static void testPrintWithBooleanParams_false() + { + boolean x = false; + System.out.print(x); + } +} diff --git a/tests/Cases/fixtures/java/JavaLangMathTest.java b/tests/Cases/fixtures/java/JavaLangMathTest.java new file mode 100644 index 00000000..d34fec3c --- /dev/null +++ b/tests/Cases/fixtures/java/JavaLangMathTest.java @@ -0,0 +1,176 @@ +class JavaLangMathTest +{ + public static void abs(float a) + { + System.out.println(Math.abs(a)); + } + + public static void acos(float a) + { + System.out.println(Math.acos(a)); + } + + public static void addExact(int a, int b) + { + System.out.println(Math.addExact(a, b)); + } + + public static void asin(float a) + { + System.out.println(Math.asin(a)); + } + + public static void atan(float a) + { + System.out.println(Math.atan(a)); + } + + public static void atan2(float a, float b) + { + System.out.println(Math.atan2(a, b)); + } + + public static void cbrt(float a) + { + System.out.println(Math.cbrt(a)); + } + + public static void ceil(float a) + { + System.out.println(Math.ceil(a)); + } + + public static void copySign(float a, float b) + { + System.out.println(Math.copySign(a, b)); + } + + public static void cos(float a) + { + System.out.println(Math.cos(a)); + } + + public static void cosh(float a) + { + System.out.println(Math.cosh(a)); + } + + public static void decrementExact(int a) + { + System.out.println(Math.decrementExact(a)); + } + + public static void exp(float a) + { + System.out.println(Math.exp(a)); + } + + public static void expm1(float a) + { + System.out.println(Math.expm1(a)); + } + + public static void floor(float a) + { + System.out.println(Math.floor(a)); + } + + // TODO: Java 9 + // public static void floorDiv(int a, int b) + // { + // System.out.println(Math.floorDiv(a, b)); + // } + + // TODO: Java 9 + // public static void floorMod(int a, int b) + // { + // System.out.println(Math.floorMod(a, b)); + // } + + public static void incrementExact(int a) + { + System.out.println(Math.incrementExact(a)); + } + + public static void log(float a) + { + System.out.println(Math.log(a)); + } + + public static void log10(float a) + { + System.out.println(Math.log10(a)); + } + + public static void log1p(float a) + { + System.out.println(Math.log1p(a)); + } + + public static void max(int a, int b) + { + System.out.println(Math.max(a, b)); + } + + public static void min(int a, int b) + { + System.out.println(Math.min(a, b)); + } + + // TODO: Java 9 + // public static void multiplyExact(int a, int b) + // { + // System.out.println(Math.multiplyExact(a, b)); + // } + + // TODO: Java 9 + // public static void multiplyFull(int a, int b) + // { + // System.out.println(Math.multiplyFull(a, b)); + // } + + public static void pow(int a, int b) + { + System.out.println(Math.pow(a, b)); + } + + public static void random() + { + System.out.println(Math.random()); + } + + public static void round(float a) + { + System.out.println(Math.round(a)); + } + + public static void sin(float a) + { + System.out.println(Math.sin(a)); + } + + public static void sinh(float a) + { + System.out.println(Math.sinh(a)); + } + + public static void sqrt(float a) + { + System.out.println(Math.sqrt(a)); + } + + public static void subtractExact(int a, int b) + { + System.out.println(Math.subtractExact(a, b)); + } + + public static void tan(float a) + { + System.out.println(Math.tan(a)); + } + + public static void tanh(float a) + { + System.out.println(Math.tanh(a)); + } +} \ No newline at end of file diff --git a/tests/Cases/fixtures/java/JavaLangStringTest.java b/tests/Cases/fixtures/java/JavaLangStringTest.java new file mode 100644 index 00000000..2988f49c --- /dev/null +++ b/tests/Cases/fixtures/java/JavaLangStringTest.java @@ -0,0 +1,70 @@ +class JavaLangStringTest +{ + public static void charAtIndex(String a, int b) + { + System.out.print(a.charAt(b)); + } + + public static void concat(String a, String b) + { + System.out.print(a.concat(b)); + } + + public static void testHashCode() + { + System.out.println("hello, world".hashCode()); + System.out.println("HELLO, WORLD".toLowerCase().hashCode()); + System.out.println((new String("hello, world")).hashCode()); + } + + public static void intern() + { + String te = "te"; + String st = "st"; + + String test = te + st; + System.out.println(System.identityHashCode(test)); + + test.intern(); + System.out.println(System.identityHashCode("test")); + } + + public static void notInterned() + { + String te = "te"; + String st = "st"; + + String test = te + st; + System.out.println(System.identityHashCode(test)); + + System.out.println(System.identityHashCode("test")); + } + + public static void notInternedAfterLiteral() + { + String te = "te"; + String st = "st"; + + String test = te + st; + System.out.println(System.identityHashCode(test)); + System.out.println(System.identityHashCode("test")); + + test.intern(); + System.out.println(System.identityHashCode("test")); + } + + public static void replace(String a, String b, String c) + { + System.out.print(a.replace(b, c)); + } + + public static void toLowerCase(String a) + { + System.out.print(a.toLowerCase()); + } + + public static void toUpperCase(String a) + { + System.out.print(a.toUpperCase()); + } +} \ No newline at end of file diff --git a/tests/Cases/fixtures/java/JavaLangSystemTest.java b/tests/Cases/fixtures/java/JavaLangSystemTest.java new file mode 100644 index 00000000..360a4780 --- /dev/null +++ b/tests/Cases/fixtures/java/JavaLangSystemTest.java @@ -0,0 +1,8 @@ +class JavaLangSystemTest +{ + public static void identityHashCode() + { + System.out.println(System.identityHashCode("Hello, World")); + System.out.println(System.identityHashCode(new String("Hello, World"))); + } +} diff --git a/tests/Cases/fixtures/java/JavaUtilObjectsTest.java b/tests/Cases/fixtures/java/JavaUtilObjectsTest.java new file mode 100644 index 00000000..a782bb99 --- /dev/null +++ b/tests/Cases/fixtures/java/JavaUtilObjectsTest.java @@ -0,0 +1,11 @@ +import java.util.Objects; + +class JavaUtilObjectsTest +{ + public static void testHashCode() + { + System.out.println(Objects.hashCode("hello, world")); + System.out.println(Objects.hashCode("HELLO, WORLD".toLowerCase())); + System.out.println(Objects.hashCode(new String("hello, world"))); + } +} diff --git a/tests/Cases/fixtures/java/LambdaSyntaxTest.java b/tests/Cases/fixtures/java/LambdaSyntaxTest.java new file mode 100644 index 00000000..593ca7d9 --- /dev/null +++ b/tests/Cases/fixtures/java/LambdaSyntaxTest.java @@ -0,0 +1,19 @@ +interface LambdaSyntaxTestInterface +{ + public void doSomething(String param); +} + +class LambdaSyntaxTest +{ + public static void testParameterWithLambdaSyntax() + { + childTestParameterWithLambdaSyntax((param) -> { + System.out.println(param); + }); + } + + private static void childTestParameterWithLambdaSyntax(LambdaSyntaxTestInterface lsti) + { + lsti.doSomething("Hello World!"); + } +} diff --git a/tests/Cases/fixtures/java/LongCalculationTest.java b/tests/Cases/fixtures/java/LongCalculationTest.java new file mode 100644 index 00000000..7301810f --- /dev/null +++ b/tests/Cases/fixtures/java/LongCalculationTest.java @@ -0,0 +1,38 @@ +class LongCalculationTest +{ + private static long returnLong(long n) + { + return n; + } + + public static long longAdd(long value1, long value2) + { + return value1 + value2; + } + + public static long longSub(long value1, long value2) + { + return value1 - value2; + } + + public static long longMul(long value1, long value2) + { + return value1 * value2; + } + + public static long longAddFromOtherMethod(long value1, long value2) + { + return returnLong(value1) + returnLong(value2); + } + + public static long longSubFromOtherMethod(long value1, long value2) + { + return returnLong(value1) - returnLong(value2); + } + + public static long longMulFromOtherMethod(long value1, long value2) + { + return returnLong(value1) * returnLong(value2); + } + +} diff --git a/tests/Cases/fixtures/java/LoopTest.java b/tests/Cases/fixtures/java/LoopTest.java new file mode 100644 index 00000000..647bbcc0 --- /dev/null +++ b/tests/Cases/fixtures/java/LoopTest.java @@ -0,0 +1,22 @@ +class LoopTest +{ + public static int calculateByFor(int length) + { + int sum = 0; + for (int i = 0; i < length; i++) { + sum += i; + } + return sum; + } + + public static int calculateByWhile(int length) + { + int sum = 0; + int i = 0; + while (i < length) { + sum += i; + i++; + } + return sum; + } +} diff --git a/tests/Cases/fixtures/java/MethodParameterTest.java b/tests/Cases/fixtures/java/MethodParameterTest.java new file mode 100644 index 00000000..d058566d --- /dev/null +++ b/tests/Cases/fixtures/java/MethodParameterTest.java @@ -0,0 +1,13 @@ +class MethodParameterTest +{ + public static void main(String[] args) + { + System.out.print("Hello World!"); + } + + + public static void noParameterMethod() + { + System.out.print("Hello World!"); + } +} diff --git a/tests/Cases/fixtures/java/NegationTest.java b/tests/Cases/fixtures/java/NegationTest.java new file mode 100644 index 00000000..99f4e264 --- /dev/null +++ b/tests/Cases/fixtures/java/NegationTest.java @@ -0,0 +1,22 @@ +class NegationTest +{ + public static int negateInt(int a) + { + return -a; + } + + public static double negateDouble(double a) + { + return -a; + } + + public static float negateFloat(float a) + { + return -a; + } + + public static long negateLong(long a) + { + return -a; + } +} diff --git a/tests/Cases/fixtures/java/ObjectCompareTest.java b/tests/Cases/fixtures/java/ObjectCompareTest.java new file mode 100644 index 00000000..87164f0c --- /dev/null +++ b/tests/Cases/fixtures/java/ObjectCompareTest.java @@ -0,0 +1,9 @@ +class ObjectCompareTest +{ + public static void compareInitiatedObjects() + { + ObjectCompareTest oct1 = new ObjectCompareTest(); + ObjectCompareTest oct2 = new ObjectCompareTest(); + System.out.println(oct1 == oct2 ? "Same" : "NotSame"); + } +} diff --git a/tests/Cases/fixtures/java/OuterClassTest.java b/tests/Cases/fixtures/java/OuterClassTest.java new file mode 100644 index 00000000..d0c088df --- /dev/null +++ b/tests/Cases/fixtures/java/OuterClassTest.java @@ -0,0 +1,9 @@ +class OuterClassTest +{ + public static void main(String[] args) + { + System.out.print(OuterClassTestOuterClass.staticHelloWorld()); + System.out.print(" AND "); + System.out.print((new OuterClassTestOuterClass()).dynamicHelloWorld()); + } +} diff --git a/tests/Cases/fixtures/java/OuterClassTestOuterClass.java b/tests/Cases/fixtures/java/OuterClassTestOuterClass.java new file mode 100644 index 00000000..1901c49f --- /dev/null +++ b/tests/Cases/fixtures/java/OuterClassTestOuterClass.java @@ -0,0 +1,12 @@ +class OuterClassTestOuterClass +{ + public static String staticHelloWorld() + { + return "Called Static Method"; + } + + public String dynamicHelloWorld() + { + return "Called Dynamic Method"; + } +} diff --git a/tests/Cases/fixtures/java/OutputDebugTraceTest.java b/tests/Cases/fixtures/java/OutputDebugTraceTest.java new file mode 100644 index 00000000..717d184c --- /dev/null +++ b/tests/Cases/fixtures/java/OutputDebugTraceTest.java @@ -0,0 +1,9 @@ +class OutputDebugTraceTest +{ + public static void main(String[] args) + { + System.out.print(args[0]); + System.out.print(args[1]); + System.out.print(args[2]); + } +} diff --git a/tests/Cases/fixtures/java/QuickSortTest.java b/tests/Cases/fixtures/java/QuickSortTest.java new file mode 100644 index 00000000..3ab535f9 --- /dev/null +++ b/tests/Cases/fixtures/java/QuickSortTest.java @@ -0,0 +1,77 @@ +class QuickSortTest +{ + + public static void asc(int[] array) + { + array = ascQuickSort(array, 0, array.length - 1); + for (int i = 0; i < array.length; i++) { + System.out.println(array[i]); + } + } + + public static void desc(int[] array) + { + array = descQuickSort(array, 0, array.length - 1); + for (int i = 0; i < array.length; i++) { + System.out.println(array[i]); + } + } + + public static int[] ascQuickSort(int[] array, int left, int right) + { + int tmp; + if (left < right) { + int i = left; + int j = right; + int pivot = array[i + (j - i) / 2]; + while (true) { + while (array[i] < pivot) { + i++; + } + while (pivot < array[j]) { + j--; + } + if (i >= j) { + break; + } + tmp = array[i]; + array[i] = array[j]; + array[j] = tmp; + i++; + j--; + } + ascQuickSort(array, left, i - 1); + ascQuickSort(array, j + 1, right); + } + return array; + } + + public static int[] descQuickSort(int[] array, int left, int right) + { + int tmp; + if (left < right) { + int i = left; + int j = right; + int pivot = array[i + (j - i) / 2]; + while (true) { + while (array[i] > pivot) { + i++; + } + while (pivot > array[j]) { + j--; + } + if (i >= j) { + break; + } + tmp = array[i]; + array[i] = array[j]; + array[j] = tmp; + i++; + j--; + } + descQuickSort(array, left, i - 1); + descQuickSort(array, j + 1, right); + } + return array; + } +} \ No newline at end of file diff --git a/tests/Cases/fixtures/java/SuperExtendingClassTest.java b/tests/Cases/fixtures/java/SuperExtendingClassTest.java new file mode 100644 index 00000000..98a3662f --- /dev/null +++ b/tests/Cases/fixtures/java/SuperExtendingClassTest.java @@ -0,0 +1,4 @@ +class SuperExtendingClassTest +{ + protected int value = 99999; +} \ No newline at end of file diff --git a/tests/Cases/fixtures/java/SwitchTest.java b/tests/Cases/fixtures/java/SwitchTest.java new file mode 100644 index 00000000..b15e7ee9 --- /dev/null +++ b/tests/Cases/fixtures/java/SwitchTest.java @@ -0,0 +1,32 @@ +class SwitchTest +{ + public static void tableswitch(int jump) + { + switch (jump) { + case -1: + System.out.print("Cat"); + break; + case 0: + System.out.print("Dog"); + break; + case 1: + System.out.print("Hamster"); + break; + } + } + + public static void lookupswitch(int jump) + { + switch (jump) { + case 1234: + System.out.print("Lion"); + break; + case 5678: + System.out.print("Panda"); + break; + case 9999: + System.out.print("Elephant"); + break; + } + } +} diff --git a/tests/Cases/fixtures/java/TryCatchTest.java b/tests/Cases/fixtures/java/TryCatchTest.java new file mode 100644 index 00000000..dd0c10fe --- /dev/null +++ b/tests/Cases/fixtures/java/TryCatchTest.java @@ -0,0 +1,41 @@ +class TryCatchTest +{ + public static int testPassthroughTryStatement() + { + try { + // nothing to do + } catch (NullPointerException e) { + return -1; + } + return 1; + } + + + public static int testPassthroughCatchStatement() + { + try { + throw new NullPointerException(); + } catch (NullPointerException e) { + return -1; + } + } + + public static int testImitationThrowException() + { + String test = "Test value"; + + try { + // Get an error + test.charAt(-1); + } catch (IndexOutOfBoundsException e) { + return -1; + } + + return 1; + } + + public static void testImitationThrownExceptionHasPreviousException() + { + "".charAt(-1); + } +} diff --git a/tests/Cases/kotlin/HelloWorldKotlin.kt b/tests/Cases/kotlin/HelloWorldKotlin.kt new file mode 100644 index 00000000..1e42b4ff --- /dev/null +++ b/tests/Cases/kotlin/HelloWorldKotlin.kt @@ -0,0 +1,3 @@ +fun main(args: Array) { + println("Hello World!") +} diff --git a/tests/Cases/templates/BubbleSortAsc.txt b/tests/Cases/templates/BubbleSortAsc.txt new file mode 100644 index 00000000..b7af7d3b --- /dev/null +++ b/tests/Cases/templates/BubbleSortAsc.txt @@ -0,0 +1,12 @@ +-50 +-14 +-10 +1 +2 +5 +33 +44 +99 +111 +123 +9999 diff --git a/tests/Cases/templates/BubbleSortDesc.txt b/tests/Cases/templates/BubbleSortDesc.txt new file mode 100644 index 00000000..b68dfb13 --- /dev/null +++ b/tests/Cases/templates/BubbleSortDesc.txt @@ -0,0 +1,12 @@ +9999 +123 +111 +99 +44 +33 +5 +2 +1 +-10 +-14 +-50 diff --git a/tests/Cases/templates/DebugTraceTest.txt b/tests/Cases/templates/DebugTraceTest.txt new file mode 100644 index 00000000..c9956f1a --- /dev/null +++ b/tests/Cases/templates/DebugTraceTest.txt @@ -0,0 +1,28 @@ +[method] +public static void main(java.lang.String[]) + +[code] +<0xb2> <0x00> <0x02> <0x2a> <0x03> <0x32> <0xb6> <0x00> <0x03> <0xb2> <0x00> <0x02> <0x2a> <0x04> <0x32> <0xb6> <0x00> <0x03> <0xb2> <0x00> +<0x02> <0x2a> <0x05> <0x32> <0xb6> <0x00> <0x03> <0xb1> + +[executed] + PC | OPCODE | MNEMONIC | OPERANDS | LOCAL STORAGE +---------+--------+----------------------+------------+----------------- + 0 | 0xB2 | getstatic | 0 | 1 + 3 | 0x2A | aload_0 | 1 | 1 + 4 | 0x03 | iconst_0 | 2 | 1 + 5 | 0x32 | aaload | 3 | 1 + 6 | 0xB6 | invokevirtual | 2 | 1 + 9 | 0xB2 | getstatic | 0 | 1 + 12 | 0x2A | aload_0 | 1 | 1 + 13 | 0x04 | iconst_1 | 2 | 1 + 14 | 0x32 | aaload | 3 | 1 + 15 | 0xB6 | invokevirtual | 2 | 1 + 18 | 0xB2 | getstatic | 0 | 1 + 21 | 0x2A | aload_0 | 1 | 1 + 22 | 0x05 | iconst_2 | 2 | 1 + 23 | 0x32 | aaload | 3 | 1 + 24 | 0xB6 | invokevirtual | 2 | 1 + 27 | 0xB1 | return | 0 | 1 +---------+--------+----------------------+------------+----------------- + diff --git a/tests/Cases/templates/FizzBuzzTest.txt b/tests/Cases/templates/FizzBuzzTest.txt new file mode 100644 index 00000000..dd964881 --- /dev/null +++ b/tests/Cases/templates/FizzBuzzTest.txt @@ -0,0 +1,100 @@ +1 +2 +Fizz +4 +Buzz +Fizz +7 +8 +Fizz +Buzz +11 +Fizz +13 +14 +FizzBuzz +16 +17 +Fizz +19 +Buzz +Fizz +22 +23 +Fizz +Buzz +26 +Fizz +28 +29 +FizzBuzz +31 +32 +Fizz +34 +Buzz +Fizz +37 +38 +Fizz +Buzz +41 +Fizz +43 +44 +FizzBuzz +46 +47 +Fizz +49 +Buzz +Fizz +52 +53 +Fizz +Buzz +56 +Fizz +58 +59 +FizzBuzz +61 +62 +Fizz +64 +Buzz +Fizz +67 +68 +Fizz +Buzz +71 +Fizz +73 +74 +FizzBuzz +76 +77 +Fizz +79 +Buzz +Fizz +82 +83 +Fizz +Buzz +86 +Fizz +88 +89 +FizzBuzz +91 +92 +Fizz +94 +Buzz +Fizz +97 +98 +Fizz +Buzz diff --git a/tests/Cases/templates/InsertionSortAsc.txt b/tests/Cases/templates/InsertionSortAsc.txt new file mode 100644 index 00000000..b7af7d3b --- /dev/null +++ b/tests/Cases/templates/InsertionSortAsc.txt @@ -0,0 +1,12 @@ +-50 +-14 +-10 +1 +2 +5 +33 +44 +99 +111 +123 +9999 diff --git a/tests/Cases/templates/InsertionSortDesc.txt b/tests/Cases/templates/InsertionSortDesc.txt new file mode 100644 index 00000000..b68dfb13 --- /dev/null +++ b/tests/Cases/templates/InsertionSortDesc.txt @@ -0,0 +1,12 @@ +9999 +123 +111 +99 +44 +33 +5 +2 +1 +-10 +-14 +-50 diff --git a/tests/Cases/templates/QuickSortAsc.txt b/tests/Cases/templates/QuickSortAsc.txt new file mode 100644 index 00000000..b7af7d3b --- /dev/null +++ b/tests/Cases/templates/QuickSortAsc.txt @@ -0,0 +1,12 @@ +-50 +-14 +-10 +1 +2 +5 +33 +44 +99 +111 +123 +9999 diff --git a/tests/Cases/templates/QuickSortDesc.txt b/tests/Cases/templates/QuickSortDesc.txt new file mode 100644 index 00000000..b68dfb13 --- /dev/null +++ b/tests/Cases/templates/QuickSortDesc.txt @@ -0,0 +1,12 @@ +9999 +123 +111 +99 +44 +33 +5 +2 +1 +-10 +-14 +-50 diff --git a/tests/Helpers/AssertionHelpers/AssertFields.php b/tests/Helpers/AssertionHelpers/AssertFields.php new file mode 100644 index 00000000..edf0761c --- /dev/null +++ b/tests/Helpers/AssertionHelpers/AssertFields.php @@ -0,0 +1,36 @@ +fixtures[0]] + ->getInvoker() + ->getStatic() + ->getFields() + ->get("s_{$number}"); + + $this->assertEquals($expected, (string) $result); + } + + private function assertDynamicField($expected, $number) + { + static $instance = null; + if ($instance === null) { + $instance = static::$initiatedJavaClasses[$this->fixtures[0]] + ->getInvoker() + ->construct(); + } + $result = $instance + ->getDynamic() + ->getFields() + ->get("d_{$number}"); + + $this->assertEquals($expected, (string) $result); + } +} diff --git a/tests/Helpers/GetField.php b/tests/Helpers/GetField.php new file mode 100644 index 00000000..ff5c7862 --- /dev/null +++ b/tests/Helpers/GetField.php @@ -0,0 +1,28 @@ +fixtures[0]] + ->getInvoker() + ->getStatic() + ->getFields() + ->get($name); + } + + private function getDynamicField($name) + { + static $instance = null; + if ($instance === null) { + $instance = static::$initiatedJavaClasses[$this->fixtures[0]] + ->getInvoker() + ->construct(); + } + return $instance + ->getDynamic() + ->getFields() + ->get($name); + } +} diff --git a/tests/Helpers/MakeUnicodeChar.php b/tests/Helpers/MakeUnicodeChar.php new file mode 100644 index 00000000..efa7323d --- /dev/null +++ b/tests/Helpers/MakeUnicodeChar.php @@ -0,0 +1,10 @@ + [ + 'handler' => '/dev/null', + 'heapspace' => true, + ], + 'operations' => [ + 'enable_trace' => true, + ], +]);