From 2803b1cbdebb3ce9d376f0a97ebb7bf454193af6 Mon Sep 17 00:00:00 2001 From: memory-agape Date: Thu, 28 Feb 2019 21:57:47 +0900 Subject: [PATCH 1/5] WIP commit --- src/Core/JVM/Invoker/Invokable.php | 4 +++ src/Core/JavaClass.php | 25 ++++++++++++++++++- .../Attributes/InnerClassesAttribute.php | 18 +++++++------ src/Kernel/Mnemonics/_new.php | 19 ++++++++++---- src/Utilities/ClassResolver.php | 3 ++- src/Utilities/TypeResolver.php | 11 ++++++++ 6 files changed, 65 insertions(+), 15 deletions(-) diff --git a/src/Core/JVM/Invoker/Invokable.php b/src/Core/JVM/Invoker/Invokable.php index 4c8b96ed..1c17e14e 100644 --- a/src/Core/JVM/Invoker/Invokable.php +++ b/src/Core/JVM/Invoker/Invokable.php @@ -80,6 +80,8 @@ function ($argument) { ); $method = null; + + // the special method foreach ($methodReferences as $methodReference) { $methodSignature = Formatter::buildArgumentsSignature( Formatter::parseSignature($constantPool[$methodReference->getDescriptorIndex()]->getString())['arguments'] @@ -92,6 +94,8 @@ function ($argument) { } if ($method === null) { + // var_dump($convertedPassedArguments); + // debug_print_backtrace(); throw new NoSuchMethodException('Call to undefined method ' . $name . '.'); } diff --git a/src/Core/JavaClass.php b/src/Core/JavaClass.php index ca144a73..6a934779 100644 --- a/src/Core/JavaClass.php +++ b/src/Core/JavaClass.php @@ -8,6 +8,8 @@ use PHPJava\Core\JVM\ConstantPool; use PHPJava\Core\JVM\Validations\MagicByte; use PHPJava\Exceptions\ValidatorException; +use PHPJava\Kernel\Attributes\AttributeInterface; +use PHPJava\Kernel\Attributes\InnerClassesAttribute; use PHPJava\Kernel\Maps\AccessFlag; use PHPJava\Kernel\Structures\_Utf8; use PHPJava\Utilities\Formatter; @@ -62,6 +64,8 @@ class JavaClass */ private $invoker; + private $innerClasses = []; + /** * JavaClass constructor. * @param JavaClassReader $reader @@ -94,6 +98,7 @@ public function __construct(JavaClassReader $reader) $this->thisClass = $reader->getBinaryReader()->readUnsignedShort(); $constantPoolEntries = $this->constantPool->getEntries(); + $this->className = $constantPoolEntries[$constantPoolEntries[$this->thisClass]->getClassIndex()]; // read super class @@ -127,14 +132,32 @@ public function __construct(JavaClassReader $reader) $this->constantPool ); + foreach ($this->activeAttributes->getEntries() as $entry) { + if ($entry->getAttributeData() instanceof InnerClassesAttribute) { + $this->innerClasses = array_merge( + $this->innerClasses, + $entry->getAttributeData()->getClasses() + ); + } + } + $this->invoker = new JavaClassInvoker($this); } - public function getClassName(): string + public function getClassName(bool $shortName = false): string { + if ($shortName === true) { + $split = explode('$', $this->className->getString()); + return $split[count($split) - 1]; + } return $this->className->getString(); } + public function getInnerClasses(): array + { + return $this->innerClasses; + } + public function getFields(): array { return $this->activeFields->getEntries(); diff --git a/src/Kernel/Attributes/InnerClassesAttribute.php b/src/Kernel/Attributes/InnerClassesAttribute.php index 6eb23852..f7f15453 100644 --- a/src/Kernel/Attributes/InnerClassesAttribute.php +++ b/src/Kernel/Attributes/InnerClassesAttribute.php @@ -2,6 +2,7 @@ namespace PHPJava\Kernel\Attributes; use PHPJava\Exceptions\NotImplementedException; +use PHPJava\Kernel\Structures\_Classes; use PHPJava\Utilities\BinaryTool; final class InnerClassesAttribute implements AttributeInterface @@ -10,20 +11,21 @@ final class InnerClassesAttribute implements AttributeInterface use \PHPJava\Kernel\Core\ConstantPool; private $numberOfClasses = 0; - private $classes = array(); + private $classes = []; + public function execute(): void { $this->numberOfClasses = $this->readUnsignedShort(); for ($i = 0; $i < $this->numberOfClasses; $i++) { - $thises[$i] = new JavaStructureClasses($this); - $thises[$i]->setInnerClassInfoIndex($this->readUnsignedShort()); - $thises[$i]->setOuterClassInfoIndex($this->readUnsignedShort()); - $thises[$i]->setInnerNameIndex($this->readUnsignedShort()); - $thises[$i]->setInnerClassAccessFlag($this->readUnsignedShort()); + $this->classes[$i] = new _Classes($this->reader); + $this->classes[$i]->setInnerClassInfoIndex($this->readUnsignedShort()); + $this->classes[$i]->setOuterClassInfoIndex($this->readUnsignedShort()); + $this->classes[$i]->setInnerNameIndex($this->readUnsignedShort()); + $this->classes[$i]->setInnerClassAccessFlag($this->readUnsignedShort()); } } - public function getClasses() + public function getClasses(): array { - return $thises; + return $this->classes; } } diff --git a/src/Kernel/Mnemonics/_new.php b/src/Kernel/Mnemonics/_new.php index 187605ba..46400b31 100644 --- a/src/Kernel/Mnemonics/_new.php +++ b/src/Kernel/Mnemonics/_new.php @@ -26,12 +26,21 @@ public function execute(): void /** * @var \PHPJava\Core\JavaClass $classObject */ - $this->pushStack( - $classObject - ->getInvoker() - ->construct() - ->getJavaClass() + $classObject = $classObject + ->getInvoker() + ->construct() + ->getJavaClass(); + + $className = explode( + '$', + $classObject->getClassName() ); + + if (count($className) > 1) { + $classObject->setParentClass($this->javaClass); + } + + $this->pushStack($classObject); return; } $this->pushStack(new $classObject()); diff --git a/src/Utilities/ClassResolver.php b/src/Utilities/ClassResolver.php index f9daa92d..4fce200e 100644 --- a/src/Utilities/ClassResolver.php +++ b/src/Utilities/ClassResolver.php @@ -34,7 +34,8 @@ public static function resolve($javaPath): array foreach (static::$resolves as [$resourceType, $value]) { switch ($resourceType) { case static::RESOURCE_TYPE_FILE: - $path = realpath($value . '/' . $relativePath . '.class'); + $relativePathByMainClass = explode('$', $relativePath, 2)[0]; + $path = realpath($value . '/' . $relativePathByMainClass . '.class'); if (($key = array_search($path, static::$resolvedPaths, true)) !== false) { return static::$resolvedPaths[$key]; } diff --git a/src/Utilities/TypeResolver.php b/src/Utilities/TypeResolver.php index a979607e..b486e463 100644 --- a/src/Utilities/TypeResolver.php +++ b/src/Utilities/TypeResolver.php @@ -1,6 +1,7 @@ 'class', + 'class_name' => $arguments->getClassName(false), + 'deep_array' => $deepArray, + ]; + } + 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 ($resolveType === 'class') { return [ From 192347a56920a9a2dcd314c2f76e3dfd349c6491 Mon Sep 17 00:00:00 2001 From: memory-agape Date: Fri, 1 Mar 2019 12:18:17 +0900 Subject: [PATCH 2/5] WIP commit --- src/Core/JVM/Invoker/Invokable.php | 6 ++++-- src/Core/JavaClass.php | 18 ++++++++++++++++++ src/Core/JavaClassInvoker.php | 5 +++-- src/Kernel/Mnemonics/_new.php | 18 +++++------------- src/Utilities/ClassResolver.php | 5 ++--- 5 files changed, 32 insertions(+), 20 deletions(-) diff --git a/src/Core/JVM/Invoker/Invokable.php b/src/Core/JVM/Invoker/Invokable.php index 1c17e14e..cdc62999 100644 --- a/src/Core/JVM/Invoker/Invokable.php +++ b/src/Core/JVM/Invoker/Invokable.php @@ -87,6 +87,8 @@ function ($argument) { Formatter::parseSignature($constantPool[$methodReference->getDescriptorIndex()]->getString())['arguments'] ); + var_dump($methodSignature, $convertedPassedArguments); + if ($methodSignature === $convertedPassedArguments) { $method = $methodReference; break; @@ -94,8 +96,8 @@ function ($argument) { } if ($method === null) { - // var_dump($convertedPassedArguments); - // debug_print_backtrace(); + + debug_print_backtrace(); throw new NoSuchMethodException('Call to undefined method ' . $name . '.'); } diff --git a/src/Core/JavaClass.php b/src/Core/JavaClass.php index 6a934779..8de10e37 100644 --- a/src/Core/JavaClass.php +++ b/src/Core/JavaClass.php @@ -66,6 +66,8 @@ class JavaClass private $innerClasses = []; + private $parentClass; + /** * JavaClass constructor. * @param JavaClassReader $reader @@ -179,6 +181,22 @@ public function appendDebug($log) return $this; } + public function hasParentClass(): bool + { + return isset($this->parentClass); + } + + public function setParentClass(JavaClass $class): self + { + $this->parentClass = $class; + return $this; + } + + public function getParentClass(): JavaClass + { + return $this->parentClass; + } + public function debug(): void { $cpInfo = $this->getConstantPool()->getEntries(); diff --git a/src/Core/JavaClassInvoker.php b/src/Core/JavaClassInvoker.php index a6b58fed..f22e4161 100644 --- a/src/Core/JavaClassInvoker.php +++ b/src/Core/JavaClassInvoker.php @@ -93,9 +93,10 @@ public function __construct(JavaClass $javaClass) } /** + * @param array $arguments * @return JavaClassInvoker */ - public function construct(): self + public function construct(array $arguments = []): self { $this->dynamicAccessor = new DynamicAccessor( $this, @@ -103,7 +104,7 @@ public function construct(): self ); if (isset($this->dynamicMethods[''])) { - $this->getDynamic()->getMethods()->call(''); + $this->getDynamic()->getMethods()->call('', ...$arguments); } return $this; diff --git a/src/Kernel/Mnemonics/_new.php b/src/Kernel/Mnemonics/_new.php index 46400b31..5b3e9b2c 100644 --- a/src/Kernel/Mnemonics/_new.php +++ b/src/Kernel/Mnemonics/_new.php @@ -26,21 +26,13 @@ public function execute(): void /** * @var \PHPJava\Core\JavaClass $classObject */ - $classObject = $classObject - ->getInvoker() - ->construct() - ->getJavaClass(); - $className = explode( - '$', - $classObject->getClassName() - ); - - if (count($className) > 1) { - $classObject->setParentClass($this->javaClass); + $classObjectInvoker = $classObject->getInvoker(); + $arguments = []; + if ($classObject->hasParentClass()) { + $arguments[] = $this->javaClass; } - - $this->pushStack($classObject); + $this->pushStack($classObjectInvoker->construct(...$arguments)->getJavaClass()); return; } $this->pushStack(new $classObject()); diff --git a/src/Utilities/ClassResolver.php b/src/Utilities/ClassResolver.php index 4fce200e..2ccd35cb 100644 --- a/src/Utilities/ClassResolver.php +++ b/src/Utilities/ClassResolver.php @@ -34,15 +34,14 @@ public static function resolve($javaPath): array foreach (static::$resolves as [$resourceType, $value]) { switch ($resourceType) { case static::RESOURCE_TYPE_FILE: - $relativePathByMainClass = explode('$', $relativePath, 2)[0]; - $path = realpath($value . '/' . $relativePathByMainClass . '.class'); + $path = realpath($value . '/' . $relativePath . '.class'); if (($key = array_search($path, static::$resolvedPaths, true)) !== false) { return static::$resolvedPaths[$key]; } if (is_file($path)) { return $resolvedPaths[] = [ static::RESOLVED_TYPE_CLASS, - new JavaClass(new JavaClassReader($path)), + (new JavaClass(new JavaClassReader($path))) ]; } break; From 36189fbef901b656654a56e557b65d6b60956a4e Mon Sep 17 00:00:00 2001 From: memory-agape Date: Fri, 1 Mar 2019 12:44:23 +0900 Subject: [PATCH 3/5] Support to inner class --- src/Core/JVM/Invoker/Invokable.php | 12 +++++++----- src/Core/JavaClassInvoker.php | 2 +- src/Kernel/Mnemonics/_invokespecial.php | 14 +++++++++++++- src/Kernel/Mnemonics/_new.php | 9 ++------- src/Utilities/ClassResolver.php | 8 ++++++-- 5 files changed, 29 insertions(+), 16 deletions(-) diff --git a/src/Core/JVM/Invoker/Invokable.php b/src/Core/JVM/Invoker/Invokable.php index cdc62999..ba5f0707 100644 --- a/src/Core/JVM/Invoker/Invokable.php +++ b/src/Core/JVM/Invoker/Invokable.php @@ -69,6 +69,13 @@ public function call(string $name, ...$arguments) ->getConstantPool() ->getEntries(); + if ($name === '' && $this->javaClassInvoker->getJavaClass()->hasParentClass()) { + array_unshift( + $arguments, + $this->javaClassInvoker->getJavaClass()->getParentClass() + ); + } + // Find same method $convertedPassedArguments = Formatter::buildArgumentsSignature( array_map( @@ -86,9 +93,6 @@ function ($argument) { $methodSignature = Formatter::buildArgumentsSignature( Formatter::parseSignature($constantPool[$methodReference->getDescriptorIndex()]->getString())['arguments'] ); - - var_dump($methodSignature, $convertedPassedArguments); - if ($methodSignature === $convertedPassedArguments) { $method = $methodReference; break; @@ -96,8 +100,6 @@ function ($argument) { } if ($method === null) { - - debug_print_backtrace(); throw new NoSuchMethodException('Call to undefined method ' . $name . '.'); } diff --git a/src/Core/JavaClassInvoker.php b/src/Core/JavaClassInvoker.php index f22e4161..11e82fde 100644 --- a/src/Core/JavaClassInvoker.php +++ b/src/Core/JavaClassInvoker.php @@ -96,7 +96,7 @@ public function __construct(JavaClass $javaClass) * @param array $arguments * @return JavaClassInvoker */ - public function construct(array $arguments = []): self + public function construct(...$arguments): self { $this->dynamicAccessor = new DynamicAccessor( $this, diff --git a/src/Kernel/Mnemonics/_invokespecial.php b/src/Kernel/Mnemonics/_invokespecial.php index 5e5c5ad9..dcfda166 100644 --- a/src/Kernel/Mnemonics/_invokespecial.php +++ b/src/Kernel/Mnemonics/_invokespecial.php @@ -30,6 +30,7 @@ public function execute(): void krsort($arguments); $methodName = $cpInfo[$nameAndTypeIndex->getNameIndex()]->getString(); + if ($this->javaClassInvoker->isInvoked($methodName, $signature)) { return; } @@ -38,7 +39,18 @@ public function execute(): void ->addToSpecialInvokedList($methodName, $signature); if ($invokerClass instanceof JavaClass) { - $result = $invokerClass->getInvoker()->getDynamic()->getMethods() + if ($invokerClass->getInvoker()->isInvoked($methodName, $signature)) { + return; + } + + $invokerClass + ->getInvoker() + ->addToSpecialInvokedList($methodName, $signature); + + $result = $invokerClass + ->getInvoker() + ->getDynamic() + ->getMethods() ->call( $methodName, ...$arguments diff --git a/src/Kernel/Mnemonics/_new.php b/src/Kernel/Mnemonics/_new.php index 5b3e9b2c..dfb93156 100644 --- a/src/Kernel/Mnemonics/_new.php +++ b/src/Kernel/Mnemonics/_new.php @@ -21,18 +21,13 @@ public function execute(): void return; } - [$resourceType, $classObject] = ClassResolver::resolve($className); + [$resourceType, $classObject] = ClassResolver::resolve($className, $this->javaClass); if ($resourceType === ClassResolver::RESOLVED_TYPE_CLASS) { /** * @var \PHPJava\Core\JavaClass $classObject */ - $classObjectInvoker = $classObject->getInvoker(); - $arguments = []; - if ($classObject->hasParentClass()) { - $arguments[] = $this->javaClass; - } - $this->pushStack($classObjectInvoker->construct(...$arguments)->getJavaClass()); + $this->pushStack($classObject->getInvoker()->construct()->getJavaClass()); return; } $this->pushStack(new $classObject()); diff --git a/src/Utilities/ClassResolver.php b/src/Utilities/ClassResolver.php index 2ccd35cb..c2b03830 100644 --- a/src/Utilities/ClassResolver.php +++ b/src/Utilities/ClassResolver.php @@ -21,7 +21,7 @@ class ClassResolver private static $resolves = []; private static $resolvedPaths = []; - public static function resolve($javaPath): array + public static function resolve(string $javaPath, JavaClass $class = null): array { $namespaces = explode('.', str_replace('/', '.', $javaPath)); $buildClassPath = []; @@ -39,9 +39,13 @@ public static function resolve($javaPath): array return static::$resolvedPaths[$key]; } if (is_file($path)) { + $initiatedClass = new JavaClass(new JavaClassReader($path)); + if ($class !== null) { + $initiatedClass->setParentClass($class); + } return $resolvedPaths[] = [ static::RESOLVED_TYPE_CLASS, - (new JavaClass(new JavaClassReader($path))) + $initiatedClass, ]; } break; From f4e95015b9b2f3ec52e205ccf07d66ef5c2e0999 Mon Sep 17 00:00:00 2001 From: memory-agape Date: Fri, 1 Mar 2019 13:07:02 +0900 Subject: [PATCH 4/5] Update readme --- README.md | 1 - src/Utilities/ClassResolver.php | 2 +- tests/InnerClassTest.php | 28 +++++++++++++++++++++++++ tests/fixtures/java/InnerClassTest.java | 20 ++++++++++++++++++ 4 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 tests/InnerClassTest.php create mode 100644 tests/fixtures/java/InnerClassTest.java diff --git a/README.md b/README.md index 31c4bef9..73e4d3a9 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,6 @@ We welcoming to contributions this project 💪 ## Not currently supported Sorry, I do not have enough time (T_T) -- Inner classes - Annotations - Extends other class - Implements diff --git a/src/Utilities/ClassResolver.php b/src/Utilities/ClassResolver.php index c2b03830..aeb57e2b 100644 --- a/src/Utilities/ClassResolver.php +++ b/src/Utilities/ClassResolver.php @@ -40,7 +40,7 @@ public static function resolve(string $javaPath, JavaClass $class = null): array } if (is_file($path)) { $initiatedClass = new JavaClass(new JavaClassReader($path)); - if ($class !== null) { + if (strpos($relativePath, '$') !== false && $class !== null) { $initiatedClass->setParentClass($class); } return $resolvedPaths[] = [ diff --git a/tests/InnerClassTest.php b/tests/InnerClassTest.php new file mode 100644 index 00000000..4af11763 --- /dev/null +++ b/tests/InnerClassTest.php @@ -0,0 +1,28 @@ +initiatedJavaClasses['InnerClassTest'] + ->getInvoker() + ->getStatic() + ->getMethods() + ->call( + 'main', + ["Hello", "World"] + ); + $result = ob_get_clean(); + + $this->assertEquals("Hello World", $result); + } +} diff --git a/tests/fixtures/java/InnerClassTest.java b/tests/fixtures/java/InnerClassTest.java new file mode 100644 index 00000000..4a7f0139 --- /dev/null +++ b/tests/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; + } + } +} From c4014cc4bac8cd7518d962e60f5f4e27f2cc0180 Mon Sep 17 00:00:00 2001 From: memory-agape Date: Fri, 1 Mar 2019 13:07:23 +0900 Subject: [PATCH 5/5] Upgrade minor version --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 23904587..4e54686b 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,7 @@ "description": "JVM emulator by PHP", "type": "library", "license": "MIT", - "version": "0.0.3-dev", + "version": "0.0.3.1-dev", "authors": [ { "name": "memory"