Skip to content

Commit cb1d589

Browse files
committed
Add support for generating method entries from stubs
1 parent 66f2ebe commit cb1d589

File tree

7 files changed

+678
-281
lines changed

7 files changed

+678
-281
lines changed

Zend/zend_API.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ typedef struct _zend_fcall_info_cache {
8585
#define ZEND_DEP_ME(classname, name, arg_info, flags) ZEND_RAW_FENTRY(#name, zim_##classname##_##name, arg_info, flags | ZEND_ACC_DEPRECATED)
8686
#define ZEND_ABSTRACT_ME(classname, name, arg_info) ZEND_RAW_FENTRY(#name, NULL, arg_info, ZEND_ACC_PUBLIC|ZEND_ACC_ABSTRACT)
8787
#define ZEND_MALIAS(classname, name, alias, arg_info, flags) ZEND_RAW_FENTRY(#name, zim_##classname##_##alias, arg_info, flags)
88-
#define ZEND_ME_MAPPING(name, func_name, arg_types, flags) ZEND_RAW_FENTRY(#name, zif_##func_name, arg_types, flags)
88+
#define ZEND_ME_MAPPING(name, func_name, arg_info, flags) ZEND_RAW_FENTRY(#name, zif_##func_name, arg_info, flags)
8989

9090
#define ZEND_NS_FENTRY(ns, zend_name, name, arg_info, flags) ZEND_RAW_FENTRY(ZEND_NS_NAME(ns, #zend_name), name, arg_info, flags)
9191

build/gen_stub.php

Lines changed: 217 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ public function tryToRepresentableType(): ?RepresentableType {
206206
$classType = $type;
207207
} else {
208208
// We can only represent a single class type.
209-
return false;
209+
return null;
210210
}
211211
}
212212
return new RepresentableType($classType, $builtinTypes);
@@ -326,9 +326,17 @@ public function equals(ReturnInfo $other): bool {
326326
class FuncInfo {
327327
/** @var string */
328328
public $name;
329-
/** @var ?string */
329+
/** @var string|null */
330330
public $className;
331-
/** @var ?string */
331+
/** @var string|null */
332+
public $visibility;
333+
/** @var bool */
334+
public $isAbstract;
335+
/** @var bool */
336+
public $isStatic;
337+
/** @var bool */
338+
public $isFinal;
339+
/** @var string|null */
332340
public $alias;
333341
/** @var bool */
334342
public $isDeprecated;
@@ -342,11 +350,25 @@ class FuncInfo {
342350
public $cond;
343351

344352
public function __construct(
345-
string $name, ?string $className, ?string $alias, bool $isDeprecated, array $args, ReturnInfo $return,
346-
int $numRequiredArgs, ?string $cond
353+
string $name,
354+
?string $className,
355+
?string $visibility,
356+
bool $isAbstract,
357+
bool $isStatic,
358+
bool $isFinal,
359+
?string $alias,
360+
bool $isDeprecated,
361+
array $args,
362+
ReturnInfo $return,
363+
int $numRequiredArgs,
364+
?string $cond
347365
) {
348366
$this->name = $name;
349367
$this->className = $className;
368+
$this->visibility = $visibility;
369+
$this->isAbstract = $isAbstract;
370+
$this->isStatic = $isStatic;
371+
$this->isFinal = $isFinal;
350372
$this->alias = $alias;
351373
$this->isDeprecated = $isDeprecated;
352374
$this->args = $args;
@@ -377,6 +399,78 @@ public function getArgInfoName(): string {
377399
}
378400
return 'arginfo_' . $this->name;
379401
}
402+
403+
public function getFlags(): string
404+
{
405+
$flags = sprintf("ZEND_ACC_%s", strtoupper($this->visibility));
406+
407+
if ($this->isStatic) {
408+
$flags .= "|ZEND_ACC_STATIC";
409+
}
410+
411+
if ($this->isFinal) {
412+
$flags .= "|ZEND_ACC_FINAL";
413+
}
414+
415+
if ($this->isAbstract) {
416+
$flags .= "|ZEND_ACC_ABSTRACT";
417+
}
418+
419+
if ($this->isDeprecated) {
420+
$flags .= "|ZEND_ACC_DEPRECATED";
421+
}
422+
423+
return $flags;
424+
}
425+
426+
public function getDeclarationKey(): string
427+
{
428+
$name = $this->alias ?? $this->name;
429+
430+
return "$name|$this->cond";
431+
}
432+
433+
public function hasDeclaration(): bool
434+
{
435+
return $this->isAbstract === false || $this->visibility !== "public";
436+
}
437+
438+
public function getDeclaration(): string
439+
{
440+
if ($this->hasDeclaration() === false) {
441+
return "";
442+
}
443+
444+
if ($this->alias) {
445+
$methodAlias = $this->getMethodAlias();
446+
447+
if ($methodAlias) {
448+
return "ZEND_METHOD($this->className, $methodAlias);\n";
449+
} else {
450+
return "ZEND_FUNCTION($this->alias);\n";
451+
}
452+
}
453+
454+
if ($this->className) {
455+
return "ZEND_METHOD($this->className, $this->name);\n";
456+
}
457+
458+
return "ZEND_FUNCTION($this->name);\n";
459+
}
460+
461+
public function getMethodAlias(): ?string
462+
{
463+
if ($this->className === false || $this->alias === null) {
464+
return null;
465+
}
466+
467+
$separatorPosition = strpos($this->alias, "::");
468+
if ($separatorPosition === false) {
469+
return null;
470+
}
471+
472+
return substr($this->alias, $separatorPosition);
473+
}
380474
}
381475

382476
class ClassInfo {
@@ -417,7 +511,7 @@ public function getAllFuncInfos(): iterable {
417511
class DocCommentTag {
418512
/** @var string */
419513
public $name;
420-
/** @var ?string */
514+
/** @var string|null */
421515
public $value;
422516

423517
public function __construct(string $name, ?string $value) {
@@ -458,7 +552,15 @@ function parseDocComment(DocComment $comment): array {
458552
}
459553

460554
function parseFunctionLike(
461-
PrettyPrinterAbstract $prettyPrinter, string $name, ?string $className, Node\FunctionLike $func, ?string $cond
555+
PrettyPrinterAbstract $prettyPrinter,
556+
string $name,
557+
?string $className,
558+
?string $visibility,
559+
bool $isAbstract,
560+
bool $isStatic,
561+
bool $isFinal,
562+
Node\FunctionLike $func,
563+
?string $cond
462564
): FuncInfo {
463565
$comment = $func->getDocComment();
464566
$paramMeta = [];
@@ -539,8 +641,23 @@ function parseFunctionLike(
539641

540642
$return = new ReturnInfo(
541643
$func->returnsByRef(),
542-
$returnType ? Type::fromNode($returnType) : null);
543-
return new FuncInfo($name, $className, $alias, $isDeprecated, $args, $return, $numRequiredArgs, $cond);
644+
$returnType ? Type::fromNode($returnType) : null
645+
);
646+
647+
return new FuncInfo(
648+
$name,
649+
$className,
650+
$visibility,
651+
$isAbstract,
652+
$isStatic,
653+
$isFinal,
654+
$alias,
655+
$isDeprecated,
656+
$args,
657+
$return,
658+
$numRequiredArgs,
659+
$cond
660+
);
544661
}
545662

546663
function handlePreprocessorConditions(array &$conds, Stmt $stmt): ?string {
@@ -622,7 +739,18 @@ function parseStubFile(string $fileName): FileInfo {
622739
}
623740

624741
if ($stmt instanceof Stmt\Function_) {
625-
$funcInfos[] = parseFunctionLike($prettyPrinter, $stmt->name->toString(), null, $stmt, $cond);
742+
$funcInfos[] = parseFunctionLike(
743+
$prettyPrinter,
744+
$stmt->name->toString(),
745+
null,
746+
null,
747+
false,
748+
false,
749+
false,
750+
$stmt,
751+
$cond
752+
);
753+
626754
continue;
627755
}
628756

@@ -639,8 +767,22 @@ function parseStubFile(string $fileName): FileInfo {
639767
throw new Exception("Not implemented {$classStmt->getType()}");
640768
}
641769

770+
$visibility = $classStmt->isPublic() ? "public" : ($classStmt->isProtected() ? "protected" : "private");
771+
$isAbstract = $classStmt->isAbstract() || $stmt instanceof Stmt\Interface_;
772+
$isStatic = $classStmt->isStatic();
773+
$isFinal = $classStmt->isFinal();
774+
642775
$methodInfos[] = parseFunctionLike(
643-
$prettyPrinter, $classStmt->name->toString(), $className, $classStmt, $cond);
776+
$prettyPrinter,
777+
$classStmt->name->toString(),
778+
$className,
779+
$visibility,
780+
$isAbstract,
781+
$isStatic,
782+
$isFinal,
783+
$classStmt,
784+
$cond
785+
);
644786
}
645787

646788
$classInfos[] = new ClassInfo($className, $methodInfos);
@@ -746,7 +888,8 @@ function funcInfoToCode(FuncInfo $funcInfo): string {
746888
return $code . "\n";
747889
}
748890

749-
function findEquivalentFuncInfo(array $generatedFuncInfos, $funcInfo): ?FuncInfo {
891+
/** @param FuncInfo[] $generatedFuncInfos */
892+
function findEquivalentFuncInfo(array $generatedFuncInfos, FuncInfo $funcInfo): ?FuncInfo {
750893
foreach ($generatedFuncInfos as $generatedFuncInfo) {
751894
if ($generatedFuncInfo->equalsApartFromName($funcInfo)) {
752895
return $generatedFuncInfo;
@@ -778,8 +921,6 @@ function generateCodeWithConditions(
778921
}
779922

780923
function generateArgInfoCode(FileInfo $fileInfo): string {
781-
$funcInfos = $fileInfo->funcInfos;
782-
783924
$code = "/* This is a generated file, edit the .stub.php file instead. */\n";
784925
$generatedFuncInfos = [];
785926
$code .= generateCodeWithConditions(
@@ -801,21 +942,67 @@ function(FuncInfo $funcInfo) use(&$generatedFuncInfos) {
801942
);
802943

803944
if ($fileInfo->generateFunctionEntries) {
804-
$code .= "\n\n";
805-
$generatedDeclarations = [];
806-
$code .= generateCodeWithConditions($funcInfos, "", function(FuncInfo $funcInfo) use (&$generatedDeclarations) {
807-
$name = $funcInfo->alias ?? $funcInfo->name;
808-
$key = "$name|$funcInfo->cond";
809-
if (isset($generatedDeclarations[$key])) {
810-
return null;
945+
$code .= generateFunctionEntry(null, $fileInfo->funcInfos);
946+
947+
foreach ($fileInfo->classInfos as $classInfo) {
948+
$code .= generateFunctionEntry($classInfo->name, $classInfo->funcInfos);
949+
}
950+
}
951+
952+
return $code;
953+
}
954+
955+
/** @param FuncInfo[] $funcInfos */
956+
function generateFunctionEntry(?string $className, array $funcInfos): string {
957+
$code = "";
958+
$generatedDeclarations = [];
959+
$code .= generateCodeWithConditions($funcInfos, "", function (FuncInfo $funcInfo) use (&$generatedDeclarations) {
960+
$key = $funcInfo->getDeclarationKey();
961+
if (isset($generatedDeclarations[$key])) {
962+
return null;
963+
}
964+
$generatedDeclarations[$key] = true;
965+
966+
return $funcInfo->getDeclaration();
967+
});
968+
969+
if ($code) {
970+
$code = "\n\n" . $code;
971+
}
972+
973+
$functionEntryName = $className ? "class_{$className}_methods" : "ext_functions";
974+
975+
$code .= "\n\nstatic const zend_function_entry {$functionEntryName}[] = {\n";
976+
$code .= generateCodeWithConditions($funcInfos, "", function (FuncInfo $funcInfo) {
977+
if ($funcInfo->className) {
978+
if ($funcInfo->alias) {
979+
$methodAlias = $funcInfo->getMethodAlias();
980+
981+
if ($methodAlias) {
982+
return sprintf(
983+
"\tZEND_MALIAS(%s, %s, %s, %s, %s)\n",
984+
$funcInfo->className, $funcInfo->name, $methodAlias, $funcInfo->getArgInfoName(), $funcInfo->getFlags()
985+
);
986+
}
987+
988+
return sprintf(
989+
"\tZEND_ME_MAPPING(%s, %s, %s, %s)\n",
990+
$funcInfo->name, $funcInfo->alias, $funcInfo->getArgInfoName(), $funcInfo->getFlags()
991+
);
811992
}
812993

813-
$generatedDeclarations[$key] = true;
814-
return "ZEND_FUNCTION($name);\n";
815-
});
994+
if ($funcInfo->hasDeclaration() === false) {
995+
return sprintf(
996+
"\tZEND_ABSTRACT_ME(%s, %s, %s)\n",
997+
$funcInfo->className, $funcInfo->name, $funcInfo->getArgInfoName()
998+
);
999+
}
8161000

817-
$code .= "\n\nstatic const zend_function_entry ext_functions[] = {\n";
818-
$code .= generateCodeWithConditions($fileInfo->funcInfos, "", function(FuncInfo $funcInfo) {
1001+
return sprintf(
1002+
"\tZEND_ME(%s, %s, %s, %s)\n",
1003+
$funcInfo->className, $funcInfo->name, $funcInfo->getArgInfoName(), $funcInfo->getFlags()
1004+
);
1005+
} else {
8191006
if ($funcInfo->alias) {
8201007
return sprintf(
8211008
"\tZEND_FALIAS(%s, %s, %s)\n",
@@ -828,10 +1015,10 @@ function(FuncInfo $funcInfo) use(&$generatedFuncInfos) {
8281015
}
8291016

8301017
return sprintf("\tZEND_FE(%s, %s)\n", $funcInfo->name, $funcInfo->getArgInfoName());
831-
});
832-
$code .= "\tZEND_FE_END\n";
833-
$code .= "};\n";
834-
}
1018+
}
1019+
});
1020+
$code .= "\tZEND_FE_END\n";
1021+
$code .= "};\n";
8351022

8361023
return $code;
8371024
}

0 commit comments

Comments
 (0)