Skip to content

Commit 4d2d1b9

Browse files
authored
Merge pull request #132 from php-java/fix-cannot-call-constructor
WIP: Fix: cannot call constructor with parameters
2 parents af5a923 + 69c3183 commit 4d2d1b9

File tree

13 files changed

+248
-80
lines changed

13 files changed

+248
-80
lines changed

src/Core/JVM/Invoker/Invokable.php

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -79,14 +79,6 @@ public function call(string $name, ...$arguments)
7979
}
8080

8181
$constantPool = $currentConstantPool->getEntries();
82-
83-
if ($name === '<init>' && $this->javaClassInvoker->getJavaClass()->hasParentClass()) {
84-
array_unshift(
85-
$arguments,
86-
$this->javaClassInvoker->getJavaClass()->getParentClass()
87-
);
88-
}
89-
9082
$convertedPassedArguments = $this->stringifyArguments(...$arguments);
9183

9284
$method = $operationCache->fetchOrPush(
@@ -225,7 +217,7 @@ function ($item) {
225217
'OpCode: 0x%02X %-15.15s Stacks: %-4.4s PC: %-8.8s Used Memory: %-8.8s Used Memory Peak: %-8.8s',
226218
[
227219
$opcode,
228-
str_replace('_', '', $mnemonic),
220+
ltrim($mnemonic, '_'),
229221
count($stacks),
230222
$pointer,
231223
Metric::bytes(memory_get_usage())->format(),
@@ -257,7 +249,7 @@ function () use ($fullName) {
257249
$returnValue = $executor
258250
->setConstantPool($currentConstantPool)
259251
->setParameters(
260-
$method->getAttributes(),
252+
$method,
261253
$this->javaClassInvoker,
262254
$reader,
263255
$localStorage,

src/Core/JavaArchive.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use PHPJava\Core\JVM\Parameters\Runtime;
66
use PHPJava\Core\Stream\Reader\InlineReader;
77
use PHPJava\Exceptions\UndefinedEntrypointException;
8+
use PHPJava\Kernel\Internal\JavaClassDeferredLoader;
89
use PHPJava\Packages\java\io\FileNotFoundException;
910
use PHPJava\Packages\java\lang\_Object;
1011
use PHPJava\Packages\java\lang\ClassNotFoundException;

src/Core/JavaClass.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,14 @@ public function __construct(ReaderInterface $reader, array $options = [])
237237
}
238238
}
239239

240+
public function __invoke(...$arguments): JavaClass
241+
{
242+
return $this
243+
->getInvoker()
244+
->construct(...$arguments)
245+
->getJavaClass();
246+
}
247+
240248
public function getOptions($key = null)
241249
{
242250
if (isset($key)) {
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?php
2+
namespace PHPJava\Exceptions;
3+
4+
class NotInstantiatedException extends \Exception
5+
{
6+
}

src/Kernel/Core/Accumulator.php

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
use PHPJava\Core\JavaClass;
55
use PHPJava\Core\JavaClassInvoker;
66
use PHPJava\Kernel\Provider\DependencyInjectionProvider;
7+
use PHPJava\Kernel\Structures\_MethodInfo;
78

89
trait Accumulator
910
{
@@ -32,21 +33,27 @@ trait Accumulator
3233
private $stacks = [];
3334
private $options;
3435

36+
/**
37+
* @var _MethodInfo $method
38+
*/
39+
private $method;
40+
3541
/**
3642
* @var DependencyInjectionProvider
3743
*/
3844
private $dependencyInjectionProvider;
3945

4046
public function setParameters(
41-
array $attributes,
47+
_MethodInfo $method,
4248
JavaClassInvoker $javaClassInvoker,
4349
\PHPJava\Core\JVM\Stream\BinaryReader $reader,
4450
array &$localStorage,
4551
array &$stacks,
4652
int $pointer,
4753
DependencyInjectionProvider $dependencyInjectionProvider
4854
): self {
49-
$this->attributes = $attributes;
55+
$this->method = $method;
56+
$this->attributes = $method->getAttributes();
5057
$this->javaClassInvoker = $javaClassInvoker;
5158
$this->javaClass = $javaClassInvoker->getJavaClass();
5259
$this->options = $this->javaClass->getOptions();
@@ -129,18 +136,21 @@ public function pushToOperandStackByReference(&$value)
129136
$this->stacks[] = &$value;
130137
}
131138

132-
public function dupStack()
139+
public function popFromOperandStack()
133140
{
134-
$stack = $this->stacks[sizeof($this->stacks) - 1] ?? null;
135-
if ($stack === null) {
136-
throw new \Exception('Stack overflow');
137-
}
138-
$this->pushStack($stack);
141+
return array_pop($this->stacks);
139142
}
140143

141-
public function popFromOperandStack()
144+
public function getCurrentStackIndex()
142145
{
143-
return array_pop($this->stacks);
146+
return count($this->stacks) - 1;
147+
}
148+
149+
public function replaceReferredObject($searchObject, $newObject)
150+
{
151+
while (($index = array_search($searchObject, $this->stacks)) !== false) {
152+
$this->stacks[$index] = $newObject;
153+
}
144154
}
145155

146156
public function popStack()
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
namespace PHPJava\Kernel\Internal;
3+
4+
use PHPJava\Core\JavaClassInterface;
5+
use PHPJava\Exceptions\NotInstantiatedException;
6+
use PHPJava\Utilities\ClassResolver;
7+
8+
final class InstanceDeferredLoader
9+
{
10+
private $instance;
11+
private $classObject;
12+
private $resourceType;
13+
private $className;
14+
15+
/**
16+
* InstanceDeferredLoader constructor.
17+
* @param $classObject
18+
* @param $resourceType
19+
* @param $className
20+
*/
21+
public function __construct(
22+
$classObject,
23+
$resourceType,
24+
$className
25+
) {
26+
$this->classObject = $classObject;
27+
$this->resourceType = $resourceType;
28+
$this->className = $className;
29+
}
30+
31+
/**
32+
* @param mixed ...$arguments
33+
* @return mixed
34+
*/
35+
public function instantiate(...$arguments)
36+
{
37+
$object = $this->classObject;
38+
switch ($this->resourceType) {
39+
case ClassResolver::RESOLVED_TYPE_CLASS:
40+
$this->instance = $object(...$arguments);
41+
break;
42+
case ClassResolver::RESOLVED_TYPE_PACKAGES:
43+
$this->instance = new $object(...$arguments);
44+
break;
45+
}
46+
47+
return $this->instance;
48+
}
49+
50+
/**
51+
* @return string
52+
*/
53+
public function getClassName(): string
54+
{
55+
return $this->className;
56+
}
57+
58+
/**
59+
* @return mixed
60+
*/
61+
public function getInstance()
62+
{
63+
return $this->instance;
64+
}
65+
}

src/Core/JavaClassDeferredLoader.php renamed to src/Kernel/Internal/JavaClassDeferredLoader.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
<?php
2-
namespace PHPJava\Core;
2+
namespace PHPJava\Kernel\Internal;
33

4-
class JavaClassDeferredLoader implements JavaClassInterface
4+
use PHPJava\Core\JavaClass;
5+
use PHPJava\Core\JavaClassInterface;
6+
7+
final class JavaClassDeferredLoader implements JavaClassInterface
58
{
69
private $deferLoadingReaderClass;
710
private $arguments = [];

src/Kernel/Mnemonics/_dup.php

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,6 @@ final class _dup implements OperationInterface
1212

1313
public function execute(): void
1414
{
15-
$stack = $this->stacks[sizeof($this->stacks) - 1] ?? null;
16-
if ($stack === null) {
17-
throw new RuntimeException('Stack overflow');
18-
}
19-
$this->pushToOperandStack($stack);
15+
$this->stacks[] = $this->stacks[$this->getCurrentStackIndex()] ?? null;
2016
}
2117
}

src/Kernel/Mnemonics/_invokespecial.php

Lines changed: 36 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
use PHPJava\Exceptions\NotImplementedException;
66
use PHPJava\Exceptions\UnableToCatchException;
77
use PHPJava\Kernel\Attributes\CodeAttribute;
8+
use PHPJava\Kernel\Internal\InstanceDeferredLoader;
89
use PHPJava\Kernel\Structures\_ExceptionTable;
910
use PHPJava\Utilities\AttributionResolver;
1011
use PHPJava\Utilities\BinaryTool;
12+
use PHPJava\Utilities\ClassResolver;
1113
use PHPJava\Utilities\Formatter;
1214
use PHPJava\Utilities\MethodNameResolver;
1315
use PHPJava\Utilities\TypeResolver;
@@ -23,56 +25,49 @@ public function execute(): void
2325
$cpInfo = $this->getConstantPool();
2426
$cp = $cpInfo[$this->readUnsignedShort()];
2527
$nameAndTypeIndex = $cpInfo[$cp->getNameAndTypeIndex()];
28+
$className = $cpInfo[$cpInfo[$cp->getClassIndex()]->getClassIndex()]->getString();
29+
$methodName = $cpInfo[$nameAndTypeIndex->getNameIndex()]->getString();
2630
$signature = $cpInfo[$nameAndTypeIndex->getDescriptorIndex()]->getString();
2731
$parsedSignature = Formatter::parseSignature($signature);
28-
$invokerClass = $this->popFromOperandStack();
29-
30-
31-
$arguments = [];
32-
if (($length = $parsedSignature['arguments_count'] - 1) >= 0) {
33-
$arguments = array_fill(0, $length, null);
34-
for ($i = $length; $i >= 0; $i--) {
35-
$arguments[$i] = $this->popFromOperandStack();
36-
}
37-
}
38-
39-
$methodName = $cpInfo[$nameAndTypeIndex->getNameIndex()]->getString();
4032

41-
if ($this->javaClassInvoker->isInvoked($methodName, $signature)) {
42-
return;
33+
// POP with right-to-left (objectref + arguments)
34+
$arguments = array_fill(0, $parsedSignature['arguments_count'], null);
35+
for ($i = count($arguments) - 1; $i >= 0; $i--) {
36+
$arguments[$i] = $this->popFromOperandStack();
4337
}
4438

45-
$this->javaClassInvoker
46-
->addToSpecialInvokedList($methodName, $signature);
47-
48-
39+
/**
40+
* @var InstanceDeferredLoader $objectref
41+
*/
42+
$objectref = $this->popFromOperandStack();
43+
$newObject = null;
4944
try {
50-
if ($invokerClass instanceof JavaClass) {
51-
if ($invokerClass->getInvoker()->isInvoked($methodName, $signature)) {
52-
return;
45+
$methodName = $cpInfo[$nameAndTypeIndex->getNameIndex()]->getString();
46+
47+
if ($objectref->getClassName() !== $className) {
48+
// If $objectref is not match $className, then change current class (I have no confidence).
49+
// See also: https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-6.html#jvms-6.5.invokespecial
50+
51+
// FIXME: if $objectref has superclass, then refer superclass from $objectref
52+
// NOTE: This implementation is a ** first aid **.
53+
[$resourceType, $classObject] = $objectref
54+
->getOptions('class_resolver')
55+
->resolve($className);
56+
57+
switch ($resourceType) {
58+
case ClassResolver::RESOLVED_TYPE_PACKAGES:
59+
$newObject = new $classObject(...$arguments);
60+
break;
61+
case ClassResolver::RESOLVED_TYPE_CLASS:
62+
$newObject = $classObject(...$arguments);
63+
break;
5364
}
54-
55-
$invokerClass
56-
->getInvoker()
57-
->addToSpecialInvokedList($methodName, $signature);
58-
59-
$result = $invokerClass
60-
->getInvoker()
61-
->getDynamic()
62-
->getMethods()
63-
->call(
64-
$methodName,
65-
...$arguments
66-
);
6765
} else {
68-
$result = call_user_func_array(
69-
[
70-
$invokerClass,
71-
MethodNameResolver::resolve($methodName),
72-
],
73-
$arguments
74-
);
66+
$newObject = $objectref->instantiate(...$arguments);
7567
}
68+
69+
// NOTE: PHP has a problem which a reference object cannot replace to an object.
70+
$this->replaceReferredObject($objectref, $newObject);
7671
} catch (\Exception $e) {
7772
/**
7873
* @var $codeAttribute CodeAttribute
@@ -104,9 +99,5 @@ public function execute(): void
10499
$e
105100
);
106101
}
107-
108-
if ($parsedSignature[0]['type'] !== 'void') {
109-
$this->pushToOperandStack($result);
110-
}
111102
}
112103
}

src/Kernel/Mnemonics/_new.php

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
namespace PHPJava\Kernel\Mnemonics;
33

44
use PHPJava\Exceptions\NotImplementedException;
5+
use PHPJava\Kernel\Internal\InstanceDeferredLoader;
56
use PHPJava\Utilities\BinaryTool;
67
use PHPJava\Utilities\ClassResolver;
78

@@ -19,13 +20,14 @@ public function execute(): void
1920
[$resourceType, $classObject] = $this->getOptions('class_resolver')
2021
->resolve($className, $this->javaClass);
2122

22-
if ($resourceType === ClassResolver::RESOLVED_TYPE_CLASS) {
23-
/**
24-
* @var \PHPJava\Core\JavaClass $classObject
25-
*/
26-
$this->pushToOperandStack($classObject->getInvoker()->construct()->getJavaClass());
27-
return;
28-
}
29-
$this->pushToOperandStack(new $classObject());
23+
$instanceDeferredLoader = new InstanceDeferredLoader(
24+
$classObject,
25+
$resourceType,
26+
$className
27+
);
28+
29+
$this->pushToOperandStackByReference(
30+
$instanceDeferredLoader
31+
);
3032
}
3133
}

0 commit comments

Comments
 (0)