Skip to content

Commit

Permalink
Fix #2020
Browse files Browse the repository at this point in the history
  • Loading branch information
dreamsxin committed Dec 31, 2019
1 parent 75b425d commit 656d611
Show file tree
Hide file tree
Showing 10 changed files with 726 additions and 1 deletion.
14 changes: 14 additions & 0 deletions Library/Backends/ZendEngine2/Backend.php
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,20 @@ public function updateStaticProperty($classEntry, $property, $value, Compilation
);
}

public function addStaticProperty($classEntry, $property, $value, CompilationContext $context)
{
throw new CompilerException(
'ZendEngine2 backend is no longer supported'
);
}

public function subStaticProperty($classEntry, $property, $value, CompilationContext $context)
{
throw new CompilerException(
'ZendEngine2 backend is no longer supported'
);
}

public function assignArrayProperty(Variable $variable, $property, $key, $value, CompilationContext $context)
{
throw new CompilerException(
Expand Down
12 changes: 12 additions & 0 deletions Library/Backends/ZendEngine3/Backend.php
Original file line number Diff line number Diff line change
Expand Up @@ -956,6 +956,18 @@ public function updateStaticProperty($classEntry, $property, $value, Compilation
$context->codePrinter->output('zephir_update_static_property_ce('.$classEntry.', ZEND_STRL("'.$property.'"), '.$value.');');
}

public function addStaticProperty($classEntry, $property, $value, CompilationContext $context)
{
$value = $this->resolveValue($value, $context);
$context->codePrinter->output('zephir_add_static_property_ce('.$classEntry.', ZEND_STRL("'.$property.'"), '.$value.');');
}

public function subStaticProperty($classEntry, $property, $value, CompilationContext $context)
{
$value = $this->resolveValue($value, $context);
$context->codePrinter->output('zephir_sub_static_property_ce('.$classEntry.', ZEND_STRL("'.$property.'"), '.$value.');');
}

public function assignArrayProperty(Variable $variable, $property, $key, $value, CompilationContext $context)
{
$resolveValue = $this->resolveValue($value, $context);
Expand Down
2 changes: 2 additions & 0 deletions Library/BaseBackend.php
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,8 @@ abstract public function fetchStaticProperty(Variable $symbolVariable, $classDef
abstract public function updateProperty(Variable $symbolVariable, $propertyName, $value, CompilationContext $compilationContext);

abstract public function updateStaticProperty($classEntry, $property, $value, CompilationContext $context);
abstract public function addStaticProperty($classEntry, $property, $value, CompilationContext $context);
abstract public function subStaticProperty($classEntry, $property, $value, CompilationContext $context);

abstract public function assignArrayProperty(Variable $variable, $property, $key, $value, CompilationContext $context);

Expand Down
324 changes: 324 additions & 0 deletions Library/Statements/Let/StaticPropertyAdd.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,324 @@
<?php

/*
* This file is part of the Zephir.
*
* (c) Phalcon Team <[email protected]>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

namespace Zephir\Statements\Let;

use Zephir\CompilationContext;
use Zephir\CompiledExpression;
use Zephir\Exception;
use Zephir\Exception\CompilerException;
use Zephir\Exception\IllegalOperationException;
use Zephir\Expression;

/**
* StaticPropertyAdd.
*
* Updates static properties
*/
class StaticPropertyAdd
{
/**
* Compiles ClassName::foo = {expr}.
*
* @param string $className
* @param string $property
* @param CompiledExpression $resolvedExpr
* @param CompilationContext $compilationContext
* @param array $statement
*
* @throws CompilerException
* @throws IllegalOperationException
*
* @internal param string $variable
*/
public function assignStatic(
string $className,
string $property,
CompiledExpression $resolvedExpr,
CompilationContext $compilationContext,
array $statement
) {
$classDefinition = $compilationContext->classLookup($className);

if (!$propertyDefinition = $classDefinition->getProperty($property)) {
throw new CompilerException(
sprintf(
"Class '%s' does not have a property called: '%s",
$classDefinition->getCompleteName(),
$property
),
$statement
);
}

if (!$propertyDefinition->isStatic()) {
throw new CompilerException(
sprintf(
"Cannot access non-static property '%s::%s",
$classDefinition->getCompleteName(),
$property
),
$statement
);
}

if ($propertyDefinition->isPrivate()) {
if ($classDefinition->getCompleteName() != $compilationContext->classDefinition->getCompleteName()) {
throw new CompilerException(
sprintf(
"Cannot access private static property '%s::%s out of its declaring context",
$classDefinition->getCompleteName(),
$property
),
$statement
);
}
}

$codePrinter = $compilationContext->codePrinter;
$compilationContext->headersManager->add('kernel/object');

try {
$classEntry = $classDefinition->getClassEntry($compilationContext);
} catch (Exception $e) {
throw new CompilerException($e->getMessage(), $statement, $e->getCode(), $e);
}

switch ($resolvedExpr->getType()) {
case 'null':
$compilationContext->backend->updateStaticProperty($classEntry, $property, 'null', $compilationContext);
break;

case 'int':
case 'uint':
case 'long':
$tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true);
$compilationContext->backend->assignLong($tempVariable, $resolvedExpr->getBooleanCode(), $compilationContext);
$compilationContext->backend->addStaticProperty($classEntry, $property, $tempVariable, $compilationContext);
if ($tempVariable->isTemporal()) {
$tempVariable->setIdle(true);
}
break;

case 'char':
case 'uchar':
$tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true);
$compilationContext->backend->assignLong($tempVariable, '\''.$resolvedExpr->getCode().'\'', $compilationContext);
$compilationContext->backend->addStaticProperty($classEntry, $property, $tempVariable, $compilationContext);
if ($tempVariable->isTemporal()) {
$tempVariable->setIdle(true);
}
break;

case 'double':
$tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true);
$compilationContext->backend->assignDouble($tempVariable, $resolvedExpr->getCode(), $compilationContext);
$compilationContext->backend->addStaticProperty($classEntry, $property, $tempVariable, $compilationContext);
if ($tempVariable->isTemporal()) {
$tempVariable->setIdle(true);
}
break;

case 'string':
// TODO: add-assign
if ('assign' !== $statement['operator']) {
throw CompilerException::illegalOperationTypeOnStaticVariable(
$statement['operator'],
$resolvedExpr->getType(),
$statement
);
}

$tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true);
$tempVariable->initVariant($compilationContext);

if ($resolvedExpr->getCode()) {
$compilationContext->backend->assignString($tempVariable, $resolvedExpr->getCode(), $compilationContext);
} else {
$codePrinter->output('ZVAL_EMPTY_STRING('.$tempVariable->getName().');');
}

if ($tempVariable->isTemporal()) {
$tempVariable->setIdle(true);
}

$compilationContext->backend->addStaticProperty($classEntry, $property, $tempVariable, $compilationContext);
break;

case 'bool':
if ('1' == $resolvedExpr->getBooleanCode()) {
$compilationContext->backend->addStaticProperty($classEntry, $property, 'true', $compilationContext);
} else {
if ('0' == $resolvedExpr->getBooleanCode()) {
$compilationContext->backend->addStaticProperty($classEntry, $property, 'false', $compilationContext);
} else {
$codePrinter->output('if ('.$resolvedExpr->getBooleanCode().') {');
$codePrinter->increaseLevel();
$compilationContext->backend->addStaticProperty($classEntry, $property, 'true', $compilationContext);
$codePrinter->decreaseLevel();
$codePrinter->output('} else {');
$codePrinter->increaseLevel();
$compilationContext->backend->addStaticProperty($classEntry, $property, 'false', $compilationContext);
$codePrinter->decreaseLevel();
$codePrinter->output('}');
}
}
break;

case 'empty-array':
$tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true);
$compilationContext->backend->initArray($tempVariable, $compilationContext);
$compilationContext->backend->addStaticProperty($classEntry, $property, $tempVariable, $compilationContext);
if ($tempVariable->isTemporal()) {
$tempVariable->setIdle(true);
}
break;

case 'array':
$compilationContext->backend->addStaticProperty($classEntry, $property, $resolvedExpr, $compilationContext);
break;

case 'variable':
$variableVariable = $compilationContext->symbolTable->getVariableForRead(
$resolvedExpr->getCode(),
$compilationContext,
$statement
);

switch ($variableVariable->getType()) {
case 'int':
case 'uint':
case 'long':
case 'ulong':
case 'char':
case 'uchar':
// TODO: mul-assign, div-assign, sub-assign, add-assign
if ('assign' !== $statement['operator']) {
throw CompilerException::illegalOperationTypeOnStaticVariable(
$statement['operator'],
$variableVariable->getType(),
$statement
);
}

$tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable(
'variable',
$compilationContext,
true
);

$compilationContext->backend->assignLong($tempVariable, $variableVariable, $compilationContext);
$compilationContext->backend->addStaticProperty($classEntry, $property, $tempVariable, $compilationContext);

if ($tempVariable->isTemporal()) {
$tempVariable->setIdle(true);
}
break;

case 'double':
// TODO: mul-assign, div-assign, sub-assign, add-assign
if ('assign' !== $statement['operator']) {
throw CompilerException::illegalOperationTypeOnStaticVariable(
$statement['operator'],
$variableVariable->getType(),
$statement
);
}

$tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable(
'variable',
$compilationContext,
true
);

$compilationContext->backend->assignDouble($tempVariable, $variableVariable, $compilationContext);
$compilationContext->backend->addStaticProperty($classEntry, $property, $tempVariable, $compilationContext);

if ($tempVariable->isTemporal()) {
$tempVariable->setIdle(true);
}
break;

case 'bool':
$tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true);
$compilationContext->backend->assignBool($tempVariable, $variableVariable, $compilationContext);
$compilationContext->backend->addStaticProperty($classEntry, $property, $tempVariable, $compilationContext);
if ($tempVariable->isTemporal()) {
$tempVariable->setIdle(true);
}
break;

case 'string':
switch ($statement['operator']) {
/* @noinspection PhpMissingBreakStatementInspection */
case 'concat-assign':
$tempVariable = $compilationContext->symbolTable->getTempVariableForObserveOrNullify(
'variable',
$compilationContext
);
$expression = new Expression([
'type' => 'static-property-access',
'left' => [
'value' => $statement['variable'],
],
'right' => [
'value' => $statement['property'],
],
]);
$expression->setExpectReturn(true, $tempVariable);

try {
$expression->compile($compilationContext);
} catch (Exception $e) {
throw new CompilerException($e->getMessage(), $statement, $e->getCode(), $e);
}

$variableVariableCode = $compilationContext->backend->getVariableCode($variableVariable);
$tempVariableCode = $compilationContext->backend->getVariableCode($tempVariable);
if ('&' === substr($variableVariableCode, 0, 1)) {
$compilationContext->codePrinter->output('SEPARATE_ZVAL_IF_NOT_REF('.$variableVariableCode.');');
} else {
$compilationContext->codePrinter->output('SEPARATE_ZVAL_IF_NOT_REF(&'.$variableVariableCode.');');
}
$compilationContext->codePrinter->output('zephir_concat_function('.$variableVariableCode.', '.$tempVariableCode.', '.$variableVariableCode.');');
// no break
case 'assign':
$compilationContext->backend->addStaticProperty($classEntry, $property, $variableVariable, $compilationContext);
if ($variableVariable->isTemporal()) {
$variableVariable->setIdle(true);
}
break;
default:
throw CompilerException::illegalOperationTypeOnStaticVariable(
$statement['operator'],
$variableVariable->getType(),
$statement
);
}
break;
case 'variable':
case 'array':
$compilationContext->backend->addStaticProperty($classEntry, $property, $variableVariable, $compilationContext);
if ($variableVariable->isTemporal()) {
$variableVariable->setIdle(true);
}
break;

default:
throw new CompilerException('Unknown type '.$variableVariable->getType(), $statement);
}
break;

default:
throw new CompilerException('Unknown type '.$resolvedExpr->getType(), $statement);
}
}
}
Loading

0 comments on commit 656d611

Please sign in to comment.