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/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" diff --git a/src/Core/JVM/Invoker/Invokable.php b/src/Core/JVM/Invoker/Invokable.php index 4c8b96ed..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( @@ -80,11 +87,12 @@ function ($argument) { ); $method = null; + + // the special method foreach ($methodReferences as $methodReference) { $methodSignature = Formatter::buildArgumentsSignature( Formatter::parseSignature($constantPool[$methodReference->getDescriptorIndex()]->getString())['arguments'] ); - if ($methodSignature === $convertedPassedArguments) { $method = $methodReference; break; diff --git a/src/Core/JavaClass.php b/src/Core/JavaClass.php index ca144a73..8de10e37 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,10 @@ class JavaClass */ private $invoker; + private $innerClasses = []; + + private $parentClass; + /** * JavaClass constructor. * @param JavaClassReader $reader @@ -94,6 +100,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 +134,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(); @@ -156,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..11e82fde 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(...$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/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/_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 187605ba..dfb93156 100644 --- a/src/Kernel/Mnemonics/_new.php +++ b/src/Kernel/Mnemonics/_new.php @@ -21,17 +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 */ - $this->pushStack( - $classObject - ->getInvoker() - ->construct() - ->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 f9daa92d..aeb57e2b 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 (strpos($relativePath, '$') !== false && $class !== null) { + $initiatedClass->setParentClass($class); + } return $resolvedPaths[] = [ static::RESOLVED_TYPE_CLASS, - new JavaClass(new JavaClassReader($path)), + $initiatedClass, ]; } break; 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 [ 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; + } + } +}