Skip to content

Commit a268383

Browse files
committed
Implement the invokeinterface operation, And fix the invokedynamic operation
1 parent 88b0aae commit a268383

8 files changed

Lines changed: 209 additions & 21 deletions

File tree

src/Core/JVM/Parameters/Runtime.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ final class Runtime
2727
'SourceFile',
2828
'InnerClasses',
2929
'BootstrapMethods',
30+
'NestMembers',
3031
];
3132

3233
const PHP_PACKAGES_MAPS = [

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: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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+
32+
}

src/Kernel/Internal/Lambda.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
namespace PHPJava\Kernel\Internal;
4+
5+
use PHPJava\Core\JavaClass;
6+
7+
class Lambda
8+
{
9+
private $javaClass;
10+
private $name;
11+
private $descriptor;
12+
private $class;
13+
14+
public function __construct(JavaClass $javaClass, string $name, string $descriptor, string $class)
15+
{
16+
$this->javaClass = $javaClass;
17+
$this->name = $name;
18+
$this->descriptor = $descriptor;
19+
$this->class = $class;
20+
}
21+
22+
public function __invoke(...$arguments)
23+
{
24+
var_dump($arguments);
25+
exit();
26+
}
27+
}

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, $methodHandledName],
115-
array_merge(
116-
[
117-
MethodHandles::lookup(),
118-
$name,
119-
$methodHandleType,
120-
],
121-
$bootstrapMethodArguments,
122-
$arguments
123-
)
136+
[$factoryClass, $methodHandledName],
137+
$arguments
124138
);
125139

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

src/Kernel/Mnemonics/_invokeinterface.php

Lines changed: 26 additions & 0 deletions
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
{
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_slice($collection, 1);
35+
36+
37+
var_dump($objectref(...$arguments));exit();
38+
var_dump($name, $descriptor);
39+
exit();
1440
throw new NotImplementedException(__CLASS__);
1541
}
1642
}

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

Lines changed: 81 additions & 4 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,9 +74,74 @@ 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-
var_dump(func_get_args());
68-
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+
//
109+
// [$resourceType, $classObject] = $javaClass->getOptions('class_resolver')
110+
// ->resolve($class);
111+
//
112+
// switch ($resourceType) {
113+
// case ClassResolver::RESOLVED_TYPE_PACKAGES:
114+
// break;
115+
// case ClassResolver::RESOLVED_TYPE_CLASS:
116+
// /**
117+
// * @var JavaClass $classObject
118+
// */
119+
// break;
120+
// }
121+
122+
/**
123+
* @var NestMembersAttribute $nestMembersAttribute
124+
*/
125+
// $nestMembersAttribute = AttributionResolver::resolve(
126+
// $javaClass->getAttributes(),
127+
// NestMembersAttribute::class
128+
// );
129+
130+
131+
132+
// /**
133+
// * @var _Class $class
134+
// */
135+
// foreach ($nestMembersAttribute->getClasses() as $class) {
136+
// var_dump($cp[$class->getClassIndex()]);
137+
// }
138+
139+
// The Lambda class.
140+
return new Lambda(
141+
$javaClass,
142+
$lambdaName,
143+
$lambdaDescriptor,
144+
$class
145+
);
69146
}
70147
}

src/Utilities/Formatter.php

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010

1111
class Formatter
1212
{
13+
const BUILT_IN_PACKAGE = 0;
14+
const USER_DEFINED_PACKAGE = 1;
15+
1316
/**
1417
* @param $signature
1518
* @param int $i
@@ -139,9 +142,9 @@ public static function convertPHPNamespacesToJava($className): string
139142

140143
/**
141144
* @param $className
142-
* @return string
145+
* @return array
143146
*/
144-
public static function convertJavaNamespaceToPHP($className): string
147+
public static function convertJavaNamespaceToPHP($className): array
145148
{
146149
$className = str_replace('.', '/', $className);
147150
$newClassName = explode(
@@ -153,7 +156,12 @@ public static function convertJavaNamespaceToPHP($className): string
153156
$newClassName[$key] = Runtime::PHP_PACKAGES_MAPS[$value] ?? $value;
154157
}
155158

156-
return Runtime::PHP_PACKAGES_DIRECTORY . '\\' . implode('\\', $newClassName);
159+
$newClassName = explode('$', implode('\\', $newClassName));
160+
$inPackage = Runtime::PHP_PACKAGES_DIRECTORY . '\\' . $newClassName[0];
161+
if (class_exists($inPackage)) {
162+
return [static::BUILT_IN_PACKAGE, $inPackage];
163+
}
164+
return [static::USER_DEFINED_PACKAGE, implode('$', $newClassName)];
157165
}
158166

159167
/**

0 commit comments

Comments
 (0)