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
4 changes: 4 additions & 0 deletions ext/standard/Module.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public function getFunctions(): array
{
return [
$this->parseAndCompileFunction('str_repeat', __DIR__.'/str_repeat.php'),
$this->parseAndCompileFunction('decbin', __DIR__.'/decbin.php'),
new abs(),
new ceil(),
new floor(),
Expand All @@ -40,6 +41,9 @@ public function getFunctions(): array
new strcmp(),
new dechex(),
new hexdec(),
new decoct(),
new octdec(),
new bindec(),
];
}

Expand Down
69 changes: 69 additions & 0 deletions ext/standard/bindec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

declare(strict_types=1);

/**
* This file is part of PHP-Compiler, a PHP CFG Compiler for PHP code
*
* @copyright 2015 Anthony Ferrara. All rights reserved
* @license MIT See LICENSE at the root of the project for more info
*/

namespace PHPCompiler\ext\standard;

use PHPCompiler\Frame;
use PHPCompiler\Func\Internal;
use PHPCompiler\JIT\Context;
use PHPCompiler\JIT\Variable as JITVariable;
use PHPCompiler\VM\Variable;
use PHPLLVM\Value;

/**
* bindec() for string arguments (subset of PHP standard library).
*/
final class bindec extends Internal
{
public function execute(Frame $frame): void
{
if (1 !== count($frame->calledArgs)) {
throw new \LogicException('bindec() requires exactly one argument');
}
$v = $frame->calledArgs[0]->resolveIndirect();
if (null === $frame->returnVar) {
return;
}
if (Variable::TYPE_STRING !== $v->type) {
throw new \LogicException('bindec() only supports strings in this compiler build');
}
$frame->returnVar->int((int) \bindec($v->toString()));
}

public Context $context;

public function call(Context $context, JITVariable ...$args): Value
{
$this->context = $context;
if (1 !== count($args)) {
throw new \LogicException('bindec() requires exactly one argument');
}
if (JITVariable::TYPE_STRING !== $args[0]->type) {
throw new \LogicException('bindec() only supports strings in this compiler build');
}
$ptr = $this->stringDataPtr($context, $context->helper->loadValue($args[0]));
$endPtr = $context->getTypeFromString('int8**')->constPointerNull();
$base = $context->getTypeFromString('int32')->constInt(2, false);
$fn = $context->lookupFunction('strtol');
$raw = $context->builder->call($fn, $ptr, $endPtr, $base);
$i64 = $context->getTypeFromString('int64');

return $context->builder->trunc($raw, $i64);
}

private function stringDataPtr(Context $context, Value $strPtr): Value
{
$structName = $strPtr->typeOf()->getElementType()->getName();
$off = $context->structFieldMap[$structName]['value'];

return $context->builder->structGep($strPtr, $off);
}
}
28 changes: 28 additions & 0 deletions ext/standard/decbin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

/**
* This file is part of PHP-Compiler, a PHP CFG Compiler for PHP code
*
* @copyright 2015 Anthony Ferrara. All rights reserved
* @license MIT See LICENSE at the root of the project for more info
*/

namespace PHPCompiler\ext\standard;

/**
* decbin() implemented as compiled PHP (subset: non-negative integers).
*/
function decbin(int $num): string
{
if (0 === $num) {
return '0';
}
$result = '';
for ($n = $num; $n > 0; $n = intval($n / 2)) {
$result = strval($n % 2) . $result;
}

return $result;
}
85 changes: 85 additions & 0 deletions ext/standard/decoct.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?php

declare(strict_types=1);

/**
* This file is part of PHP-Compiler, a PHP CFG Compiler for PHP code
*
* @copyright 2015 Anthony Ferrara. All rights reserved
* @license MIT See LICENSE at the root of the project for more info
*/

namespace PHPCompiler\ext\standard;

use PHPCompiler\Frame;
use PHPCompiler\Func\Internal;
use PHPCompiler\JIT\Context;
use PHPCompiler\JIT\Variable as JITVariable;
use PHPCompiler\VM\Variable;
use PHPLLVM\Value;

/**
* decoct() for integer arguments (subset of PHP standard library).
*/
final class decoct extends Internal
{
public function execute(Frame $frame): void
{
if (1 !== count($frame->calledArgs)) {
throw new \LogicException('decoct() requires exactly one argument');
}
$v = $frame->calledArgs[0]->resolveIndirect();
if (null === $frame->returnVar) {
return;
}
if (Variable::TYPE_INTEGER !== $v->type) {
throw new \LogicException('decoct() only supports integers in this compiler build');
}
$frame->returnVar->string(\decoct($v->toInt()));
}

public Context $context;

public function call(Context $context, JITVariable ...$args): Value
{
$this->context = $context;
if (1 !== count($args)) {
throw new \LogicException('decoct() requires exactly one argument');
}
if (JITVariable::TYPE_NATIVE_LONG !== $args[0]->type) {
throw new \LogicException('decoct() only supports integers in this compiler build');
}

return $this->formatToString($context, $context->helper->loadValue($args[0]), '%o');
}

private function formatToString(Context $context, Value $value, string $format): Value
{
$sizeT = $context->getTypeFromString('size_t');
$charPtr = $context->getTypeFromString('char*');
$i64 = $context->getTypeFromString('int64');
$bufSize = $sizeT->constInt(64, false);
$buf = $context->builder->call($context->lookupFunction('__mm__malloc'), $bufSize);
$bufChar = $context->builder->pointerCast($buf, $charPtr);
$fmt = $context->builder->pointerCast(
$context->constantFromString($format),
$charPtr
);
$written = $context->builder->call(
$context->lookupFunction('snprintf'),
$bufChar,
$bufSize,
$fmt,
$value
);
$len = $context->builder->zExt($written, $i64);
$str = $context->builder->call(
$context->lookupFunction('__string__init'),
$len,
$bufChar
);
$context->builder->call($context->lookupFunction('__mm__free'), $buf);

return $str;
}
}
69 changes: 69 additions & 0 deletions ext/standard/octdec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

declare(strict_types=1);

/**
* This file is part of PHP-Compiler, a PHP CFG Compiler for PHP code
*
* @copyright 2015 Anthony Ferrara. All rights reserved
* @license MIT See LICENSE at the root of the project for more info
*/

namespace PHPCompiler\ext\standard;

use PHPCompiler\Frame;
use PHPCompiler\Func\Internal;
use PHPCompiler\JIT\Context;
use PHPCompiler\JIT\Variable as JITVariable;
use PHPCompiler\VM\Variable;
use PHPLLVM\Value;

/**
* octdec() for string arguments (subset of PHP standard library).
*/
final class octdec extends Internal
{
public function execute(Frame $frame): void
{
if (1 !== count($frame->calledArgs)) {
throw new \LogicException('octdec() requires exactly one argument');
}
$v = $frame->calledArgs[0]->resolveIndirect();
if (null === $frame->returnVar) {
return;
}
if (Variable::TYPE_STRING !== $v->type) {
throw new \LogicException('octdec() only supports strings in this compiler build');
}
$frame->returnVar->int((int) \octdec($v->toString()));
}

public Context $context;

public function call(Context $context, JITVariable ...$args): Value
{
$this->context = $context;
if (1 !== count($args)) {
throw new \LogicException('octdec() requires exactly one argument');
}
if (JITVariable::TYPE_STRING !== $args[0]->type) {
throw new \LogicException('octdec() only supports strings in this compiler build');
}
$ptr = $this->stringDataPtr($context, $context->helper->loadValue($args[0]));
$endPtr = $context->getTypeFromString('int8**')->constPointerNull();
$base = $context->getTypeFromString('int32')->constInt(8, false);
$fn = $context->lookupFunction('strtol');
$raw = $context->builder->call($fn, $ptr, $endPtr, $base);
$i64 = $context->getTypeFromString('int64');

return $context->builder->trunc($raw, $i64);
}

private function stringDataPtr(Context $context, Value $strPtr): Value
{
$structName = $strPtr->typeOf()->getElementType()->getName();
$off = $context->structFieldMap[$structName]['value'];

return $context->builder->structGep($strPtr, $off);
}
}
13 changes: 13 additions & 0 deletions test/compliance/cases/stdlib/bindec.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
--TEST--
stdlib bindec() for binary strings
--FILE--
<?php
echo bindec('0'), "\n";
echo bindec('1010'), "\n";
echo bindec('11111111'), "\n";
echo bindec('1000'), "\n";
--EXPECT--
0
10
255
8
13 changes: 13 additions & 0 deletions test/compliance/cases/stdlib/decbin.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
--TEST--
stdlib decbin() for non-negative integers
--FILE--
<?php
echo decbin(0), "\n";
echo decbin(10), "\n";
echo decbin(255), "\n";
echo decbin(8), "\n";
--EXPECT--
0
1010
11111111
1000
13 changes: 13 additions & 0 deletions test/compliance/cases/stdlib/decoct.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
--TEST--
stdlib decoct() for non-negative integers
--FILE--
<?php
echo decoct(0), "\n";
echo decoct(8), "\n";
echo decoct(63), "\n";
echo decoct(512), "\n";
--EXPECT--
0
10
77
1000
13 changes: 13 additions & 0 deletions test/compliance/cases/stdlib/octdec.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
--TEST--
stdlib octdec() for octal strings
--FILE--
<?php
echo octdec('0'), "\n";
echo octdec('10'), "\n";
echo octdec('77'), "\n";
echo octdec('1000'), "\n";
--EXPECT--
0
8
63
512
15 changes: 15 additions & 0 deletions test/real/cases/stdlib_radix_casts.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
Integration: decbin, bindec, decoct, octdec round-trips
--FILE--
<?php
echo decbin(bindec('1010')), "\n";
echo decoct(octdec('77')), "\n";
echo bindec(decbin(255)), "\n";
echo octdec(decoct(512)), "\n";
echo strlen(decbin(10)), "\n";
--EXPECT--
1010
77
255
512
4