Skip to content

Commit 89d2609

Browse files
authored
Merge pull request #130 from php-java/implement-invokeinterface
[TRIAL] Implement the `invokeinterface` operation, And fix the `invokedynamic` operation
2 parents e4b2ded + 1e640a4 commit 89d2609

File tree

14 files changed

+269
-25
lines changed

14 files changed

+269
-25
lines changed

src/Core/JVM/ConstantPool.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use PHPJava\Kernel\Structures\_Long;
1616
use PHPJava\Kernel\Structures\_MethodHandle;
1717
use PHPJava\Kernel\Structures\_Methodref;
18+
use PHPJava\Kernel\Structures\_MethodType;
1819
use PHPJava\Kernel\Structures\_NameAndType;
1920
use PHPJava\Kernel\Structures\_String;
2021
use PHPJava\Kernel\Structures\_Utf8;
@@ -85,6 +86,7 @@ private function read($entryTag): ?StructureInterface
8586
case ConstantPoolTag::CONSTANT_MethodHandle:
8687
return new _MethodHandle($this->reader);
8788
case ConstantPoolTag::CONSTANT_MethodType:
89+
return new _MethodType($this->reader);
8890
case ConstantPoolTag::CONSTANT_Module:
8991
case ConstantPoolTag::CONSTANT_Package:
9092
throw new ReadEntryException('Entry tag ' . sprintf('0x%04X', $entryTag) . ' is not implemented.');

src/Core/JavaClass.php

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

240-
public function getOptions()
240+
public function getOptions($key = null)
241241
{
242+
if (isset($key)) {
243+
return $this->options[$key];
244+
}
242245
return $this->options;
243246
}
244247

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
namespace PHPJava\Kernel\Attributes;
3+
4+
use PHPJava\Exceptions\NotImplementedException;
5+
use PHPJava\Kernel\Structures\_LocalVariableTable;
6+
use PHPJava\Utilities\BinaryTool;
7+
8+
final class NestMembersAttribute implements AttributeInterface
9+
{
10+
use \PHPJava\Kernel\Core\BinaryReader;
11+
use \PHPJava\Kernel\Core\ConstantPool;
12+
use \PHPJava\Kernel\Core\AttributeReference;
13+
use \PHPJava\Kernel\Core\DebugTool;
14+
15+
private $classes = [];
16+
17+
public function execute(): void
18+
{
19+
$numberOfClasses = $this->readUnsignedShort();
20+
$cp = $this->getConstantPool();
21+
22+
for ($i = 0; $i < $numberOfClasses; $i++) {
23+
$this->classes[] = $cp[$this->readUnsignedShort()];
24+
}
25+
}
26+
27+
public function getClasses(): array
28+
{
29+
return $this->classes;
30+
}
31+
}

src/Kernel/Internal/Lambda.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
namespace PHPJava\Kernel\Internal;
4+
5+
use PHPJava\Core\JavaClass;
6+
use PHPJava\Utilities\ClassResolver;
7+
8+
class Lambda
9+
{
10+
private $javaClass;
11+
private $name;
12+
private $descriptor;
13+
private $class;
14+
private $resourceType;
15+
private $classObject;
16+
17+
public function __construct(JavaClass $javaClass, string $name, string $descriptor, string $class)
18+
{
19+
$this->javaClass = $javaClass;
20+
$this->name = $name;
21+
$this->descriptor = $descriptor;
22+
$this->class = $class;
23+
24+
[$this->resourceType, $this->classObject] = $javaClass
25+
->getOptions('class_resolver')
26+
->resolve($this->class);
27+
}
28+
29+
public function __invoke(...$arguments)
30+
{
31+
return $this->javaClass
32+
->getInvoker()
33+
->getStatic()
34+
->getMethods()
35+
->call(
36+
$this->name,
37+
...$arguments
38+
);
39+
}
40+
}

src/Kernel/Mnemonics/_invokedynamic.php

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ final class _invokedynamic implements OperationInterface
2020
{
2121
use \PHPJava\Kernel\Core\Accumulator;
2222
use \PHPJava\Kernel\Core\ConstantPool;
23+
use \PHPJava\Kernel\Core\DependencyInjector;
2324

2425
/**
2526
* `invokedynamic` is a trial implementation.
@@ -94,7 +95,7 @@ public function execute(): void
9495
case MethodHandleKind::REF_invokeVirtual:
9596
throw new NotImplementedException($methodHandle->getReferenceKind() . ' does not implemented in ' . __METHOD__);
9697
case MethodHandleKind::REF_invokeStatic:
97-
$factory = Runtime::PHP_PACKAGES_DIRECTORY . '\\' . str_replace(
98+
$factoryClass = Runtime::PHP_PACKAGES_DIRECTORY . '\\' . str_replace(
9899
'/',
99100
'\\',
100101
$cp[$cp[$cp[$methodHandle->getReferenceIndex()]->getClassIndex()]->getClassIndex()]->getString()
@@ -106,21 +107,34 @@ public function execute(): void
106107
$methodHandledDescriptor = $cp[$methodHandleNameAndTypeConstant->getDescriptorIndex()]->getString();
107108

108109
// NOTE: Must be a class name.
109-
$className = Formatter::convertJavaNamespaceToPHP($signature[0]['class_name']);
110+
$methodHandleType = MethodType::methodType($signature[0]['class_name']);
111+
112+
$reflectionClass = new \ReflectionClass($factoryClass);
113+
$methodAccessor = $reflectionClass->getMethod($methodHandledName);
114+
115+
$arguments = array_merge(
116+
[
117+
MethodHandles::lookup(),
118+
$name,
119+
$methodHandleType,
120+
],
121+
$bootstrapMethodArguments,
122+
$arguments
123+
);
110124

111-
$methodHandleType = MethodType::methodType($className);
125+
if ($document = $methodAccessor->getDocComment()) {
126+
$prependInjections = $this->getAnnotateInjections($document);
127+
if (!empty($prependInjections)) {
128+
array_unshift(
129+
$arguments,
130+
...$prependInjections
131+
);
132+
}
133+
}
112134

113135
$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-
)
136+
[$factoryClass, $methodHandledName],
137+
$arguments
124138
);
125139

126140
if ($signature[0]['type'] !== 'void') {

src/Kernel/Mnemonics/_invokeinterface.php

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
use PHPJava\Exceptions\NotImplementedException;
55
use PHPJava\Utilities\BinaryTool;
6+
use PHPJava\Utilities\Formatter;
67

78
final class _invokeinterface implements OperationInterface
89
{
@@ -11,6 +12,31 @@ final class _invokeinterface implements OperationInterface
1112

1213
public function execute(): void
1314
{
14-
throw new NotImplementedException(__CLASS__);
15+
$cp = $this->getConstantPool();
16+
$index = $this->readUnsignedShort();
17+
$count = $this->readUnsignedByte();
18+
19+
// NOTE: The value of the fourth operand byte must always be zero.
20+
$this->readByte();
21+
22+
$interfaceMethodRef = $cp[$cp[$index]->getNameAndTypeIndex()];
23+
$name = $cp[$interfaceMethodRef->getNameIndex()]->getString();
24+
$descriptor = $cp[$interfaceMethodRef->getDescriptorIndex()]->getString();
25+
26+
$signature = Formatter::parseSignature($descriptor);
27+
// POP with right-to-left (objectref + arguments)
28+
$collection = array_fill(0, $signature['arguments_count'] + 1, null);
29+
for ($i = count($collection) - 1; $i >= 0; $i--) {
30+
$collection[$i] = $this->popFromOperandStack();
31+
}
32+
33+
$objectref = $collection[0];
34+
$arguments = array_values(array_slice($collection, 1));
35+
36+
$result = $objectref(...$arguments);
37+
38+
if ($signature[0] !== 'void') {
39+
$this->pushToOperandStack($result);
40+
}
1541
}
1642
}

src/Kernel/Structures/_BootstrapMethod.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public function execute(): void
2121

2222
$cp = $this->getConstantPool();
2323
for ($i = 0; $i < $this->numBootstrapArguments; $i++) {
24-
$this->bootstrapArguments[] = $cp[$cp[$this->readUnsignedShort()]->getStringIndex()];
24+
$this->bootstrapArguments[] = $cp[$this->readUnsignedShort()];
2525
}
2626
}
2727

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
namespace PHPJava\Kernel\Structures;
3+
4+
use PHPJava\Exceptions\NotImplementedException;
5+
use PHPJava\Utilities\BinaryTool;
6+
7+
class _MethodType implements StructureInterface
8+
{
9+
use \PHPJava\Kernel\Core\BinaryReader;
10+
use \PHPJava\Kernel\Core\ConstantPool;
11+
use \PHPJava\Kernel\Core\DebugTool;
12+
13+
private $descriptorIndex;
14+
15+
public function execute(): void
16+
{
17+
$this->descriptorIndex = $this->readUnsignedShort();
18+
}
19+
20+
public function getDescriptorIndex(): int
21+
{
22+
return $this->descriptorIndex;
23+
}
24+
}

src/Kernel/Structures/_String.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public function execute(): void
1515
{
1616
$this->stringIndex = $this->readUnsignedShort();
1717
}
18+
1819
public function getStringIndex()
1920
{
2021
return $this->stringIndex;

src/Packages/java/lang/invoke/LambdaMetafactory.php

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
<?php
22
namespace PHPJava\Packages\java\lang\invoke;
33

4+
use PHPJava\Core\JavaClass;
5+
use PHPJava\Core\JVM\ConstantPool;
46
use PHPJava\Exceptions\NotImplementedException;
7+
use PHPJava\Kernel\Attributes\NestMembersAttribute;
8+
use PHPJava\Kernel\Internal\Lambda;
9+
use PHPJava\Kernel\Structures\_Class;
10+
use PHPJava\Kernel\Structures\_MethodHandle;
511
use PHPJava\Packages\java\lang\_Object;
12+
use PHPJava\Packages\java\lang\invoke\MethodHandles\Lookup;
13+
use PHPJava\Utilities\AttributionResolver;
14+
use PHPJava\Utilities\ClassResolver;
615

716
/**
817
* The `LambdaMetafactory` class was auto generated.
@@ -52,6 +61,9 @@ public static function altMetafactory($a = null, $b = null, $c = null, $d = null
5261
/**
5362
* Facilitates the creation of simple "function objects" that implement one or more interfaces by delegation to a provided MethodHandle, after appropriate type adaptation and partial evaluation of arguments.
5463
*
64+
* @native JavaClass
65+
* @native ConstantPool
66+
* @param JavaClass $javaClass
5567
* @param mixed $a
5668
* @param mixed $b
5769
* @param mixed $c
@@ -62,8 +74,43 @@ public static function altMetafactory($a = null, $b = null, $c = null, $d = null
6274
* @throws NotImplementedException
6375
* @see https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/package-summary.html#metafactory
6476
*/
65-
public static function metafactory($a = null, $b = null, $c = null, $d = null, $e = null, $f = null)
66-
{
67-
throw new NotImplementedException(__METHOD__);
77+
public static function metafactory(
78+
JavaClass $javaClass,
79+
ConstantPool $cp,
80+
$a = null,
81+
$b = null,
82+
$c = null,
83+
$d = null,
84+
$e = null,
85+
$f = null
86+
) {
87+
/**
88+
* @var Lookup $a
89+
* @var string $invokedName
90+
* @var MethodType $invokedType
91+
* @var MethodType $samMethodType
92+
* @var _MethodHandle $implMethod
93+
* @var MethodType $instantiatedMethodType
94+
*/
95+
$caller = $a;
96+
$invokedName = $b;
97+
$invokedType = $c;
98+
$samMethodType = $d;
99+
$implMethod = $e;
100+
$instantiatedMethodType = $f;
101+
102+
$class = $invokedType->returnType();
103+
104+
$lambdaInfo = $cp[$cp[$implMethod->getReferenceIndex()]->getNameAndTypeIndex()];
105+
$lambdaName = $cp[$lambdaInfo->getNameIndex()]->getString();
106+
$lambdaDescriptor = $cp[$lambdaInfo->getDescriptorIndex()]->getString();
107+
108+
// Create a lambda class.
109+
return new Lambda(
110+
$javaClass,
111+
$lambdaName,
112+
$lambdaDescriptor,
113+
$class
114+
);
68115
}
69116
}

0 commit comments

Comments
 (0)