Skip to content

Commit

Permalink
Implementation of variadic arguments
Browse files Browse the repository at this point in the history
  • Loading branch information
m3m0r7 committed Sep 26, 2023
1 parent 4797257 commit a0d0d64
Show file tree
Hide file tree
Showing 9 changed files with 138 additions and 14 deletions.
15 changes: 11 additions & 4 deletions src/VM/Core/Runtime/Entity/Array_.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public function new(RubyClassInterface|array $values = null): self
return $this;
}

public function each(ContextInterface $context): void
public function each(ContextInterface $context): RubyClassInterface
{
/**
* @var ArraySymbol $symbol
Expand All @@ -50,7 +50,11 @@ public function each(ContextInterface $context): void
previousContext: $context,
));

if (!$symbol[$i] instanceof \RubyVM\VM\Core\YARV\Essential\Symbol\SymbolInterface) {
// Renew environment table
$executor->context()
->renewEnvironmentTable();

if (!$symbol[$i] instanceof SymbolInterface) {
throw new RuntimeException(
sprintf(
'Out of index#%d in Array',
Expand All @@ -61,8 +65,8 @@ public function each(ContextInterface $context): void

$object = Number::createBy($symbol[$i]->valueOf())
->toBeRubyClass()
->setRuntimeContext($context)
->setUserlandHeapSpace($context->self()->userlandHeapSpace());
->setRuntimeContext($executor->context())
->setUserlandHeapSpace($executor->context()->self()->userlandHeapSpace());

$executor->context()
->environmentTable()
Expand All @@ -81,6 +85,9 @@ public function each(ContextInterface $context): void
throw $result->threw;
}
}

return Nil::createBy()
->toBeRubyClass();
}

public function push(RubyClassInterface $object): self
Expand Down
12 changes: 11 additions & 1 deletion src/VM/Core/Runtime/Entity/Nil.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace RubyVM\VM\Core\Runtime\Entity;

use RubyVM\VM\Core\Runtime\Essential\RubyClassInterface;
use RubyVM\VM\Core\YARV\Essential\Symbol\NilSymbol;

class Nil extends Entity implements EntityInterface
Expand All @@ -15,6 +16,15 @@ public function __construct(NilSymbol $symbol)

public static function createBy(mixed $value = null): EntityInterface
{
return new self(new NilSymbol());
static $symbol = new NilSymbol();

return new self($symbol);
}

public function toBeRubyClass(): RubyClassInterface
{
static $class = null;

return $class ??= parent::toBeRubyClass();
}
}
4 changes: 2 additions & 2 deletions src/VM/Core/Runtime/Entity/Range.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace RubyVM\VM\Core\Runtime\Entity;

use RubyVM\VM\Core\Helper\ClassHelper;
use RubyVM\VM\Core\Runtime\Executor\Context\OperationProcessorContext;
use RubyVM\VM\Core\Runtime\Executor\Context\ContextInterface;
use RubyVM\VM\Core\Runtime\Executor\Executor;
use RubyVM\VM\Core\Runtime\Executor\LocalTableHelper;
use RubyVM\VM\Core\Runtime\Option;
Expand All @@ -18,7 +18,7 @@ public function __construct(RangeSymbol $symbol)
$this->symbol = $symbol;
}

public function each(OperationProcessorContext $context): EntityInterface
public function each(ContextInterface $context): EntityInterface
{
foreach ($this->symbol->valueOf() as $index => $number) {
$executor = (new Executor(
Expand Down
76 changes: 71 additions & 5 deletions src/VM/Core/Runtime/Executor/CallBlockHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

namespace RubyVM\VM\Core\Runtime\Executor;

use RubyVM\VM\Core\Runtime\Entity\Array_;
use RubyVM\VM\Core\Runtime\Entity\Nil;
use RubyVM\VM\Core\Runtime\Entity\Number;
use RubyVM\VM\Core\Runtime\Essential\RubyClassInterface;
use RubyVM\VM\Core\Runtime\Executor\Context\ContextInterface;
Expand Down Expand Up @@ -31,10 +33,12 @@ private function callSimpleMethod(ContextInterface $context, RubyClassInterface|
instructionSequence: $context->instructionSequence(),
option: $context->option(),
debugger: $context->debugger(),
previousContext: $context
->renewEnvironmentTable(),
previousContext: $context,
));

$executor->context()
->renewEnvironmentTable();

$iseqBodyData = $executor
->context()
->instructionSequence()
Expand All @@ -44,12 +48,28 @@ private function callSimpleMethod(ContextInterface $context, RubyClassInterface|
$localTableSize = $iseqBodyData
->localTableSize();

$startArguments = (Option::VM_ENV_DATA_SIZE + $localTableSize) - count($arguments);
$size = $iseqBodyData->objectParam()->size();

$comparedArgumentsSizeByLocalSize = min($size, count($arguments));

$startArguments = (Option::VM_ENV_DATA_SIZE + $localTableSize) - $comparedArgumentsSizeByLocalSize;

// NOTE: this var means to required parameter (non optional parameter)
$paramLead = $iseqBodyData->objectParam()->leadNum();

for ($localIndex = 0; $localIndex < count($arguments); ++$localIndex) {
// NOTE: Implements splat expression
if ($iseqBodyData->objectParam()->objectParamFlags()->hasRest()) {
$restStart = $iseqBodyData->objectParam()->restStart();

$startOfSplat = count($arguments) - $restStart;

$arguments = self::alignArguments(
array_reverse(array_slice($arguments, 0, $startOfSplat)),
...array_slice($arguments, $startOfSplat),
);
}

for ($localIndex = 0; $localIndex < $comparedArgumentsSizeByLocalSize; ++$localIndex) {
$argument = $arguments[$localIndex];
$slotIndex = LocalTableHelper::computeLocalTableIndex(
$localTableSize,
Expand All @@ -62,7 +82,7 @@ private function callSimpleMethod(ContextInterface $context, RubyClassInterface|
$slotIndex,
$argument,
// NOTE: The parameter is coming by reversed
$paramLead <= (count($arguments) - $localIndex),
$paramLead <= ($size - $localIndex),
);
}

Expand Down Expand Up @@ -131,4 +151,50 @@ private function callBlockWithArguments(CallInfoInterface $callInfo, Number $blo

return $result;
}

/**
* @param (array<ContextInterface|RubyClassInterface>[]|ContextInterface|RubyClassInterface)[] ...$arguments
*
* @return (array<ContextInterface|RubyClassInterface>[]|ContextInterface|RubyClassInterface)[]
*/
private static function alignArguments(RubyClassInterface|ContextInterface|array ...$arguments): array
{
$newArguments = [];

foreach ($arguments as $argument) {
if (is_array($argument)) {
$newArguments[] = Array_::createBy(
array_map(
// Extract symbol
// Do not expected coming here an array but PHP Stan will show an error
// @phpstan-ignore-next-line
static function (RubyClassInterface|ContextInterface $rubyClass) {
if ($rubyClass instanceof ContextInterface) {
return Nil::createBy()
->symbol();
}

return $rubyClass
->entity()
->symbol();
},
$argument,
)
)->toBeRubyClass();

continue;
}

if ($argument instanceof ContextInterface) {
$newArguments[] = Nil::createBy()
->toBeRubyClass();

continue;
}

$newArguments[] = $argument;
}

return $newArguments;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,12 @@ public function process(ContextInterface|RubyClassInterface ...$arguments): Proc
instructionSequence: $instructionSequence,
option: $this->context->option(),
debugger: $this->context->debugger(),
previousContext: $this->context->renewEnvironmentTable(),
previousContext: $this->context,
));

$executor->context()
->renewEnvironmentTable();

$executor->context()
->appendTrace($class->entity()->valueOf());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,9 @@ public function get(mixed $index): OperationProcessorInterface

return $processor;
}

public function __debugInfo(): array
{
return [];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,9 @@ public function __construct(
public readonly bool $acceptsNoKeywordArg,
public readonly bool $ruby2Keywords,
) {}

public function hasRest(): bool
{
return $this->hasRest;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,7 @@

namespace RubyVM\VM\Core\YARV\Criterion\InstructionSequence;

interface ObjectParameterFlagsInterface {}
interface ObjectParameterFlagsInterface
{
public function hasRest(): bool;
}
25 changes: 25 additions & 0 deletions tests/Version/Ruby3_2/MethodTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,29 @@ public function testProc(): void
$this->assertSame(ExecutedStatus::SUCCESS, $executor->execute()->executedStatus);
$this->assertSame("Hello World!\n", $rubyVMManager->stdOut->readAll());
}

public function testVariadicArguments(): void
{
$rubyVMManager = $this->createRubyVMFromCode(
<<< '_'
def variadic_arguments(a, b, *params)
puts a
params.each do |param|
puts param
end
# Check if VM stack is not empty (call a method testing)
puts b
end
variadic_arguments(1, 5, 2, 3, 4)
_,
);

$executor = $rubyVMManager
->rubyVM
->disassemble(RubyVersion::VERSION_3_2);

$this->assertSame(ExecutedStatus::SUCCESS, $executor->execute()->executedStatus);
$this->assertSame("1\n2\n3\n4\n5\n", $rubyVMManager->stdOut->readAll());
}
}

0 comments on commit a0d0d64

Please sign in to comment.