TypePHP 编译器
https://swoole.com/aot/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
210 lines
6.6 KiB
210 lines
6.6 KiB
<?php
|
|
/**
|
|
* This file is part of TypePHP.
|
|
*
|
|
* @link https://www.swoole.com/
|
|
* @contact service@swoole.com
|
|
*/
|
|
|
|
namespace TypePhp\Context;
|
|
|
|
use PhpParser\Node\Expr\Variable;
|
|
use PhpParser\NodeAbstract;
|
|
use TypePhp\Entity\ClassDef;
|
|
use TypePhp\Entity\FunctionDef;
|
|
use TypePhp\Entity\InterfaceDef;
|
|
use TypePhp\Exception\Skip;
|
|
|
|
trait CompilationStateTrait
|
|
{
|
|
protected function addLocalVar(string $name, string $type): void
|
|
{
|
|
$this->context->localVars[$name] = $type;
|
|
}
|
|
|
|
protected function registerStdType(string $key): int
|
|
{
|
|
if (isset($this->stdTypeMap[$key])) {
|
|
return $this->stdTypeMap[$key];
|
|
}
|
|
$typeId = count($this->stdTypeMap) + 1;
|
|
$this->stdTypeMap[$key] = $typeId;
|
|
return $typeId;
|
|
}
|
|
|
|
protected function addTmpVar(string $type): string
|
|
{
|
|
$var = $this->genTmpVarName();
|
|
$this->addLocalVar($var, $type);
|
|
return $var;
|
|
}
|
|
|
|
protected function addStaticVar(Variable $var, string $name, string $type): string
|
|
{
|
|
if ($this->hasVar($name)) {
|
|
$this->fatalError($var, 'Duplicate variable `$' . $var->name . '`');
|
|
}
|
|
$this->context->staticVars[$name] = $type;
|
|
// 静态变量实际上是一个全局变量的引用
|
|
$globalVar = $this->escapeStaticVar($name);
|
|
$this->addGlobalVar($globalVar, $type);
|
|
return $globalVar;
|
|
}
|
|
|
|
protected function hasArgument(string $name): bool
|
|
{
|
|
return isset($this->context->arguments[$name]);
|
|
}
|
|
|
|
protected function addArgument(string $name, string $type): void
|
|
{
|
|
$this->context->arguments[$name] = $type;
|
|
$this->addLocalVar($name, $type);
|
|
}
|
|
|
|
protected function addLiteralString(string $value): int
|
|
{
|
|
$index = $this->literalStringIndex++;
|
|
$this->literalStrings[$value] = $index;
|
|
|
|
return $index;
|
|
}
|
|
|
|
protected function addGlobalVar(string $name, string $type): void
|
|
{
|
|
$this->globalVars[$name] = $type;
|
|
}
|
|
|
|
protected function promoteGlobalOrStaticToNativeObject(
|
|
string $name,
|
|
string $class,
|
|
?NodeAbstract $node = null,
|
|
): void
|
|
{
|
|
$class = ltrim($class, '\\');
|
|
if ($this->hasStaticVar($name)) {
|
|
$slot = $this->escapeStaticVar($name);
|
|
$this->context->staticVars[$name] = $this->getNativeObjectPointerType($class);
|
|
} elseif ($this->hasScopeGlobalVar($name)) {
|
|
$slot = $name;
|
|
$this->context->globalVars[$name] = $this->getNativeObjectPointerType($class);
|
|
} else {
|
|
return;
|
|
}
|
|
if (isset($this->nativeGlobalObjects[$slot])) {
|
|
$existing = $this->nativeGlobalObjects[$slot];
|
|
if (!$this->isObjectClassStaticallyAssignableTo($class, $existing)) {
|
|
$message = "Native global/static slot cannot change from `{$existing}` to `{$class}`";
|
|
if ($node !== null) {
|
|
$this->fatalError($node, $message);
|
|
}
|
|
$this->error($message);
|
|
}
|
|
// The first assignment fixes the C++ slot type. A derived object
|
|
// remains assignable, but must not narrow later uses of the slot.
|
|
$class = $existing;
|
|
}
|
|
$this->globalVars[$slot] = $this->getNativeObjectPointerType($class);
|
|
$this->nativeGlobalObjects[$slot] = $class;
|
|
$this->addNativeObject($name, $class);
|
|
}
|
|
|
|
protected function addScopeGlobalVar(string $name, string $type): void
|
|
{
|
|
$this->context->globalVars[$name] = $type;
|
|
}
|
|
|
|
protected function addObject(string $name, string $class): void
|
|
{
|
|
if ($this->isNativeObjectClass($class)) {
|
|
$this->addNativeObject($name, $class);
|
|
return;
|
|
}
|
|
// Interfaces have no concrete method body for native calls. Abstract classes may have concrete methods.
|
|
if ($this->isInterface($class)) {
|
|
$this->context->declaredObjects[$name] = $class;
|
|
} elseif ($this->isNativeClass($class) or $this->isInternalClass($class)) {
|
|
$this->context->objects[$name] = $class;
|
|
}
|
|
}
|
|
|
|
protected function hasVar(string $name): bool
|
|
{
|
|
return $this->hasLocalVar($name) || $this->hasStaticVar($name) || $this->hasScopeGlobalVar($name) || $this->isSuperGlobal($name);
|
|
}
|
|
|
|
protected function hasLocalVar(string $name): bool
|
|
{
|
|
return isset($this->context->localVars[$name]);
|
|
}
|
|
|
|
protected function hasObjectPropVar(string $name): bool
|
|
{
|
|
return isset($this->context->objectProps[$name]);
|
|
}
|
|
|
|
protected function addFunction(string $name, FunctionDef $functionDef): void
|
|
{
|
|
$this->symbols->putFunction($this->escapeFunction($name), $functionDef);
|
|
}
|
|
|
|
/**
|
|
* @param string $name 必须传入带有完整命名空间的类名,将会自动转义为 native name
|
|
*/
|
|
protected function hasFunction(string $name): bool
|
|
{
|
|
return $this->symbols->hasFunction($this->escapeFunction($name));
|
|
}
|
|
|
|
protected function getFunction(string $name): FunctionDef
|
|
{
|
|
return $this->symbols->function($this->escapeFunction($name));
|
|
}
|
|
|
|
protected function addClass(string $name, ClassDef $classDef): void
|
|
{
|
|
$this->symbols->putClass($this->escapeClass($name), $classDef);
|
|
}
|
|
|
|
protected function getClass(string $name): ClassDef
|
|
{
|
|
return $this->symbols->class($this->escapeClass($name));
|
|
}
|
|
|
|
public function getClassDef(string $name): ?ClassDef
|
|
{
|
|
return $this->symbols->findClass($this->escapeClass($name));
|
|
}
|
|
|
|
public function getParentClass(string $class): string
|
|
{
|
|
return $this->symbols->parent(strtolower(ltrim($class, '\\')));
|
|
}
|
|
|
|
protected function hasClass(string $name): bool
|
|
{
|
|
return $this->symbols->hasClass($this->escapeClass($name));
|
|
}
|
|
|
|
protected function hasInterface(string $name): bool
|
|
{
|
|
return $this->symbols->hasInterface($this->escapeClass($name));
|
|
}
|
|
|
|
protected function getInterface(string $name): InterfaceDef
|
|
{
|
|
return $this->symbols->interface($this->escapeClass($name));
|
|
}
|
|
|
|
protected function checkFunction(string $name): void
|
|
{
|
|
// 在预处理阶段检测到函数声明,但是未定义,说明在当前文件,但是顺序错误
|
|
// 跳过,稍后再处理
|
|
if (isset($this->symbolDeclInFile[$name])
|
|
and $this->symbolDeclInFile[$name] === $this->file
|
|
and !$this->hasFunction($name)) {
|
|
$this->redoAfterDeclare[$name] = true;
|
|
throw new Skip();
|
|
}
|
|
}
|
|
}
|
|
|