-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #183 from xp-framework/feature/asymmetric-visibility
Add emitting support for asymmetric visibility
- Loading branch information
Showing
13 changed files
with
299 additions
and
44 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
<?php namespace lang\ast\emit; | ||
|
||
use lang\ast\nodes\{ | ||
Assignment, | ||
Block, | ||
InstanceExpression, | ||
Literal, | ||
OffsetExpression, | ||
ReturnStatement, | ||
Variable | ||
}; | ||
|
||
/** | ||
* Asymmetric Visibility | ||
* | ||
* @see https://wiki.php.net/rfc/asymmetric-visibility-v2 | ||
* @test lang.ast.unittest.emit.AsymmetricVisibilityTest | ||
*/ | ||
trait AsymmetricVisibility { | ||
use VisibilityChecks; | ||
|
||
protected function emitProperty($result, $property) { | ||
static $lookup= [ | ||
'public' => MODIFIER_PUBLIC, | ||
'protected' => MODIFIER_PROTECTED, | ||
'private' => MODIFIER_PRIVATE, | ||
'static' => MODIFIER_STATIC, | ||
'final' => MODIFIER_FINAL, | ||
'abstract' => MODIFIER_ABSTRACT, | ||
'readonly' => MODIFIER_READONLY, | ||
'public(set)' => 0x1000000, | ||
'protected(set)' => 0x0000800, | ||
'private(set)' => 0x0001000, | ||
]; | ||
|
||
$scope= $result->codegen->scope[0]; | ||
$modifiers= 0; | ||
foreach ($property->modifiers as $name) { | ||
$modifiers|= $lookup[$name]; | ||
} | ||
|
||
// Declare checks for private(set) and protected(set), folding declarations | ||
// like `[visibility] [visibility](set)` to just the visibility itself. | ||
if ($modifiers & 0x1000000) { | ||
$checks= []; | ||
$modifiers&= ~0x1000000; | ||
} else if ($modifiers & 0x0000800) { | ||
$checks= [$this->protected($property->name, 'modify protected(set)')]; | ||
$modifiers & MODIFIER_PROTECTED && $modifiers&= ~0x0000800; | ||
} else if ($modifiers & 0x0001000) { | ||
$checks= [$this->private($property->name, 'modify private(set)')]; | ||
$modifiers & MODIFIER_PRIVATE && $modifiers&= ~0x0001000; | ||
} | ||
|
||
// Emit XP meta information for the reflection API | ||
$scope->meta[self::PROPERTY][$property->name]= [ | ||
DETAIL_RETURNS => $property->type ? $property->type->name() : 'var', | ||
DETAIL_ANNOTATIONS => $property->annotations, | ||
DETAIL_COMMENT => $property->comment, | ||
DETAIL_TARGET_ANNO => [], | ||
DETAIL_ARGUMENTS => [$modifiers] | ||
]; | ||
|
||
// The readonly flag is really two flags in one: write-once and restricted(set) | ||
if (in_array('readonly', $property->modifiers)) { | ||
$checks[]= $this->initonce($property->name); | ||
} | ||
|
||
$virtual= new InstanceExpression(new Variable('this'), new OffsetExpression( | ||
new Literal('__virtual'), | ||
new Literal("'{$property->name}'")) | ||
); | ||
$scope->virtual[$property->name]= [ | ||
new ReturnStatement($virtual), | ||
new Block([...$checks, new Assignment($virtual, '=', new Variable('value'))]), | ||
]; | ||
if (isset($property->expression)) { | ||
$scope->init[sprintf('$this->__virtual["%s"]', $property->name)]= $property->expression; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<?php namespace lang\ast\emit; | ||
|
||
use lang\ast\Code; | ||
|
||
trait VisibilityChecks { | ||
|
||
private function initonce($name) { | ||
return new Code('if (isset($this->__virtual["'.$name.'"])) throw new \\Error("Cannot modify readonly property ".__CLASS__."::\$'.$name.'");'); | ||
} | ||
|
||
private function private($name, $access) { | ||
return new Code( | ||
'$scope= debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]["class"] ?? null;'. | ||
'if (__CLASS__ !== $scope && \\lang\\VirtualProperty::class !== $scope)'. | ||
'throw new \\Error("Cannot '.$access.' property ".__CLASS__."::\$'.$name.' from ".($scope ? "scope ".$scope : "global scope"));' | ||
); | ||
} | ||
|
||
private function protected($name, $access) { | ||
return new Code( | ||
'$scope= debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]["class"] ?? null;'. | ||
'if (__CLASS__ !== $scope && !is_subclass_of($scope, __CLASS__) && \\lang\\VirtualProperty::class !== $scope)'. | ||
'throw new \\Error("Cannot '.$access.' property ".__CLASS__."::\$'.$name.' from ".($scope ? "scope ".$scope : "global scope"));' | ||
); | ||
} | ||
} |
Oops, something went wrong.