Skip to content

Commit e4b2ded

Browse files
authored
Merge pull request #127 from php-java/deal-with-jdk-11
[TRIAL] Implement the `invokedynamic` operation.
2 parents da37ba0 + fa3791b commit e4b2ded

36 files changed

+3463
-12
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,6 @@
3737
"friendsofphp/php-cs-fixer": "^2.14"
3838
},
3939
"scripts": {
40-
"tests": "phpunit tests --stop-on-failure && phpcs --standard=phpcs.xml src"
40+
"tests": "phpunit tests --stop-on-failure && phpcs -n --standard=phpcs.xml src"
4141
}
4242
}

src/Core/JVM/Cache/OperationCache.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,4 @@
33

44
class OperationCache extends Cache
55
{
6-
76
}

src/Core/JVM/Intern/InternInterface.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,4 @@
33

44
interface InternInterface
55
{
6-
76
}

src/Core/JVM/Parameters/Runtime.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ final class Runtime
2525
'Code',
2626
'Exceptions',
2727
'SourceFile',
28+
'InnerClasses',
29+
'BootstrapMethods',
2830
];
2931

3032
const PHP_PACKAGES_MAPS = [

src/Core/JavaClass.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,11 @@ public function getSuperClass()
316316
return $this->superClass;
317317
}
318318

319+
public function getAttributes(): array
320+
{
321+
return $this->attributePool->getEntries();
322+
}
323+
319324
public function debug(): void
320325
{
321326
$isEnabledTrace = $this->options['operations']['enable_trace'] ?? GlobalOptions::get('operations.enable_trace') ?? Runtime::OPERATIONS_ENABLE_TRACE;

src/Kernel/Attributes/BootstrapMethodsAttribute.php

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
namespace PHPJava\Kernel\Attributes;
33

44
use PHPJava\Exceptions\NotImplementedException;
5+
use PHPJava\Kernel\Structures\_BootstrapMethod;
56
use PHPJava\Utilities\BinaryTool;
67

78
final class BootstrapMethodsAttribute implements AttributeInterface
@@ -11,8 +12,24 @@ final class BootstrapMethodsAttribute implements AttributeInterface
1112
use \PHPJava\Kernel\Core\AttributeReference;
1213
use \PHPJava\Kernel\Core\DebugTool;
1314

15+
private $bootstrapMethods = [];
16+
1417
public function execute(): void
1518
{
16-
throw new NotImplementedException(__CLASS__);
19+
$numBootstrapMethods = $this->readUnsignedShort();
20+
21+
for ($i = 0; $i < $numBootstrapMethods; $i++) {
22+
$bootstrapMethod = new _BootstrapMethod($this->reader);
23+
$bootstrapMethod
24+
->setDebugTool($this->getDebugTool())
25+
->setConstantPool($this->getConstantPool())
26+
->execute();
27+
$this->bootstrapMethods[] = $bootstrapMethod;
28+
}
29+
}
30+
31+
public function getBootstrapMethods()
32+
{
33+
return $this->bootstrapMethods;
1734
}
1835
}

src/Kernel/Attributes/StackMapTableAttribute.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ final class StackMapTableAttribute implements AttributeInterface
1212
use \PHPJava\Kernel\Core\DebugTool;
1313

1414
private $numberOfEntries = null;
15-
private $stackMapFrames = array();
15+
private $stackMapFrames = [];
1616
public function execute(): void
1717
{
1818
$this->numberOfEntries = $this->readUnsignedShort();
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
namespace PHPJava\Kernel\Maps;
3+
4+
class MethodHandleKind extends Map
5+
{
6+
const REF_getField = 0x0001;
7+
const REF_getStatic = 0x0002;
8+
const REF_putField = 0x0003;
9+
const REF_putStatic = 0x0004;
10+
const REF_invokeVirtual = 0x0005;
11+
const REF_invokeStatic = 0x0006;
12+
const REF_invokeSpecial = 0x0007;
13+
const REF_newInvokeSpecial = 0x0008;
14+
const REF_invokeInterface = 0x0009;
15+
}
Lines changed: 123 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,138 @@
11
<?php
22
namespace PHPJava\Kernel\Mnemonics;
33

4+
use PHPJava\Core\JVM\Parameters\Runtime;
45
use PHPJava\Exceptions\NotImplementedException;
6+
use PHPJava\Packages\java\lang\invoke\MethodHandles;
7+
use PHPJava\Packages\java\lang\invoke\MethodHandles\Lookup;
8+
use PHPJava\Kernel\Attributes\BootstrapMethodsAttribute;
9+
use PHPJava\Kernel\Maps\MethodHandleKind;
10+
use PHPJava\Kernel\Structures\_BootstrapMethod;
11+
use PHPJava\Kernel\Structures\_MethodHandle;
12+
use PHPJava\Kernel\Structures\_NameAndType;
13+
use PHPJava\Packages\java\lang\invoke\MethodType;
14+
use PHPJava\Utilities\AttributionResolver;
515
use PHPJava\Utilities\BinaryTool;
16+
use PHPJava\Utilities\Formatter;
17+
use PHPJava\Utilities\Normalizer;
618

719
final class _invokedynamic implements OperationInterface
820
{
921
use \PHPJava\Kernel\Core\Accumulator;
1022
use \PHPJava\Kernel\Core\ConstantPool;
1123

24+
/**
25+
* `invokedynamic` is a trial implementation.
26+
*
27+
* @throws NotImplementedException
28+
* @throws \PHPJava\Exceptions\NormalizerException
29+
* @throws \PHPJava\Exceptions\TypeException
30+
* @throws \PHPJava\Exceptions\UnableToFindAttributionException
31+
* @see https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-6.html#jvms-6.5.invokedynamic
32+
*/
1233
public function execute(): void
1334
{
14-
throw new NotImplementedException(__CLASS__);
35+
$cp = $this->getConstantPool();
36+
/**
37+
* @var \PHPJava\Kernel\Structures\_InvokeDynamic $invokeDynamicStructure
38+
*/
39+
$invokeDynamicStructure = $cp[$this->readUnsignedShort()];
40+
41+
// NOTE: The values of the third and fourth operand bytes must always be zero.
42+
$thirdByte = $this->read();
43+
$forthByte = $this->read();
44+
45+
/**
46+
* @var _NameAndType $nameAndTypeIndex
47+
*/
48+
$nameAndTypeIndex = $cp[$invokeDynamicStructure->getNameAndTypeIndex()];
49+
50+
$name = $cp[$nameAndTypeIndex->getNameIndex()]->getString();
51+
$signature = Formatter::parseSignature($cp[$nameAndTypeIndex->getDescriptorIndex()]->getString());
52+
53+
$arguments = [];
54+
if (($length = $signature['arguments_count'] - 1) >= 0) {
55+
$arguments = array_fill(0, $length, null);
56+
for ($i = $length; $i >= 0; $i--) {
57+
$arguments[$i] = $this->popFromOperandStack();
58+
}
59+
60+
$arguments = Normalizer::normalizeValues(
61+
$arguments,
62+
$signature['arguments']
63+
);
64+
}
65+
66+
/**
67+
* @var BootstrapMethodsAttribute $bootstrapMethodAttribute
68+
*/
69+
$bootstrapMethodAttribute = AttributionResolver::resolve(
70+
// NOTE: bootstrapMethods attribution is defined in JavaClass class.
71+
$this->javaClass->getAttributes(),
72+
BootstrapMethodsAttribute::class
73+
);
74+
75+
/**
76+
* @var _BootstrapMethod $bootstrapMethod
77+
*/
78+
$bootstrapMethod = $bootstrapMethodAttribute->getBootstrapMethods()[$invokeDynamicStructure->getBootstrapMethodAttrIndex()];
79+
$bootstrapMethodArguments = $bootstrapMethod->getBootstrapArguments();
80+
81+
/**
82+
* @var _MethodHandle $methodHandle
83+
*/
84+
$methodHandle = $cp[$bootstrapMethod->getBootstrapMethodRef()];
85+
switch ($methodHandle->getReferenceKind()) {
86+
case MethodHandleKind::REF_getField:
87+
throw new NotImplementedException($methodHandle->getReferenceKind() . ' does not implemented in ' . __METHOD__);
88+
case MethodHandleKind::REF_getStatic:
89+
throw new NotImplementedException($methodHandle->getReferenceKind() . ' does not implemented in ' . __METHOD__);
90+
case MethodHandleKind::REF_putField:
91+
throw new NotImplementedException($methodHandle->getReferenceKind() . ' does not implemented in ' . __METHOD__);
92+
case MethodHandleKind::REF_putStatic:
93+
throw new NotImplementedException($methodHandle->getReferenceKind() . ' does not implemented in ' . __METHOD__);
94+
case MethodHandleKind::REF_invokeVirtual:
95+
throw new NotImplementedException($methodHandle->getReferenceKind() . ' does not implemented in ' . __METHOD__);
96+
case MethodHandleKind::REF_invokeStatic:
97+
$factory = Runtime::PHP_PACKAGES_DIRECTORY . '\\' . str_replace(
98+
'/',
99+
'\\',
100+
$cp[$cp[$cp[$methodHandle->getReferenceIndex()]->getClassIndex()]->getClassIndex()]->getString()
101+
);
102+
$methodHandleLookup = new Lookup();
103+
104+
$methodHandleNameAndTypeConstant = $cp[$cp[$methodHandle->getReferenceIndex()]->getNameAndTypeIndex()];
105+
$methodHandledName = $cp[$methodHandleNameAndTypeConstant->getNameIndex()]->getString();
106+
$methodHandledDescriptor = $cp[$methodHandleNameAndTypeConstant->getDescriptorIndex()]->getString();
107+
108+
// NOTE: Must be a class name.
109+
$className = Formatter::convertJavaNamespaceToPHP($signature[0]['class_name']);
110+
111+
$methodHandleType = MethodType::methodType($className);
112+
113+
$result = forward_static_call_array(
114+
[$factory, $name],
115+
array_merge(
116+
[
117+
MethodHandles::lookup(),
118+
$methodHandledName,
119+
$methodHandleType,
120+
$bootstrapMethodArguments[0]->getStringObject()
121+
],
122+
$arguments
123+
)
124+
);
125+
126+
if ($signature[0]['type'] !== 'void') {
127+
$this->pushToOperandStack($result);
128+
}
129+
break;
130+
case MethodHandleKind::REF_invokeSpecial:
131+
throw new NotImplementedException($methodHandle->getReferenceKind() . ' does not implemented in ' . __METHOD__);
132+
case MethodHandleKind::REF_newInvokeSpecial:
133+
throw new NotImplementedException($methodHandle->getReferenceKind() . ' does not implemented in ' . __METHOD__);
134+
case MethodHandleKind::REF_invokeInterface:
135+
throw new NotImplementedException($methodHandle->getReferenceKind() . ' does not implemented in ' . __METHOD__);
136+
}
15137
}
16138
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
namespace PHPJava\Kernel\Structures;
3+
4+
use PHPJava\Exceptions\NotImplementedException;
5+
use PHPJava\Utilities\BinaryTool;
6+
7+
class _BootstrapMethod implements StructureInterface
8+
{
9+
use \PHPJava\Kernel\Core\BinaryReader;
10+
use \PHPJava\Kernel\Core\ConstantPool;
11+
use \PHPJava\Kernel\Core\DebugTool;
12+
13+
private $bootstrapMethodRef = null;
14+
private $numBootstrapArguments = 0;
15+
private $bootstrapArguments = [];
16+
17+
public function execute(): void
18+
{
19+
$this->bootstrapMethodRef = $this->readUnsignedShort();
20+
$this->numBootstrapArguments = $this->readUnsignedShort();
21+
22+
$cp = $this->getConstantPool();
23+
for ($i = 0; $i < $this->numBootstrapArguments; $i++) {
24+
$this->bootstrapArguments[] = $cp[$cp[$this->readUnsignedShort()]->getStringIndex()];
25+
}
26+
}
27+
28+
public function getBootstrapMethodRef(): int
29+
{
30+
return $this->bootstrapMethodRef;
31+
}
32+
33+
public function getBootstrapArguments(): array
34+
{
35+
return $this->bootstrapArguments;
36+
}
37+
}

0 commit comments

Comments
 (0)