Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,80 @@ $dynamicMethodAccessor
echo $result;
```

### Call ambiguous method into Java from PHP
- PHP types are ambiguous than Java.
- You may want to call a methods in PHP that contain long type in Java.
In its case, you can call a method as follows:

#### ex. ) [Recommended] Wrap with `\PHPJava\Kernel\Types\_Long`.
##### In Java
```java
class Test
{
public static void includingLongTypeParameter(long n)
{
System.out.println(n);
}

}
```

##### In PHP
```php
<?php
$javaClass->getInvoker()->getStatic()->getMethods()->call(
'includingLongTypeParameter',
new \PHPJava\Kernel\Types\_Long(1234)
);
```

The example will return `1234`.


#### ex. ) Set `false` to strict mode within options.
##### In PHP
```php
<?php
use PHPJava\Core\JavaClass;
use PHPJava\Core\JavaClassFileReader;

$javaClass = new JavaClass(
new JavaClassFileReader('Test'),
[
'strict' => false,
]
);
```

### Runtime options
- Available options on `JavaClass` or `JavaArchive` is below:

|Options | Value | Default | Description |Targeted |
|:-------------:|:-------------:|:-------------:|:-------------:|:-------------:|
| entrypoint | ?string | null | Specify to run entrypoint in JAR. | JavaArchive |
| max_stack_exceeded | integer | 9999 | Execute more than the specified number of times be stopped the operation. | JavaClass |
| strict | boolean | true | If strict mode is `true` then execute method, variables and so on with strict. But if strict mode is `false` then execute ambiguously method, variable and etc in PHPJava. | Both |
| validation.method.arguments_count_only | boolean | false | If this mode `true` then ClassResolver validate arguments size only. | JavaClass |

- For example:
```php
<?php
use PHPJava\Core\JavaClass;
use PHPJava\Core\JavaClassFileReader;

$javaClass = new JavaClass(
new JavaClassFileReader('Test'),
[
'max_stack_exceeded' => 12345,
'validation' => [
'method' => [
'arguments_count_only' => true,
],
],
]
);
```

### Output PHPJava operations

- Output debug trace as follows if you want to show operation log:
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"description": "JVM emulator by PHP",
"type": "library",
"license": "MIT",
"version": "0.0.4.5-dev",
"version": "0.0.5.0-dev",
"authors": [
{
"name": "memory"
Expand Down
4 changes: 2 additions & 2 deletions src/Core/JVM/DynamicAccessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ class DynamicAccessor implements AccessorInterface
*/
private $methodAccessor;

public function __construct(JavaClassInvoker $invoker, array $methods)
public function __construct(JavaClassInvoker $invoker, array $methods, array $options = [])
{
$this->methodAccessor = new DynamicMethodInvoker($invoker, $methods);
$this->methodAccessor = new DynamicMethodInvoker($invoker, $methods, $options);
$this->fieldAccessor = new DynamicField($invoker, []);
}

Expand Down
45 changes: 36 additions & 9 deletions src/Core/JVM/Invoker/Invokable.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,19 @@
use PHPJava\Utilities\Formatter;
use PHPJava\Utilities\SuperClassResolver;
use PHPJava\Utilities\TypeResolver;
use PHPJava\Core\JVM\Parameters\Runtime;

trait Invokable
{
private $javaClassInvoker;
private $methods = [];
private $options = [];

public function __construct(JavaClassInvoker $javaClassInvoker, array $methods)
public function __construct(JavaClassInvoker $javaClassInvoker, array $methods, array $options = [])
{
$this->javaClassInvoker = $javaClassInvoker;
$this->methods = $methods;
$this->options = $options;
}

/**
Expand Down Expand Up @@ -104,15 +107,36 @@ function ($argument) {
break;
}
$constantPool = ($currentConstantPool = $methodReference->getConstantPool())->getEntries();
$formattedArguments = Formatter::parseSignature(
$constantPool[$methodReference->getDescriptorIndex()]->getString()
)['arguments'];

// does not strict mode can be PHP types
if (!($this->options['strict'] ?? Runtime::STRICT)) {
$formattedArguments = Formatter::signatureConvertToAmbiguousForPHP($formattedArguments);
}

/**
* @var _MethodInfo $methodReference
*/
$methodSignature = Formatter::buildArgumentsSignature(
Formatter::parseSignature($constantPool[$methodReference->getDescriptorIndex()]->getString())['arguments']
);
if ($methodSignature === $convertedPassedArguments) {
$method = $methodReference;
break;
$methodSignature = Formatter::buildArgumentsSignature($formattedArguments);

if (!($this->options['validation']['method']['arguments_count_only'] ?? Runtime::VALIDATION_METHOD_ARGUMENTS_COUNT_ONLY)) {
if ($methodSignature === $convertedPassedArguments) {
$method = $methodReference;
break;
}
}
if (($this->options['validation']['method']['arguments_count_only'] ?? Runtime::VALIDATION_METHOD_ARGUMENTS_COUNT_ONLY) === true) {
$size = count($formattedArguments);
$passedArgumentsSize = count(
$arguments
);

if ($size === $passedArgumentsSize) {
$method = $methodReference;
break;
}
}
}

Expand Down Expand Up @@ -157,8 +181,11 @@ function ($argument) {
$mnemonicMap = new OpCode();
$executedCounter = 0;
while ($reader->getOffset() < $codeAttribute->getOpCodeLength()) {
if (++$executedCounter > \PHPJava\Core\JVM\Parameters\Invoker::MAX_STACK_EXCEEDED) {
throw new RuntimeException('Max stack exceeded. PHPJava has been stopped by safety guard. Maybe Java class has illegal program counter, stacks, or OpCode.');
if (++$executedCounter > ($this->options['max_stack_exceeded'] ?? Runtime::MAX_STACK_EXCEEDED)) {
throw new RuntimeException(
'Max stack exceeded. PHPJava has been stopped by safety guard.' .
' Maybe Java class has illegal program counter, stacks, or OpCode.'
);
}
$opcode = $reader->readUnsignedByte();
$mnemonic = $mnemonicMap->getName($opcode);
Expand Down
7 changes: 0 additions & 7 deletions src/Core/JVM/Parameters/Invoker.php

This file was deleted.

12 changes: 12 additions & 0 deletions src/Core/JVM/Parameters/Runtime.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php
namespace PHPJava\Core\JVM\Parameters;

final class Runtime
{
const ENTRYPOINT = null;

const MAX_STACK_EXCEEDED = 9999;
const STRICT = true;

const VALIDATION_METHOD_ARGUMENTS_COUNT_ONLY = false;
}
4 changes: 2 additions & 2 deletions src/Core/JVM/StaticAccessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ class StaticAccessor implements AccessorInterface
*/
private $methodAccessor;

public function __construct(JavaClassInvoker $invoker, array $methods)
public function __construct(JavaClassInvoker $invoker, array $methods, array $options = [])
{
$this->methodAccessor = new StaticMethodInvoker($invoker, $methods);
$this->methodAccessor = new StaticMethodInvoker($invoker, $methods, $options);
$this->fieldAccessor = new StaticField($invoker, []);
}

Expand Down
20 changes: 14 additions & 6 deletions src/Core/JavaArchive.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php
namespace PHPJava\Core;

use PHPJava\Core\JVM\Parameters\Runtime;
use PHPJava\Exceptions\UndefinedEntrypointException;
use PHPJava\Imitation\java\io\FileNotFoundException;
use PHPJava\Imitation\java\lang\ClassNotFoundException;
Expand All @@ -17,21 +18,25 @@ class JavaArchive
private $expandedHArchive;
private $files = [];
private $classes = [];
private $options = [];

/**
* @param string $jarFile
* @param string|null $entryPoint
* @param array $options
* @throws FileNotFoundException
* @throws \PHPJava\Exceptions\ReadEntryException
* @throws \PHPJava\Exceptions\ValidatorException
* @throws \PHPJava\Imitation\java\lang\ClassNotFoundException
*/
public function __construct(string $jarFile, string $entryPoint = null)
public function __construct(string $jarFile, array $options = [])
{
$this->jarFile = $jarFile;
$archive = new \ZipArchive();
$archive->open($jarFile);
$this->expandedHArchive = $archive;
$this->options = $options;

$this->manifestData['main-class'] = $options['entrypoint'] ?? Runtime::ENTRYPOINT;

// Add resolving path
ClassResolver::add(
Expand Down Expand Up @@ -72,10 +77,13 @@ function ($fileName) {
);

foreach ($this->files as $className => $code) {
$this->classes[str_replace('/', '.', $className)] = new JavaClass(new JavaClassInlineReader(
$className,
$code
));
$this->classes[str_replace('/', '.', $className)] = new JavaClass(
new JavaClassInlineReader(
$className,
$code
),
$this->options
);
}

$currentDirectory = getcwd();
Expand Down
10 changes: 8 additions & 2 deletions src/Core/JavaClass.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,17 @@ class JavaClass

private $superClass;

private $options = [];

/**
* JavaClass constructor.
* @param JavaClassReaderInterface $reader
* @param array $options
* @throws ValidatorException
* @throws \PHPJava\Exceptions\ReadEntryException
* @throws \PHPJava\Imitation\java\lang\ClassNotFoundException
*/
public function __construct(JavaClassReaderInterface $reader)
public function __construct(JavaClassReaderInterface $reader, array $options = [])
{
// Validate Java file
if (!(new MagicByte($reader->getBinaryReader()->readUnsignedInt()))->isValid()) {
Expand Down Expand Up @@ -162,7 +165,10 @@ public function __construct(JavaClassReaderInterface $reader)
}
}

$this->invoker = new JavaClassInvoker($this);
$this->invoker = new JavaClassInvoker(
$this,
$options
);
}

public function __debugInfo()
Expand Down
16 changes: 11 additions & 5 deletions src/Core/JavaClassInvoker.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,16 @@ class JavaClassInvoker

private $specialInvoked = [];

private $options = [];

/**
* JavaClassInvoker constructor.
* @param JavaClass $javaClass
* @param array $options
*/
public function __construct(JavaClass $javaClass)
public function __construct(JavaClass $javaClass, array $options)
{
$this->javaClass = $javaClass;
$this->options = $options;
$cpInfo = $javaClass->getConstantPool()->getEntries();

foreach ($javaClass->getMethods() as $methodInfo) {
Expand Down Expand Up @@ -78,12 +81,14 @@ public function __construct(JavaClass $javaClass)

$this->dynamicAccessor = new DynamicAccessor(
$this,
$this->dynamicMethods
$this->dynamicMethods,
$this->options
);

$this->staticAccessor = new StaticAccessor(
$this,
$this->staticMethods
$this->staticMethods,
$this->options
);

// call <clinit>
Expand All @@ -100,7 +105,8 @@ public function construct(...$arguments): self
{
$this->dynamicAccessor = new DynamicAccessor(
$this,
$this->dynamicMethods
$this->dynamicMethods,
$this->options
);

if (isset($this->dynamicMethods['<init>'])) {
Expand Down
5 changes: 4 additions & 1 deletion src/Imitation/java/io/PrintStream.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php
namespace PHPJava\Imitation\java\io;

use PHPJava\Imitation\java\util\IllegalFormatException;
use PHPJava\Kernel\Structures\_Utf8;
use PHPJava\Kernel\Types\Type;

Expand All @@ -22,7 +23,7 @@ public function println($arg)
return;
}

echo "\n";
throw new IllegalFormatException('Cannot pass "' . gettype($arg) . '" in ' . __METHOD__);
}

public function print($arg)
Expand All @@ -39,6 +40,8 @@ public function print($arg)
echo $arg;
return;
}

throw new IllegalFormatException('Cannot pass "' . gettype($arg) . '" in ' . __METHOD__);
}

public function append($string)
Expand Down
6 changes: 6 additions & 0 deletions src/Imitation/java/lang/IllegalArgumentException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?php
namespace PHPJava\Imitation\java\lang;

class IllegalArgumentException extends RuntimeException
{
}
Loading