Skip to content

Commit 949dcf7

Browse files
committed
Fix: Call <clinit> delayed, applying default values into fields, catchable finally statement
1 parent db885dc commit 949dcf7

33 files changed

Lines changed: 221 additions & 98 deletions

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"description": "JVM emulator by PHP",
44
"type": "library",
55
"license": "MIT",
6-
"version": "0.0.7.5-dev",
6+
"version": "0.0.8.0-dev",
77
"authors": [
88
{
99
"name": "memory"

src/Core/JVM/DynamicAccessor.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ class DynamicAccessor implements AccessorInterface
2222
/**
2323
* @param PHPJava\Kernel\Structures\_MethodInfo[] $methods
2424
*/
25-
public function __construct(JavaClassInvoker $invoker, array $methods, array $options = [])
25+
public function __construct(JavaClassInvoker $invoker, array $methods, array $fields, array $options = [])
2626
{
2727
$this->methodAccessor = new DynamicMethodInvoker($invoker, $methods, $options);
28-
$this->fieldAccessor = new DynamicField($invoker, []);
28+
$this->fieldAccessor = new DynamicField($invoker, $fields);
2929
}
3030

3131
public function getFields(): FieldInterface
Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
<?php
22
namespace PHPJava\Core\JVM\Field;
33

4-
use PHPJava\Packages\java\lang\_String;
54
use PHPJava\Packages\java\lang\NoSuchFieldException;
65

76
trait FieldGettable
@@ -11,12 +10,10 @@ trait FieldGettable
1110
*/
1211
public function get(string $name)
1312
{
14-
if (!isset($this->fields[$name])) {
15-
throw new NoSuchFieldException('Get to undefined Field ' . $name);
16-
}
17-
if ($this->fields[$name] instanceof _String) {
18-
return (string) $this->fields[$name];
13+
if (!array_key_exists($name, $this->fields)) {
14+
throw new NoSuchFieldException('Get to undefined field ' . $name);
1915
}
16+
2017
return $this->fields[$name];
2118
}
2219
}

src/Core/JVM/Invoker/Invokable.php

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use PHPJava\Core\JVM\Stream\BinaryReader;
1111
use PHPJava\Exceptions\IllegalJavaClassException;
1212
use PHPJava\Exceptions\RuntimeException;
13+
use PHPJava\Exceptions\UnableToFindAttributionException;
1314
use PHPJava\Exceptions\UndefinedMethodException;
1415
use PHPJava\Exceptions\UndefinedOpCodeException;
1516
use PHPJava\Kernel\Attributes\CodeAttribute;
@@ -52,6 +53,8 @@ trait Invokable
5253
*/
5354
private $debugTool;
5455

56+
private $isInstantiatedStaticInitializer = false;
57+
5558
/**
5659
* @param _MethodInfo[] $methods
5760
*/
@@ -114,13 +117,13 @@ function () use ($name, $arguments) {
114117
return $method(...$arguments);
115118
}
116119

117-
$codeAttribute = AttributionResolver::resolve(
118-
$method->getAttributes(),
119-
CodeAttribute::class
120-
);
121-
122-
if ($codeAttribute === null) {
123-
throw new IllegalJavaClassException('Java class does not have code attribute.');
120+
try {
121+
$codeAttribute = AttributionResolver::resolve(
122+
$method->getAttributes(),
123+
CodeAttribute::class
124+
);
125+
} catch (UnableToFindAttributionException $e) {
126+
return null;
124127
}
125128

126129
$handle = fopen(
@@ -129,6 +132,7 @@ function () use ($name, $arguments) {
129132
Runtime::OPERATIONS_TEMPORARY_CODE_STREAM,
130133
'r+'
131134
);
135+
132136
fwrite($handle, $codeAttribute->getCode());
133137
rewind($handle);
134138

@@ -358,13 +362,15 @@ private function findMethod(string $name, ...$arguments): _MethodInfo
358362

359363
if (empty($methodReferences)) {
360364
if (!isset($methodReferences)) {
361-
throw new UndefinedMethodException('Call to undefined method ' . $name . '.');
365+
throw new NoSuchMethodException(
366+
'Call to undefined method ' . $name . '.'
367+
);
362368
}
363369
}
364370

365371
$convertedPassedArguments = $this->stringifyArguments(...$arguments);
366372

367-
$this->debugTool->getLogger()->debug('Passed descriptor is ' . $convertedPassedArguments);
373+
$this->debugTool->getLogger()->debug('Passed descriptor is ' . ($convertedPassedArguments ?: '(none)'));
368374

369375
$method = null;
370376

@@ -388,7 +394,7 @@ private function findMethod(string $name, ...$arguments): _MethodInfo
388394
*/
389395
$methodSignature = Formatter::buildArgumentsSignature($formattedArguments);
390396

391-
$this->debugTool->getLogger()->debug('Find descriptor for ' . $methodSignature);
397+
$this->debugTool->getLogger()->debug('Find descriptor for ' . ($methodSignature ?: '(none)'));
392398

393399
if (!($this->options['validation']['method']['arguments_count_only'] ?? GlobalOptions::get('validation.method.arguments_count_only') ?? Runtime::VALIDATION_METHOD_ARGUMENTS_COUNT_ONLY)) {
394400
if (TypeResolver::compare($methodSignature, $convertedPassedArguments)) {
@@ -421,4 +427,21 @@ function ($argument) {
421427
)
422428
);
423429
}
430+
431+
public function callStaticInitializerIfNotInstantiated(): InvokerInterface
432+
{
433+
if ($this->isInstantiatedStaticInitializer) {
434+
return $this;
435+
}
436+
$this->isInstantiatedStaticInitializer = true;
437+
if ($this->javaClassInvoker->getStatic()->getMethods()->has('<clinit>')) {
438+
$this->javaClassInvoker
439+
->getStatic()
440+
->getMethods()
441+
->call(
442+
'<clinit>'
443+
);
444+
}
445+
return $this;
446+
}
424447
}

src/Core/JVM/Invoker/InvokerInterface.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,6 @@ public function isDynamic(): bool;
2020
public function getList(): array;
2121

2222
public function has(string $name): bool;
23+
24+
public function callStaticInitializerIfNotInstantiated(): InvokerInterface;
2325
}

src/Core/JVM/StaticAccessor.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ class StaticAccessor implements AccessorInterface
2222
/**
2323
* @param PHPJava\Kernel\Structures\_MethodInfo[] $methods
2424
*/
25-
public function __construct(JavaClassInvoker $invoker, array $methods, array $options = [])
25+
public function __construct(JavaClassInvoker $invoker, array $methods, array $fields, array $options = [])
2626
{
2727
$this->methodAccessor = new StaticMethodInvoker($invoker, $methods, $options);
28-
$this->fieldAccessor = new StaticField($invoker, []);
28+
$this->fieldAccessor = new StaticField($invoker, $fields);
2929
}
3030

3131
public function getFields(): FieldInterface

src/Core/JavaClass.php

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -264,15 +264,6 @@ public function __construct(ReaderInterface $reader, array $options = [])
264264
$this,
265265
$options
266266
);
267-
268-
if ($this->invoker->getStatic()->getMethods()->has('<clinit>')) {
269-
$this->invoker
270-
->getStatic()
271-
->getMethods()
272-
->call(
273-
'<clinit>'
274-
);
275-
}
276267
}
277268

278269
public function __invoke(...$arguments): JavaClass

src/Core/JavaClassInterface.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
* @method string getPackageName()
2020
* @method string getClassName(bool $shortName = false)
2121
* @method mixed getOptions($key = null)
22+
* @method mixed __invoke(...$parameters)
2223
*/
2324
interface JavaClassInterface
2425
{

src/Core/JavaClassInvoker.php

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
use PHPJava\Core\JVM\StaticAccessor;
66
use PHPJava\Exceptions\IllegalJavaClassException;
77
use PHPJava\Kernel\Maps\FieldAccessFlag;
8+
use PHPJava\Kernel\Maps\MethodAccessFlag;
89
use PHPJava\Kernel\Provider\ProviderInterface;
910
use PHPJava\Kernel\Structures\_FieldInfo;
1011
use PHPJava\Kernel\Structures\_MethodInfo;
12+
use PHPJava\Utilities\Normalizer;
1113

1214
class JavaClassInvoker
1315
{
@@ -17,22 +19,22 @@ class JavaClassInvoker
1719
private $javaClass;
1820

1921
/**
20-
* @var _MethodInfo
22+
* @var _MethodInfo[]
2123
*/
2224
private $dynamicMethods = [];
2325

2426
/**
25-
* @var _MethodInfo
27+
* @var _MethodInfo[]
2628
*/
2729
private $staticMethods = [];
2830

2931
/**
30-
* @var _FieldInfo
32+
* @var _FieldInfo[]
3133
*/
3234
private $dynamicFields = [];
3335

3436
/**
35-
* @var _FieldInfo
37+
* @var _FieldInfo[]
3638
*/
3739
private $staticFields = [];
3840

@@ -71,34 +73,45 @@ public function __construct(
7173
$cpInfo = $javaClass->getConstantPool();
7274

7375
foreach ($javaClass->getDefinedMethods() as $methodInfo) {
76+
/**
77+
* @var _MethodInfo $methodInfo
78+
*/
7479
$methodName = $cpInfo[$methodInfo->getNameIndex()]->getString();
7580

76-
if (($methodInfo->getAccessFlag() & FieldAccessFlag::ACC_STATIC) !== 0) {
81+
if (($methodInfo->getAccessFlag() & MethodAccessFlag::ACC_STATIC) !== 0) {
7782
$this->staticMethods[$methodName][] = $methodInfo;
78-
} elseif ($methodInfo->getAccessFlag() === 0 || ($methodInfo->getAccessFlag() & FieldAccessFlag::ACC_PUBLIC) !== 0) {
83+
} else {
7984
$this->dynamicMethods[$methodName][] = $methodInfo;
8085
}
8186
}
8287

8388
foreach ($javaClass->getDefinedFields() as $fieldInfo) {
89+
/**
90+
* @var _FieldInfo $fieldInfo
91+
*/
8492
$fieldName = $cpInfo[$fieldInfo->getNameIndex()]->getString();
8593

86-
if ($fieldInfo->getAccessFlag() === 0) {
87-
$this->dynamicFields[$fieldName] = $fieldInfo;
88-
} elseif (($fieldInfo->getAccessFlag() & FieldAccessFlag::ACC_STATIC) !== 0) {
94+
if (($fieldInfo->getAccessFlag() & FieldAccessFlag::ACC_STATIC) !== 0) {
8995
$this->staticFields[$fieldName] = $fieldInfo;
96+
} else {
97+
$this->dynamicFields[$fieldName] = $fieldInfo;
9098
}
9199
}
92100

93101
$this->dynamicAccessor = new DynamicAccessor(
94102
$this,
95103
$this->dynamicMethods,
104+
[],
96105
$this->options
97106
);
98107

99108
$this->staticAccessor = new StaticAccessor(
100109
$this,
101110
$this->staticMethods,
111+
Normalizer::normalizeFields(
112+
$this->staticFields,
113+
$this->javaClass
114+
),
102115
$this->options
103116
);
104117
}
@@ -111,15 +124,17 @@ public function construct(...$arguments): self
111124
$this->dynamicAccessor = new DynamicAccessor(
112125
$this,
113126
$this->dynamicMethods,
127+
Normalizer::normalizeFields(
128+
$this->dynamicFields,
129+
$this->javaClass
130+
),
114131
$this->options
115132
);
116133

117-
if (isset($this->dynamicMethods['<init>'])) {
118-
$this->getDynamic()->getMethods()->call(
119-
'<init>',
120-
...$arguments
121-
);
122-
}
134+
$this->getDynamic()->getMethods()->call(
135+
'<init>',
136+
...$arguments
137+
);
123138

124139
return $this;
125140
}
@@ -136,18 +151,11 @@ public function getDynamic(): DynamicAccessor
136151

137152
public function getStatic(): StaticAccessor
138153
{
139-
return $this->staticAccessor;
140-
}
141-
142-
public function isInvoked(string $name, string $signature): bool
143-
{
144-
return in_array($signature, $this->specialInvoked[$name] ?? [], true);
145-
}
154+
$this->staticAccessor
155+
->getMethods()
156+
->callStaticInitializerIfNotInstantiated();
146157

147-
public function addToSpecialInvokedList(string $name, string $signature): self
148-
{
149-
$this->specialInvoked[$name][] = $signature;
150-
return $this;
158+
return $this->staticAccessor;
151159
}
152160

153161
/**

src/Core/PHPJava.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@ final class PHPJava
1212
/**
1313
* As same as composer version.
1414
*/
15-
const VERSION = 0x000075;
15+
const VERSION = 0x000080;
1616
}

0 commit comments

Comments
 (0)