Skip to content

Commit eaf656d

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

File tree

7 files changed

+678
-280
lines changed

7 files changed

+678
-280
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 & 29 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);
@@ -778,8 +920,6 @@ function generateCodeWithConditions(
778920
}
779921

780922
function generateArgInfoCode(FileInfo $fileInfo): string {
781-
$funcInfos = $fileInfo->funcInfos;
782-
783923
$code = "/* This is a generated file, edit the .stub.php file instead. */\n";
784924
$generatedFuncInfos = [];
785925
$code .= generateCodeWithConditions(
@@ -801,21 +941,69 @@ function(FuncInfo $funcInfo) use(&$generatedFuncInfos) {
801941
);
802942

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

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

817-
$code .= "\n\nstatic const zend_function_entry ext_functions[] = {\n";
818-
$code .= generateCodeWithConditions($fileInfo->funcInfos, "", function(FuncInfo $funcInfo) {
1002+
return sprintf(
1003+
"\tZEND_ME(%s, %s, %s, %s)\n",
1004+
$funcInfo->className, $funcInfo->name, $funcInfo->getArgInfoName(), $funcInfo->getFlags()
1005+
);
1006+
} else {
8191007
if ($funcInfo->alias) {
8201008
return sprintf(
8211009
"\tZEND_FALIAS(%s, %s, %s)\n",
@@ -828,10 +1016,10 @@ function(FuncInfo $funcInfo) use(&$generatedFuncInfos) {
8281016
}
8291017

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

8361024
return $code;
8371025
}

0 commit comments

Comments
 (0)