Skip to content

Commit 7484ef0

Browse files
committed
Generate method entries from stubs
1 parent 66f2ebe commit 7484ef0

File tree

7 files changed

+681
-280
lines changed

7 files changed

+681
-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: 218 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,83 @@ 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 getDeclarationName(): string
434+
{
435+
return $this->alias ?? $this->name;
436+
}
437+
438+
public function hasDeclaration(): bool
439+
{
440+
return $this->isAbstract === false || $this->visibility !== "public";
441+
}
442+
443+
public function getDeclaration(): string
444+
{
445+
if ($this->hasDeclaration() === false) {
446+
return "";
447+
}
448+
449+
if ($this->alias) {
450+
$methodAlias = $this->getMethodAlias();
451+
452+
if ($methodAlias) {
453+
return "ZEND_METHOD($this->className, $methodAlias);\n";
454+
} else {
455+
return "ZEND_FUNCTION($this->alias);\n";
456+
}
457+
}
458+
459+
if ($this->className) {
460+
return "ZEND_METHOD($this->className, $this->name);\n";
461+
}
462+
463+
return "ZEND_FUNCTION($this->name);\n";
464+
}
465+
466+
public function getMethodAlias(): ?string
467+
{
468+
if ($this->className === false || $this->alias === null) {
469+
return null;
470+
}
471+
472+
$separatorPosition = strpos($this->alias, "::");
473+
if ($separatorPosition === false) {
474+
return null;
475+
}
476+
477+
return substr($this->alias, $separatorPosition);
478+
}
380479
}
381480

382481
class ClassInfo {
@@ -417,7 +516,7 @@ public function getAllFuncInfos(): iterable {
417516
class DocCommentTag {
418517
/** @var string */
419518
public $name;
420-
/** @var ?string */
519+
/** @var string|null */
421520
public $value;
422521

423522
public function __construct(string $name, ?string $value) {
@@ -458,7 +557,15 @@ function parseDocComment(DocComment $comment): array {
458557
}
459558

460559
function parseFunctionLike(
461-
PrettyPrinterAbstract $prettyPrinter, string $name, ?string $className, Node\FunctionLike $func, ?string $cond
560+
PrettyPrinterAbstract $prettyPrinter,
561+
string $name,
562+
?string $className,
563+
?string $visibility,
564+
bool $isAbstract,
565+
bool $isStatic,
566+
bool $isFinal,
567+
Node\FunctionLike $func,
568+
?string $cond
462569
): FuncInfo {
463570
$comment = $func->getDocComment();
464571
$paramMeta = [];
@@ -539,8 +646,23 @@ function parseFunctionLike(
539646

540647
$return = new ReturnInfo(
541648
$func->returnsByRef(),
542-
$returnType ? Type::fromNode($returnType) : null);
543-
return new FuncInfo($name, $className, $alias, $isDeprecated, $args, $return, $numRequiredArgs, $cond);
649+
$returnType ? Type::fromNode($returnType) : null
650+
);
651+
652+
return new FuncInfo(
653+
$name,
654+
$className,
655+
$visibility,
656+
$isAbstract,
657+
$isStatic,
658+
$isFinal,
659+
$alias,
660+
$isDeprecated,
661+
$args,
662+
$return,
663+
$numRequiredArgs,
664+
$cond
665+
);
544666
}
545667

546668
function handlePreprocessorConditions(array &$conds, Stmt $stmt): ?string {
@@ -622,7 +744,18 @@ function parseStubFile(string $fileName): FileInfo {
622744
}
623745

624746
if ($stmt instanceof Stmt\Function_) {
625-
$funcInfos[] = parseFunctionLike($prettyPrinter, $stmt->name->toString(), null, $stmt, $cond);
747+
$funcInfos[] = parseFunctionLike(
748+
$prettyPrinter,
749+
$stmt->name->toString(),
750+
null,
751+
null,
752+
false,
753+
false,
754+
false,
755+
$stmt,
756+
$cond
757+
);
758+
626759
continue;
627760
}
628761

@@ -639,8 +772,22 @@ function parseStubFile(string $fileName): FileInfo {
639772
throw new Exception("Not implemented {$classStmt->getType()}");
640773
}
641774

775+
$visibility = $classStmt->isPublic() ? "public" : ($classStmt->isProtected() ? "protected" : "private");
776+
$isAbstract = $classStmt->isAbstract() || $stmt instanceof Stmt\Interface_;
777+
$isStatic = $classStmt->isStatic();
778+
$isFinal = $classStmt->isFinal();
779+
642780
$methodInfos[] = parseFunctionLike(
643-
$prettyPrinter, $classStmt->name->toString(), $className, $classStmt, $cond);
781+
$prettyPrinter,
782+
$classStmt->name->toString(),
783+
$className,
784+
$visibility,
785+
$isAbstract,
786+
$isStatic,
787+
$isFinal,
788+
$classStmt,
789+
$cond
790+
);
644791
}
645792

646793
$classInfos[] = new ClassInfo($className, $methodInfos);
@@ -778,8 +925,6 @@ function generateCodeWithConditions(
778925
}
779926

780927
function generateArgInfoCode(FileInfo $fileInfo): string {
781-
$funcInfos = $fileInfo->funcInfos;
782-
783928
$code = "/* This is a generated file, edit the .stub.php file instead. */\n";
784929
$generatedFuncInfos = [];
785930
$code .= generateCodeWithConditions(
@@ -801,21 +946,65 @@ function(FuncInfo $funcInfo) use(&$generatedFuncInfos) {
801946
);
802947

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

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

817-
$code .= "\n\nstatic const zend_function_entry ext_functions[] = {\n";
818-
$code .= generateCodeWithConditions($fileInfo->funcInfos, "", function(FuncInfo $funcInfo) {
1003+
return sprintf(
1004+
"\tZEND_ME(%s, %s, %s, %s)\n",
1005+
$funcInfo->className, $funcInfo->name, $funcInfo->getArgInfoName(), $funcInfo->getFlags()
1006+
);
1007+
} else {
8191008
if ($funcInfo->alias) {
8201009
return sprintf(
8211010
"\tZEND_FALIAS(%s, %s, %s)\n",
@@ -828,10 +1017,10 @@ function(FuncInfo $funcInfo) use(&$generatedFuncInfos) {
8281017
}
8291018

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

8361025
return $code;
8371026
}

0 commit comments

Comments
 (0)