From 3f2de3bff1bb946613facd4b5648e5beb22ef50a Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Tue, 15 Sep 2026 22:26:43 +0200 Subject: [PATCH 01/12] Put single-statement guard clauses of the native sources on one line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A guard whose body is only a return, RETURN_THROWS(), break or continue is written `if (cond) return x;` — mostly the propagation of a pending exception after each engine call. Guards that would grow wider than 160 columns keep their braces; macro bodies and else chains are untouched. Token-identical, so every object compiles byte-identical. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01MShHKdUB19w38vboJLPKXy --- turbo-ext/src/ArenaCache.cpp | 288 ++----- turbo-ext/src/CombinationsHelper.cpp | 8 +- turbo-ext/src/ConditionalExpressionHolder.cpp | 16 +- turbo-ext/src/ExpressionResultStorage.cpp | 16 +- turbo-ext/src/ExpressionTypeHolder.cpp | 16 +- turbo-ext/src/NodeScanner.cpp | 4 +- turbo-ext/src/NodeTraverser.cpp | 144 +--- turbo-ext/src/PharForkGuard.cpp | 28 +- turbo-ext/src/ScopeOps.cpp | 716 +++++------------- turbo-ext/src/Shadow.cpp | 20 +- turbo-ext/src/SymbolFinderInFiles.cpp | 28 +- turbo-ext/src/SymbolScan.h | 128 +--- turbo-ext/src/TrinaryLogic.cpp | 80 +- turbo-ext/src/TrustedTypes.cpp | 40 +- turbo-ext/src/TypeCombinatorCache.cpp | 84 +- turbo-ext/src/main.cpp | 8 +- turbo-ext/src/parser/ParserRunner.cpp | 180 ++--- turbo-ext/src/parser/ParserRunnerHelpers.cpp | 272 ++----- turbo-ext/src/support.cpp | 172 ++--- turbo-ext/src/zv.h | 4 +- 20 files changed, 563 insertions(+), 1689 deletions(-) diff --git a/turbo-ext/src/ArenaCache.cpp b/turbo-ext/src/ArenaCache.cpp index f3151bf8438..3a53b6a93db 100644 --- a/turbo-ext/src/ArenaCache.cpp +++ b/turbo-ext/src/ArenaCache.cpp @@ -280,30 +280,20 @@ struct SerializeCtx * cases are process singletons that a flat record cannot represent. */ static bool objectClassCodecable(zend_class_entry *ce) { - if (ce->type == ZEND_INTERNAL_CLASS && ce != zend_standard_class_def) { - return false; - } - if ((ce->ce_flags & (ZEND_ACC_INTERFACE | ZEND_ACC_ABSTRACT | ZEND_ACC_ENUM)) != 0) { - return false; - } - if (ce->__serialize != NULL || ce->__unserialize != NULL) { - return false; - } + if (ce->type == ZEND_INTERNAL_CLASS && ce != zend_standard_class_def) return false; + if ((ce->ce_flags & (ZEND_ACC_INTERFACE | ZEND_ACC_ABSTRACT | ZEND_ACC_ENUM)) != 0) return false; + if (ce->__serialize != NULL || ce->__unserialize != NULL) return false; if (zend_hash_str_exists(&ce->function_table, "__wakeup", sizeof("__wakeup") - 1) || zend_hash_str_exists(&ce->function_table, "__sleep", sizeof("__sleep") - 1)) { return false; } - if (ce->create_object != NULL) { - return false; - } + if (ce->create_object != NULL) return false; return true; } static bool serializeValue(WriteBuffer &out, zval *value, uint32_t depth, SerializeCtx &ctx) { - if (depth > SERIALIZE_DEPTH_LIMIT) { - return false; - } + if (depth > SERIALIZE_DEPTH_LIMIT) return false; ZVAL_DEREF(value); switch (Z_TYPE_P(value)) { case IS_NULL: @@ -343,18 +333,14 @@ static bool serializeValue(WriteBuffer &out, zval *value, uint32_t depth, Serial out.u8(0); out.u64((uint64_t) entry.indexKey()); } - if (!serializeValue(out, entry.value().raw(), depth + 1, ctx)) { - return false; - } + if (!serializeValue(out, entry.value().raw(), depth + 1, ctx)) return false; } return true; } case IS_OBJECT: { zend_object *obj = Z_OBJ_P(value); zend_class_entry *ce = obj->ce; - if (!objectClassCodecable(ce)) { - return false; - } + if (!objectClassCodecable(ce)) return false; if (!ctx.seenInited) { zend_hash_init(&ctx.seenObjects, 8, NULL, NULL, 0); @@ -383,27 +369,19 @@ static bool serializeValue(WriteBuffer &out, zval *value, uint32_t depth, Serial for (zv::ArrayEntry entry : zv::TableRef(props)) { zval *propValue = entry.value().raw(); ZVAL_DEINDIRECT(propValue); - if (Z_ISUNDEF_P(propValue)) { - continue; - } - if (entry.stringKeyOrNull() == NULL) { - return false; /* numeric-keyed dynamic prop: not worth supporting */ - } + if (Z_ISUNDEF_P(propValue)) continue; + if (entry.stringKeyOrNull() == NULL) return false; /* numeric-keyed dynamic prop: not worth supporting */ propCount++; } out.u32(propCount); for (zv::ArrayEntry entry : zv::TableRef(props)) { zval *propValue = entry.value().raw(); ZVAL_DEINDIRECT(propValue); - if (Z_ISUNDEF_P(propValue)) { - continue; - } + if (Z_ISUNDEF_P(propValue)) continue; zend_string *propKey = entry.stringKey(); out.u32((uint32_t) ZSTR_LEN(propKey)); out.blob(ZSTR_VAL(propKey), ZSTR_LEN(propKey)); - if (!serializeValue(out, propValue, depth + 1, ctx)) { - return false; - } + if (!serializeValue(out, propValue, depth + 1, ctx)) return false; } return true; } @@ -432,18 +410,14 @@ struct ReadCursor bool u8(uint8_t *out) { - if (!need(1)) { - return false; - } + if (!need(1)) return false; *out = *p++; return true; } bool u32(uint32_t *out) { - if (!need(sizeof(*out))) { - return false; - } + if (!need(sizeof(*out))) return false; memcpy(out, p, sizeof(*out)); p += sizeof(*out); return true; @@ -451,9 +425,7 @@ struct ReadCursor bool u64(uint64_t *out) { - if (!need(sizeof(*out))) { - return false; - } + if (!need(sizeof(*out))) return false; memcpy(out, p, sizeof(*out)); p += sizeof(*out); return true; @@ -489,9 +461,7 @@ static zend_string *internString(DeserializeCtx &ctx, const char *bytes, size_t ctx.internsInited = true; } zend_string *existing = (zend_string *) zend_hash_str_find_ptr(&ctx.interns, bytes, len); - if (existing != NULL) { - return existing; - } + if (existing != NULL) return existing; zend_string *created = zend_string_init(bytes, len, 0); zend_hash_add_new_ptr(&ctx.interns, created, created); zend_string_release(created); /* the table's key reference keeps it alive */ @@ -500,13 +470,9 @@ static zend_string *internString(DeserializeCtx &ctx, const char *bytes, size_t static bool deserializeValue(ReadCursor &in, zval *out, uint32_t depth, DeserializeCtx &ctx) { - if (depth > SERIALIZE_DEPTH_LIMIT) { - return false; - } + if (depth > SERIALIZE_DEPTH_LIMIT) return false; uint8_t tag; - if (!in.u8(&tag)) { - return false; - } + if (!in.u8(&tag)) return false; switch (tag) { case TAG_NULL: ZVAL_NULL(out); @@ -519,16 +485,12 @@ static bool deserializeValue(ReadCursor &in, zval *out, uint32_t depth, Deserial return true; case TAG_INT: { uint64_t v; - if (!in.u64(&v)) { - return false; - } + if (!in.u64(&v)) return false; ZVAL_LONG(out, (zend_long) v); return true; } case TAG_DOUBLE: { - if (!in.need(sizeof(double))) { - return false; - } + if (!in.need(sizeof(double))) return false; double d; memcpy(&d, in.p, sizeof(d)); in.p += sizeof(d); @@ -537,23 +499,17 @@ static bool deserializeValue(ReadCursor &in, zval *out, uint32_t depth, Deserial } case TAG_STRING: { uint32_t len; - if (!in.u32(&len) || !in.need(len)) { - return false; - } + if (!in.u32(&len) || !in.need(len)) return false; ZVAL_STR_COPY(out, internString(ctx, (const char *) in.p, len)); in.p += len; return true; } case TAG_ARRAY: { uint32_t count; - if (!in.u32(&count)) { - return false; - } + if (!in.u32(&count)) return false; /* every entry costs >= 2 stream bytes; rejects corrupt counts * before they turn into a giant preallocation */ - if ((size_t) count > (size_t) (in.end - in.p)) { - return false; - } + if ((size_t) count > (size_t) (in.end - in.p)) return false; zend_array *arr = zend_new_array(count); for (uint32_t i = 0; i < count; i++) { uint8_t keyKind; @@ -593,19 +549,13 @@ static bool deserializeValue(ReadCursor &in, zval *out, uint32_t depth, Deserial } case TAG_OBJECT: { uint32_t nameLen; - if (!in.u32(&nameLen) || !in.need(nameLen)) { - return false; - } + if (!in.u32(&nameLen) || !in.need(nameLen)) return false; zend_string *className = internString(ctx, (const char *) in.p, nameLen); in.p += nameLen; zend_class_entry *ce = zend_lookup_class(className); - if (ce == NULL || EG(exception) != NULL || !objectClassCodecable(ce)) { - return false; - } + if (ce == NULL || EG(exception) != NULL || !objectClassCodecable(ce)) return false; zval objZv; - if (object_init_ex(&objZv, ce) != SUCCESS) { - return false; - } + if (object_init_ex(&objZv, ce) != SUCCESS) return false; /* registered before the children parse so cycles resolve */ ctx.objects.push_back(Z_OBJ(objZv)); uint32_t propCount; @@ -659,9 +609,7 @@ static bool deserializeValue(ReadCursor &in, zval *out, uint32_t depth, Deserial } case TAG_OBJREF: { uint32_t objectId; - if (!in.u32(&objectId) || (size_t) objectId >= ctx.objects.size()) { - return false; - } + if (!in.u32(&objectId) || (size_t) objectId >= ctx.objects.size()) return false; ZVAL_OBJ_COPY(out, ctx.objects[objectId]); return true; } @@ -690,14 +638,10 @@ struct RecordView static bool recordAt(uint64_t offset, RecordView *view) { - if (offset < arenaDataStart() || offset + sizeof(RecordHeader) > pt_arena_total) { - return false; - } + if (offset < arenaDataStart() || offset + sizeof(RecordHeader) > pt_arena_total) return false; const RecordHeader *header = (const RecordHeader *) ((char *) pt_arena_base + offset); uint64_t payloadStart = alignUp8(offset + sizeof(RecordHeader) + header->keyLen); - if (payloadStart > pt_arena_total || header->payloadLen > pt_arena_total - payloadStart) { - return false; - } + if (payloadStart > pt_arena_total || header->payloadLen > pt_arena_total - payloadStart) return false; view->header = header; view->key = (const char *) (header + 1); view->payload = (const uint8_t *) pt_arena_base + payloadStart; @@ -707,23 +651,15 @@ static bool recordAt(uint64_t offset, RecordView *view) /* Probes the index for key; fills view on hit. */ static bool findRecord(const char *key, size_t keyLen, RecordView *view) { - if (pt_arena_base == NULL) { - return false; - } + if (pt_arena_base == NULL) return false; uint64_t hash = fnv1a64(key, keyLen); uint64_t mask = INDEX_SLOT_COUNT - 1; uint64_t *slots = indexSlots(); for (uint32_t probe = 0; probe < INDEX_PROBE_LIMIT; probe++) { uint64_t offset = atomicLoadAcquire(&slots[(hash + probe) & mask]); - if (offset == 0) { - return false; - } - if (!recordAt(offset, view)) { - return false; - } - if (view->header->keyLen == keyLen && memcmp(view->key, key, keyLen) == 0) { - return true; - } + if (offset == 0) return false; + if (!recordAt(offset, view)) return false; + if (view->header->keyLen == keyLen && memcmp(view->key, key, keyLen) == 0) return true; } return false; } @@ -732,15 +668,11 @@ static bool findRecord(const char *key, size_t keyLen, RecordView *view) * gracefully to a concurrent publisher of the same key, as late as it can. */ static void publishRecord(const char *key, size_t keyLen, uint32_t kind, const WriteBuffer &payload) { - if (pt_arena_base == NULL || keyLen > UINT32_MAX) { - return; - } + if (pt_arena_base == NULL || keyLen > UINT32_MAX) return; uint64_t recordSize = alignUp8(sizeof(RecordHeader) + keyLen) + payload.bytes.size(); uint64_t offset = atomicFetchAdd(&arenaHeader()->allocCursor, alignUp8(recordSize)); - if (offset > pt_arena_total || recordSize > pt_arena_total - offset) { - return; /* arena full: analysis continues, just unshared */ - } + if (offset > pt_arena_total || recordSize > pt_arena_total - offset) return; /* arena full: analysis continues, just unshared */ /* The callers checked the index before building the payload; check it once * more before writing it. Building a record takes long enough for another @@ -748,9 +680,7 @@ static void publishRecord(const char *key, size_t keyLen, uint32_t kind, const W * here is a page the backing store commits for good - a loser that returns * now costs nothing but the bump it already took. */ RecordView published; - if (findRecord(key, keyLen, &published)) { - return; - } + if (findRecord(key, keyLen, &published)) return; char *record = (char *) pt_arena_base + offset; RecordHeader header; @@ -768,18 +698,12 @@ static void publishRecord(const char *key, size_t keyLen, uint32_t kind, const W uint64_t *slot = &slots[(hash + probe) & mask]; uint64_t current = atomicLoadAcquire(slot); if (current == 0) { - if (atomicCasRelease(slot, 0, offset)) { - return; - } + if (atomicCasRelease(slot, 0, offset)) return; current = atomicLoadAcquire(slot); } RecordView existing; - if (!recordAt(current, &existing)) { - return; - } - if (existing.header->keyLen == keyLen && memcmp(existing.key, key, keyLen) == 0) { - return; /* lost the race: someone published this key first */ - } + if (!recordAt(current, &existing)) return; + if (existing.header->keyLen == keyLen && memcmp(existing.key, key, keyLen) == 0) return; /* lost the race: someone published this key first */ } /* index congested — give up on this record, it stays dead space */ } @@ -825,9 +749,7 @@ static bool hashRecordBuild(WriteBuffer &out, HashTable *entries) /* each entry stream is self-contained — object ids and OBJREFs must * not cross entry boundaries, entries deserialize independently */ SerializeCtx entryCtx; - if (!serializeValue(out, entry.value().raw(), 0, entryCtx)) { - return false; - } + if (!serializeValue(out, entry.value().raw(), 0, entryCtx)) return false; uint64_t hash = fnv1a64(keyBytes, keyLen); uint64_t mask = slotCount - 1; @@ -853,9 +775,7 @@ static bool hashRecordAll(const RecordView &view, zval *result) { const uint8_t *payload = view.payload; uint64_t payloadLen = view.header->payloadLen; - if (payloadLen < sizeof(uint64_t)) { - return false; - } + if (payloadLen < sizeof(uint64_t)) return false; uint64_t slotCount; memcpy(&slotCount, payload, sizeof(slotCount)); if (slotCount == 0 || (slotCount & (slotCount - 1)) != 0 @@ -907,9 +827,7 @@ static bool hashRecordFind(const RecordView &view, const char *entryKey, size_t { const uint8_t *payload = view.payload; uint64_t payloadLen = view.header->payloadLen; - if (payloadLen < sizeof(uint64_t)) { - return false; - } + if (payloadLen < sizeof(uint64_t)) return false; uint64_t slotCount; memcpy(&slotCount, payload, sizeof(slotCount)); if (slotCount == 0 || (slotCount & (slotCount - 1)) != 0 @@ -924,17 +842,11 @@ static bool hashRecordFind(const RecordView &view, const char *entryKey, size_t for (uint64_t probe = 0; probe < slotCount; probe++) { uint64_t entryOffset; memcpy(&entryOffset, slots + ((hash + probe) & mask) * sizeof(uint64_t), sizeof(entryOffset)); - if (entryOffset == 0) { - return false; - } - if (entryOffset < entriesStart || entryOffset + sizeof(uint32_t) > payloadLen) { - return false; - } + if (entryOffset == 0) return false; + if (entryOffset < entriesStart || entryOffset + sizeof(uint32_t) > payloadLen) return false; uint32_t keyLen; memcpy(&keyLen, payload + entryOffset, sizeof(keyLen)); - if (keyLen > payloadLen - entryOffset - sizeof(uint32_t)) { - return false; - } + if (keyLen > payloadLen - entryOffset - sizeof(uint32_t)) return false; const char *keyBytes = (const char *) payload + entryOffset + sizeof(uint32_t); if (keyLen == entryKeyLen && memcmp(keyBytes, entryKey, entryKeyLen) == 0) { ReadCursor in; @@ -965,15 +877,11 @@ static void arenaResetState() static bool runIdValid(zend_string *runId) { - if (ZSTR_LEN(runId) == 0 || ZSTR_LEN(runId) > RUN_ID_LENGTH_LIMIT) { - return false; - } + if (ZSTR_LEN(runId) == 0 || ZSTR_LEN(runId) > RUN_ID_LENGTH_LIMIT) return false; for (size_t i = 0; i < ZSTR_LEN(runId); i++) { char c = ZSTR_VAL(runId)[i]; bool alnum = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'); - if (!alnum) { - return false; - } + if (!alnum) return false; } return true; } @@ -996,23 +904,15 @@ static bool headerValid(const ArenaHeader *header, uint64_t mappedSize) static uint64_t backedArenaSize(int fd) { struct statvfs backing; - if (fstatvfs(fd, &backing) != 0) { - return ARENA_SIZE_LIMIT; - } + if (fstatvfs(fd, &backing) != 0) return ARENA_SIZE_LIMIT; uint64_t blockSize = backing.f_frsize != 0 ? (uint64_t) backing.f_frsize : (uint64_t) backing.f_bsize; uint64_t available = (uint64_t) backing.f_bavail * blockSize; - if (blockSize == 0 || available <= ARENA_BACKING_RESERVE_LIMIT) { - return 0; - } + if (blockSize == 0 || available <= ARENA_BACKING_RESERVE_LIMIT) return 0; uint64_t size = available - ARENA_BACKING_RESERVE_LIMIT; - if (size < ARENA_MIN_SIZE_LIMIT) { - return 0; - } - if (size > ARENA_SIZE_LIMIT) { - return ARENA_SIZE_LIMIT; - } + if (size < ARENA_MIN_SIZE_LIMIT) return 0; + if (size > ARENA_SIZE_LIMIT) return ARENA_SIZE_LIMIT; /* the index is indexed off page-aligned offsets; keep the tail whole */ return size & ~(uint64_t) 4095; @@ -1025,9 +925,7 @@ class ArenaCache static void create(zend_string *runId, zval *return_value) { RETVAL_NULL(); - if (pt_arena_base != NULL || !runIdValid(runId)) { - return; - } + if (pt_arena_base != NULL || !runIdValid(runId)) return; #ifdef _WIN32 // a pagefile-backed section is committed when it is created, so @@ -1042,9 +940,7 @@ class ArenaCache (DWORD) (ARENA_SIZE_LIMIT >> 32), (DWORD) (ARENA_SIZE_LIMIT & 0xFFFFFFFF), name); - if (section == NULL) { - return; - } + if (section == NULL) return; if (GetLastError() == ERROR_ALREADY_EXISTS) { CloseHandle(section); return; @@ -1059,9 +955,7 @@ class ArenaCache char name[64]; snprintf(name, sizeof(name), "/phpstan-%s", ZSTR_VAL(runId)); int fd = shm_open(name, O_CREAT | O_EXCL | O_RDWR, 0600); - if (fd < 0) { - return; - } + if (fd < 0) return; uint64_t size = backedArenaSize(fd); if (size == 0) { close(fd); @@ -1107,19 +1001,13 @@ class ArenaCache /* already mapped — a forked child inherits the parent's mapping */ return true; } - if (ZSTR_LEN(name) == 0 || ZSTR_LEN(name) >= sizeof(pt_arena_name)) { - return false; - } + if (ZSTR_LEN(name) == 0 || ZSTR_LEN(name) >= sizeof(pt_arena_name)) return false; #ifdef _WIN32 - if (strncmp(ZSTR_VAL(name), "Local\\phpstan-", 14) != 0) { - return false; - } + if (strncmp(ZSTR_VAL(name), "Local\\phpstan-", 14) != 0) return false; uint64_t size = ARENA_SIZE_LIMIT; HANDLE section = OpenFileMappingA(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, ZSTR_VAL(name)); - if (section == NULL) { - return false; - } + if (section == NULL) return false; void *base = MapViewOfFile(section, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0); if (base == NULL) { CloseHandle(section); @@ -1132,13 +1020,9 @@ class ArenaCache } pt_arena_section = section; #else - if (strncmp(ZSTR_VAL(name), "/phpstan-", 9) != 0) { - return false; - } + if (strncmp(ZSTR_VAL(name), "/phpstan-", 9) != 0) return false; int fd = shm_open(ZSTR_VAL(name), O_RDWR, 0); - if (fd < 0) { - return false; - } + if (fd < 0) return false; /* the creator sized the object to what its filesystem could back, so * the object itself says how much there is to map */ struct stat objectStat; @@ -1153,9 +1037,7 @@ class ArenaCache } void *base = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); close(fd); - if (base == MAP_FAILED) { - return false; - } + if (base == MAP_FAILED) return false; if (!headerValid((const ArenaHeader *) base, size)) { munmap(base, size); return false; @@ -1184,9 +1066,7 @@ class ArenaCache static void destroy() { - if (pt_arena_base == NULL) { - return; - } + if (pt_arena_base == NULL) return; unlinkName(); #ifdef _WIN32 UnmapViewOfFile(pt_arena_base); @@ -1207,34 +1087,24 @@ class ArenaCache { RETVAL_NULL(); RecordView view; - if (!findRecord(ZSTR_VAL(key), ZSTR_LEN(key), &view) || view.header->kind != RECORD_KIND_VALUE) { - return; - } + if (!findRecord(ZSTR_VAL(key), ZSTR_LEN(key), &view) || view.header->kind != RECORD_KIND_VALUE) return; ReadCursor in; in.p = view.payload; in.end = view.payload + view.header->payloadLen; zval result; DeserializeCtx ctx; - if (!deserializeValue(in, &result, 0, ctx)) { - return; - } + if (!deserializeValue(in, &result, 0, ctx)) return; RETVAL_ZVAL(&result, 0, 0); } static void publish(zend_string *key, zval *value) { - if (pt_arena_base == NULL) { - return; - } + if (pt_arena_base == NULL) return; RecordView existing; - if (findRecord(ZSTR_VAL(key), ZSTR_LEN(key), &existing)) { - return; - } + if (findRecord(ZSTR_VAL(key), ZSTR_LEN(key), &existing)) return; WriteBuffer payload; SerializeCtx ctx; - if (!serializeValue(payload, value, 0, ctx)) { - return; - } + if (!serializeValue(payload, value, 0, ctx)) return; publishRecord(ZSTR_VAL(key), ZSTR_LEN(key), RECORD_KIND_VALUE, payload); } @@ -1242,13 +1112,9 @@ class ArenaCache { RETVAL_NULL(); RecordView view; - if (!findRecord(ZSTR_VAL(recordKey), ZSTR_LEN(recordKey), &view) || view.header->kind != RECORD_KIND_HASH) { - return; - } + if (!findRecord(ZSTR_VAL(recordKey), ZSTR_LEN(recordKey), &view) || view.header->kind != RECORD_KIND_HASH) return; zval result; - if (!hashRecordFind(view, ZSTR_VAL(entryKey), ZSTR_LEN(entryKey), &result)) { - return; - } + if (!hashRecordFind(view, ZSTR_VAL(entryKey), ZSTR_LEN(entryKey), &result)) return; RETVAL_ZVAL(&result, 0, 0); } @@ -1256,29 +1122,19 @@ class ArenaCache { RETVAL_NULL(); RecordView view; - if (!findRecord(ZSTR_VAL(recordKey), ZSTR_LEN(recordKey), &view) || view.header->kind != RECORD_KIND_HASH) { - return; - } + if (!findRecord(ZSTR_VAL(recordKey), ZSTR_LEN(recordKey), &view) || view.header->kind != RECORD_KIND_HASH) return; zval result; - if (!hashRecordAll(view, &result)) { - return; - } + if (!hashRecordAll(view, &result)) return; RETVAL_ZVAL(&result, 0, 0); } static void publishHash(zend_string *recordKey, HashTable *entries) { - if (pt_arena_base == NULL) { - return; - } + if (pt_arena_base == NULL) return; RecordView existing; - if (findRecord(ZSTR_VAL(recordKey), ZSTR_LEN(recordKey), &existing)) { - return; - } + if (findRecord(ZSTR_VAL(recordKey), ZSTR_LEN(recordKey), &existing)) return; WriteBuffer payload; - if (!hashRecordBuild(payload, entries)) { - return; - } + if (!hashRecordBuild(payload, entries)) return; publishRecord(ZSTR_VAL(recordKey), ZSTR_LEN(recordKey), RECORD_KIND_HASH, payload); } }; diff --git a/turbo-ext/src/CombinationsHelper.cpp b/turbo-ext/src/CombinationsHelper.cpp index 4dce4c73776..727590cdc4a 100644 --- a/turbo-ext/src/CombinationsHelper.cpp +++ b/turbo-ext/src/CombinationsHelper.cpp @@ -121,9 +121,7 @@ class CombinationsHelper /* odometer: advance the rightmost index, carrying leftwards */ for (int64_t j = (int64_t) n - 1; j >= 0; j--) { - if (++indices[j] < sizes[j]) { - break; - } + if (++indices[j] < sizes[j]) break; indices[j] = 0; } } @@ -154,9 +152,7 @@ void pt_register_combinations_helper() zval arraysZv; ZVAL_ARR(&arraysZv, arrays); zv::Val result = CombinationsHelper::combinations(zv::ArrRef(&arraysZv)); - if (UNEXPECTED(result.isUndef())) { - RETURN_THROWS(); - } + if (UNEXPECTED(result.isUndef())) RETURN_THROWS(); result.intoReturnValue(return_value); }); diff --git a/turbo-ext/src/ConditionalExpressionHolder.cpp b/turbo-ext/src/ConditionalExpressionHolder.cpp index 246f516b6bd..b5277a40db3 100644 --- a/turbo-ext/src/ConditionalExpressionHolder.cpp +++ b/turbo-ext/src/ConditionalExpressionHolder.cpp @@ -50,15 +50,11 @@ class ConditionalExpressionHolder zv::Ref typeHolder = obj.propAt(PT_CEH_PROP_TYPEHOLDER); for (auto entry : zv::ArrRef(conds.raw())) { - if (UNEXPECTED(!pt_check_holder(entry.value().deref().raw()))) { - return zv::Val(); - } + if (UNEXPECTED(!pt_check_holder(entry.value().deref().raw()))) return zv::Val(); } zend_string *key = pt_ceh_key_build(conds.asArrayTable(), typeHolder.raw()); - if (UNEXPECTED(key == NULL)) { - return zv::Val(); - } + if (UNEXPECTED(key == NULL)) return zv::Val(); return zv::Val::adoptString(key); } @@ -91,9 +87,7 @@ void pt_register_conditional_expression_holder() Z_PARAM_ARRAY(holders) Z_PARAM_OBJECT_OF_CLASS(typeHolder, pt_ce_expr_type_holder) ZEND_PARSE_PARAMETERS_END(); - if (UNEXPECTED(!ConditionalExpressionHolder(ZEND_THIS).construct(zv::ArrRef(holders), zv::Ref(typeHolder)))) { - RETURN_THROWS(); - } + if (UNEXPECTED(!ConditionalExpressionHolder(ZEND_THIS).construct(zv::ArrRef(holders), zv::Ref(typeHolder)))) RETURN_THROWS(); }); cls.method("getConditionExpressionTypeHolders", reg::Public, 0, {}, [](INTERNAL_FUNCTION_PARAMETERS) { @@ -109,9 +103,7 @@ void pt_register_conditional_expression_holder() cls.method("getKey", reg::Public, 0, {}, [](INTERNAL_FUNCTION_PARAMETERS) { ZEND_PARSE_PARAMETERS_NONE(); zv::Val key = ConditionalExpressionHolder(ZEND_THIS).getKey(); - if (UNEXPECTED(key.isUndef())) { - RETURN_THROWS(); - } + if (UNEXPECTED(key.isUndef())) RETURN_THROWS(); key.intoReturnValue(return_value); }); diff --git a/turbo-ext/src/ExpressionResultStorage.cpp b/turbo-ext/src/ExpressionResultStorage.cpp index a6ed029950d..6d3f4c09e91 100644 --- a/turbo-ext/src/ExpressionResultStorage.cpp +++ b/turbo-ext/src/ExpressionResultStorage.cpp @@ -35,9 +35,7 @@ class ExpressionResultStorage zv::Val duplicate() const { zval newObj; - if (UNEXPECTED(object_init_ex(&newObj, Z_OBJCE_P(self)) != SUCCESS)) { - return zv::Val(); - } + if (UNEXPECTED(object_init_ex(&newObj, Z_OBJCE_P(self)) != SUCCESS)) return zv::Val(); zv::ObjRef(&newObj).propAtWrite(PT_ERS_PROP_FALLBACK, zv::Val::copyOf(zv::Ref(self))); return zv::Val::adopt(newObj); } @@ -71,14 +69,10 @@ class ExpressionResultStorage for (;;) { zv::ObjRef obj(cur); zv::Ref found = zv::ArrRef(obj.propAt(PT_ERS_PROP_RESULTS).raw()).findIndex(id); - if (found.raw() != NULL) { - return zv::Val::copyOf(found); - } + if (found.raw() != NULL) return zv::Val::copyOf(found); /* the twin recurses into ?self $fallback; iterate the chain */ zval *fallback = obj.propAt(PT_ERS_PROP_FALLBACK).raw(); - if (Z_TYPE_P(fallback) != IS_OBJECT) { - return zv::Val::null(); - } + if (Z_TYPE_P(fallback) != IS_OBJECT) return zv::Val::null(); cur = fallback; } } @@ -114,9 +108,7 @@ void pt_register_expression_result_storage() cls.method("duplicate", reg::Public, 0, {}, [](INTERNAL_FUNCTION_PARAMETERS) { ZEND_PARSE_PARAMETERS_NONE(); zv::Val result = ExpressionResultStorage(ZEND_THIS).duplicate(); - if (UNEXPECTED(result.isUndef())) { - RETURN_THROWS(); - } + if (UNEXPECTED(result.isUndef())) RETURN_THROWS(); result.intoReturnValue(return_value); }); diff --git a/turbo-ext/src/ExpressionTypeHolder.cpp b/turbo-ext/src/ExpressionTypeHolder.cpp index d9ff0a47c06..60fec05e074 100644 --- a/turbo-ext/src/ExpressionTypeHolder.cpp +++ b/turbo-ext/src/ExpressionTypeHolder.cpp @@ -56,9 +56,7 @@ class ExpressionTypeHolder zv::Val and_(zval *other) const { zval result; - if (UNEXPECTED(!pt_holder_and(self, other, &result))) { - return zv::Val(); - } + if (UNEXPECTED(!pt_holder_and(self, other, &result))) return zv::Val(); return zv::Val::adopt(result); } @@ -124,9 +122,7 @@ void pt_register_expression_type_holder() ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_OBJECT_OF_CLASS(other, pt_ce_expr_type_holder) ZEND_PARSE_PARAMETERS_END(); - if (UNEXPECTED(!ExpressionTypeHolder(ZEND_THIS).equalTypes(other, out))) { - RETURN_THROWS(); - } + if (UNEXPECTED(!ExpressionTypeHolder(ZEND_THIS).equalTypes(other, out))) RETURN_THROWS(); RETURN_BOOL(out); }); @@ -136,9 +132,7 @@ void pt_register_expression_type_holder() ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_OBJECT_OF_CLASS(other, pt_ce_expr_type_holder) ZEND_PARSE_PARAMETERS_END(); - if (UNEXPECTED(!ExpressionTypeHolder(ZEND_THIS).equals(other, out))) { - RETURN_THROWS(); - } + if (UNEXPECTED(!ExpressionTypeHolder(ZEND_THIS).equals(other, out))) RETURN_THROWS(); RETURN_BOOL(out); }); @@ -148,9 +142,7 @@ void pt_register_expression_type_holder() Z_PARAM_OBJECT_OF_CLASS(other, pt_ce_expr_type_holder) ZEND_PARSE_PARAMETERS_END(); zv::Val result = ExpressionTypeHolder(ZEND_THIS).and_(other); - if (UNEXPECTED(result.isUndef())) { - RETURN_THROWS(); - } + if (UNEXPECTED(result.isUndef())) RETURN_THROWS(); result.intoReturnValue(return_value); }); diff --git a/turbo-ext/src/NodeScanner.cpp b/turbo-ext/src/NodeScanner.cpp index 2110b0a1329..9813e7b15df 100644 --- a/turbo-ext/src/NodeScanner.cpp +++ b/turbo-ext/src/NodeScanner.cpp @@ -61,9 +61,7 @@ void pt_register_node_scanner() ZEND_PARSE_PARAMETERS_END(); bool failed = false; bool result = NodeScanner::nodeIsOrContainsYield(zv::ObjRef(node), failed); - if (UNEXPECTED(failed)) { - RETURN_THROWS(); - } + if (UNEXPECTED(failed)) RETURN_THROWS(); RETURN_BOOL(result); }); diff --git a/turbo-ext/src/NodeTraverser.cpp b/turbo-ext/src/NodeTraverser.cpp index 0ac64fa01fe..ca3e98b2876 100644 --- a/turbo-ext/src/NodeTraverser.cpp +++ b/turbo-ext/src/NodeTraverser.cpp @@ -141,9 +141,7 @@ static pt_trav_class_info *pt_trav_class_info_for(zend_object *obj) } info = (pt_trav_class_info *) zend_hash_find_ptr(&pt_trav_class_cache, ce->name); - if (EXPECTED(info != NULL)) { - return info; - } + if (EXPECTED(info != NULL)) return info; info = (pt_trav_class_info *) ecalloc(1, sizeof(pt_trav_class_info)); @@ -157,13 +155,9 @@ static pt_trav_class_info *pt_trav_class_info_for(zend_object *obj) info->names = (zend_string **) emalloc(sizeof(zend_string *) * (capacity > 0 ? capacity : 1)); ZEND_HASH_FOREACH_VAL(Z_ARRVAL(names), name_zv) { zend_property_info *prop; - if (Z_TYPE_P(name_zv) != IS_STRING) { - continue; - } + if (Z_TYPE_P(name_zv) != IS_STRING) continue; prop = (zend_property_info *) zend_hash_find_ptr(&ce->properties_info, Z_STR_P(name_zv)); - if (prop == NULL || (prop->flags & ZEND_ACC_STATIC) != 0) { - continue; - } + if (prop == NULL || (prop->flags & ZEND_ACC_STATIC) != 0) continue; info->offsets[info->count] = (uint32_t) prop->offset; info->names[info->count] = zend_string_copy(Z_STR_P(name_zv)); info->count++; @@ -243,9 +237,7 @@ class NodeTraverser void removeVisitor(zv::Ref visitor) { zv::Ref prop = visitorsProp(); - if (!prop.isArray()) { - return; - } + if (!prop.isArray()) return; /* array_search() with loose comparison, like the PHP implementation */ bool found = false; @@ -260,9 +252,7 @@ class NodeTraverser } pos++; } - if (!found) { - return; - } + if (!found) return; /* array_splice($visitors, $index, 1, []) — reindexes */ zv::ArrRef old(prop.raw()); @@ -284,9 +274,7 @@ class NodeTraverser /* $this->stopTraversal = false */ selfObj.propAtWrite(PT_NT_PROP_STOP, zv::Val::boolean(false)); - if (UNEXPECTED(!buildVisitorPlan())) { - return zv::Val(); - } + if (UNEXPECTED(!buildVisitorPlan())) return zv::Val(); /* work on our own copy of the nodes array */ zv::Arr nodes = zv::Arr::adoptTable(zend_array_dup(nodesTable)); @@ -294,13 +282,9 @@ class NodeTraverser /* beforeTraverse */ for (uint32_t vi = 0; vi < nvisitors; vi++) { const pt_visitor_plan *p = &plan[vi]; - if (p->before_fn == NULL) { - continue; - } + if (p->before_fn == NULL) continue; zv::Val ret = callVisitorHook(p, p->before_fn, nodes.ref()); - if (UNEXPECTED(ret.isUndef())) { - return zv::Val(); - } + if (UNEXPECTED(ret.isUndef())) return zv::Val(); if (ret.ref().isArray()) { nodes = zv::Arr::adoptVal(std::move(ret)); nodes.separate(); @@ -309,9 +293,7 @@ class NodeTraverser nodes.separate(); zv::Arr replacement = traverseArray(nodes.arrRef()); - if (UNEXPECTED(failed)) { - return zv::Val(); - } + if (UNEXPECTED(failed)) return zv::Val(); if (!replacement.isUndef()) { nodes = std::move(replacement); } @@ -319,13 +301,9 @@ class NodeTraverser /* afterTraverse, in reverse */ for (int64_t vi = (int64_t) nvisitors - 1; vi >= 0; vi--) { const pt_visitor_plan *p = &plan[vi]; - if (p->after_fn == NULL) { - continue; - } + if (p->after_fn == NULL) continue; zv::Val ret = callVisitorHook(p, p->after_fn, nodes.ref()); - if (UNEXPECTED(ret.isUndef())) { - return zv::Val(); - } + if (UNEXPECTED(ret.isUndef())) return zv::Val(); if (ret.ref().isArray()) { nodes = zv::Arr::adoptVal(std::move(ret)); } @@ -377,21 +355,15 @@ class NodeTraverser * array it started with. */ zv::Val arrayGuard = zv::Val::copyOf(zv::Ref(value.raw())); zv::Arr replacement = traverseArray(zv::ArrRef(value.raw())); - if (UNEXPECTED(failed)) { - return; - } + if (UNEXPECTED(failed)) return; if (!replacement.isUndef()) { value.assign(std::move(replacement)); } - if (stop) { - return; - } + if (stop) return; continue; } - if (!value.instanceOf(nodeIface)) { - continue; - } + if (!value.instanceOf(nodeIface)) continue; /* Own a reference for the whole block: a visitor writing to the * parent's property from a hook can otherwise drop the node's * last reference while later hooks still run on it. The PHP twin @@ -407,27 +379,21 @@ class NodeTraverser for (uint32_t vi = 0; vi < nvisitors; vi++) { const pt_visitor_plan *p = &plan[vi]; visitorIndex = vi; - if (!p->call_enter) { - continue; - } + if (!p->call_enter) continue; zv::Val ret = callVisitorHook(p, p->enter_fn, subNode); if (UNEXPECTED(ret.isUndef())) { failed = true; return; } zv::Ref retRef = ret.ref(); - if (retRef.isNull()) { - continue; - } + if (retRef.isNull()) continue; if (retRef.instanceOf(nodeIface)) { if (UNEXPECTED(!ensureReplacementReasonable(subNode, retRef.asObject()))) { failed = true; return; } /* $node->$name = $subNode = $return */ - if (UNEXPECTED(!writeSubnode(node, info->names[i], retRef))) { - return; - } + if (UNEXPECTED(!writeSubnode(node, info->names[i], retRef))) return; subNodeOwned = zv::Val::copyOf(retRef); subNode = retRef.asObject(); continue; @@ -447,9 +413,7 @@ class NodeTraverser return; } if (code == REPLACE_WITH_NULL) { - if (UNEXPECTED(!writeSubnodeNull(node, info->names[i]))) { - return; - } + if (UNEXPECTED(!writeSubnodeNull(node, info->names[i]))) return; skipToNext = true; break; } @@ -459,40 +423,30 @@ class NodeTraverser return; } - if (skipToNext) { - continue; - } + if (skipToNext) continue; if (traverseChildren) { traverseNode(subNode); - if (UNEXPECTED(failed) || stop) { - return; - } + if (UNEXPECTED(failed) || stop) return; } /* leaveNode, in reverse from the last visitor whose enterNode ran */ for (int64_t vi = visitorIndex; vi >= 0; vi--) { const pt_visitor_plan *p = &plan[vi]; - if (!p->call_leave) { - continue; - } + if (!p->call_leave) continue; zv::Val ret = callVisitorHook(p, p->leave_fn, subNode); if (UNEXPECTED(ret.isUndef())) { failed = true; return; } zv::Ref retRef = ret.ref(); - if (retRef.isNull()) { - continue; - } + if (retRef.isNull()) continue; if (retRef.instanceOf(nodeIface)) { if (UNEXPECTED(!ensureReplacementReasonable(subNode, retRef.asObject()))) { failed = true; return; } - if (UNEXPECTED(!writeSubnode(node, info->names[i], retRef))) { - return; - } + if (UNEXPECTED(!writeSubnode(node, info->names[i], retRef))) return; subNodeOwned = zv::Val::copyOf(retRef); subNode = retRef.asObject(); continue; @@ -504,9 +458,7 @@ class NodeTraverser return; } if (code == REPLACE_WITH_NULL) { - if (UNEXPECTED(!writeSubnodeNull(node, info->names[i]))) { - return; - } + if (UNEXPECTED(!writeSubnodeNull(node, info->names[i]))) return; break; } } @@ -561,18 +513,14 @@ class NodeTraverser for (uint32_t vi = 0; vi < nvisitors; vi++) { const pt_visitor_plan *p = &plan[vi]; visitorIndex = vi; - if (!p->call_enter) { - continue; - } + if (!p->call_enter) continue; zv::Val ret = callVisitorHook(p, p->enter_fn, node); if (UNEXPECTED(ret.isUndef())) { failed = true; break; } zv::Ref retRef = ret.ref(); - if (retRef.isNull()) { - continue; - } + if (retRef.isNull()) continue; if (retRef.instanceOf(nodeIface)) { if (UNEXPECTED(!ensureReplacementReasonable(node, retRef.asObject()))) { failed = true; @@ -618,35 +566,25 @@ class NodeTraverser break; } - if (UNEXPECTED(failed) || stop) { - break; - } - if (skipToNext) { - continue; - } + if (UNEXPECTED(failed) || stop) break; + if (skipToNext) continue; if (traverseChildren) { traverseNode(node); - if (UNEXPECTED(failed) || stop) { - break; - } + if (UNEXPECTED(failed) || stop) break; } /* leaveNode, in reverse from the last visitor whose enterNode ran */ for (int64_t vi = visitorIndex; vi >= 0; vi--) { const pt_visitor_plan *p = &plan[vi]; - if (!p->call_leave) { - continue; - } + if (!p->call_leave) continue; zv::Val ret = callVisitorHook(p, p->leave_fn, node); if (UNEXPECTED(ret.isUndef())) { failed = true; break; } zv::Ref retRef = ret.ref(); - if (retRef.isNull()) { - continue; - } + if (retRef.isNull()) continue; if (retRef.instanceOf(nodeIface)) { if (UNEXPECTED(!ensureReplacementReasonable(node, retRef.asObject()))) { failed = true; @@ -681,9 +619,7 @@ class NodeTraverser break; } - if (UNEXPECTED(failed) || stop) { - break; - } + if (UNEXPECTED(failed) || stop) break; } if (UNEXPECTED(failed)) { @@ -724,9 +660,7 @@ class NodeTraverser { zend_class_entry *stmtCe = pt_class(PT_CLASS_STMT); zend_class_entry *exprCe = pt_class(PT_CLASS_EXPR); - if (UNEXPECTED(stmtCe == NULL || exprCe == NULL)) { - return false; - } + if (UNEXPECTED(stmtCe == NULL || exprCe == NULL)) return false; zv::ObjRef oldRef(oldNode); zv::ObjRef newRef(newNode); @@ -814,9 +748,7 @@ class NodeTraverser { zval retval; zend_call_known_function(hook, p->visitor, p->ce, &retval, 1, arg.raw(), NULL); - if (UNEXPECTED(EG(exception))) { - return zv::Val(); - } + if (UNEXPECTED(EG(exception))) return zv::Val(); return zv::Val::adopt(retval); } @@ -888,9 +820,7 @@ void pt_register_node_traverser() Z_PARAM_VARIADIC('+', visitors, count) ZEND_PARSE_PARAMETERS_END(); NodeTraverser self(Z_OBJ_P(ZEND_THIS)); - if (UNEXPECTED(!self.construct(visitors, count))) { - RETURN_THROWS(); - } + if (UNEXPECTED(!self.construct(visitors, count))) RETURN_THROWS(); }); static const reg::Arg voidReturn = { "", MAY_BE_VOID, nullptr }; @@ -919,9 +849,7 @@ void pt_register_node_traverser() ZEND_PARSE_PARAMETERS_END(); NodeTraverser self(Z_OBJ_P(ZEND_THIS)); zv::Val result = self.traverse(nodes); - if (UNEXPECTED(result.isUndef())) { - RETURN_THROWS(); - } + if (UNEXPECTED(result.isUndef())) RETURN_THROWS(); result.intoReturnValue(return_value); }, &arrayReturn); diff --git a/turbo-ext/src/PharForkGuard.cpp b/turbo-ext/src/PharForkGuard.cpp index b87caf73197..53993f5e8ed 100644 --- a/turbo-ext/src/PharForkGuard.cpp +++ b/turbo-ext/src/PharForkGuard.cpp @@ -61,30 +61,22 @@ static int pt_pfg_count = 0; static void pt_pfg_prepare(void) { pt_pfg_count = 0; - if (!pt_pfg_registered) { - return; - } + if (!pt_pfg_registered) return; struct stat target; - if (stat(pt_pfg_path, &target) != 0) { - return; - } + if (stat(pt_pfg_path, &target) != 0) return; /* /dev/fd is a symlink to /proc/self/fd on Linux and native on the BSDs * and macOS; listing it beats fstat()ing every fd up to the rlimit. */ DIR *dir = opendir("/dev/fd"); - if (dir == NULL) { - return; - } + if (dir == NULL) return; int dir_fd = dirfd(dir); struct dirent *entry; while ((entry = readdir(dir)) != NULL && pt_pfg_count < PT_PFG_MAX_FDS) { char *end = NULL; long fd = strtol(entry->d_name, &end, 10); - if (end == entry->d_name || *end != '\0' || fd < 0 || fd == dir_fd) { - continue; - } + if (end == entry->d_name || *end != '\0' || fd < 0 || fd == dir_fd) continue; struct stat st; if (fstat((int) fd, &st) != 0 @@ -97,14 +89,10 @@ static void pt_pfg_prepare(void) /* A write-mode fd would mean someone is rebuilding the archive — * swapping its description out from under them is not ours to do. */ int fl_flags = fcntl((int) fd, F_GETFL); - if (fl_flags == -1 || (fl_flags & O_ACCMODE) != O_RDONLY) { - continue; - } + if (fl_flags == -1 || (fl_flags & O_ACCMODE) != O_RDONLY) continue; off_t cursor = lseek((int) fd, 0, SEEK_CUR); - if (cursor == (off_t) -1) { - continue; - } + if (cursor == (off_t) -1) continue; pt_pfg_table[pt_pfg_count].fd = (int) fd; pt_pfg_table[pt_pfg_count].cursor = cursor; @@ -140,9 +128,7 @@ static void pt_pfg_child(void) void pt_phar_fork_guard_register(zend_string *path) { - if (ZSTR_LEN(path) == 0 || ZSTR_LEN(path) >= sizeof(pt_pfg_path)) { - return; - } + if (ZSTR_LEN(path) == 0 || ZSTR_LEN(path) >= sizeof(pt_pfg_path)) return; memcpy(pt_pfg_path, ZSTR_VAL(path), ZSTR_LEN(path) + 1); pt_pfg_registered = true; diff --git a/turbo-ext/src/ScopeOps.cpp b/turbo-ext/src/ScopeOps.cpp index a4403bdb09b..d104adc7d8e 100644 --- a/turbo-ext/src/ScopeOps.cpp +++ b/turbo-ext/src/ScopeOps.cpp @@ -60,9 +60,7 @@ static pt_scope_offsets *pt_scope_offsets_for(zend_class_entry *ce) pt_scope_offsets_cache_inited = true; } off = (pt_scope_offsets *) zend_hash_find_ptr(&pt_scope_offsets_cache, ce->name); - if (EXPECTED(off != NULL)) { - return off; - } + if (EXPECTED(off != NULL)) return off; off = (pt_scope_offsets *) emalloc(sizeof(pt_scope_offsets)); off->expression_types = pt_instance_prop_offset(ce, "expressionTypes", sizeof("expressionTypes") - 1); @@ -103,9 +101,7 @@ class ScopeOps static zv::Val nodeKey(zend_object *node, zval *exprPrinter) { zend_string *key = pt_node_key(node, exprPrinter); - if (UNEXPECTED(key == NULL)) { - return zv::Val(); - } + if (UNEXPECTED(key == NULL)) return zv::Val(); return zv::Val::adoptString(key); } @@ -121,23 +117,17 @@ class ScopeOps *keyOut = NULL; zval *exprPrinter = scopeProp(scope, "exprPrinter", sizeof("exprPrinter") - 1); - if (UNEXPECTED(exprPrinter == NULL)) { - return zv::Val(); - } + if (UNEXPECTED(exprPrinter == NULL)) return zv::Val(); if (UNEXPECTED(Z_TYPE_P(exprPrinter) != IS_OBJECT)) { zend_throw_error(NULL, "phpstan_turbo: exprPrinter is not an object"); return zv::Val(); } zv::Str key = zv::Str::adopt(pt_node_key(node, exprPrinter)); - if (UNEXPECTED(key.isNull())) { - return zv::Val(); - } + if (UNEXPECTED(key.isNull())) return zv::Val(); zval *table = scopeProp(scope, "resolvedTypes", sizeof("resolvedTypes") - 1); - if (UNEXPECTED(table == NULL)) { - return zv::Val(); - } + if (UNEXPECTED(table == NULL)) return zv::Val(); if (EXPECTED(Z_TYPE_P(table) == IS_ARRAY)) { zval *found = zend_symtable_find(Z_ARRVAL_P(table), key.get()); if (found != NULL && Z_TYPE_P(found) != IS_NULL) { @@ -161,9 +151,7 @@ class ScopeOps zend_class_entry *variableCe = pt_class(PT_CLASS_VARIABLE); zend_class_entry *closureCe = pt_class(PT_CLASS_CLOSURE_EXPR); zend_class_entry *arrowFunctionCe = pt_class(PT_CLASS_ARROW_FUNCTION); - if (UNEXPECTED(variableCe == NULL || closureCe == NULL || arrowFunctionCe == NULL)) { - return zv::Val(); - } + if (UNEXPECTED(variableCe == NULL || closureCe == NULL || arrowFunctionCe == NULL)) return zv::Val(); if (instanceof_function(node->ce, variableCe) || instanceof_function(node->ce, closureCe) @@ -172,20 +160,12 @@ class ScopeOps } zval *table = scopeArrayProp(scope, "expressionTypes", sizeof("expressionTypes") - 1); - if (UNEXPECTED(table == NULL)) { - return zv::Val(); - } + if (UNEXPECTED(table == NULL)) return zv::Val(); zval *found = zend_symtable_find(Z_ARRVAL_P(table), exprString); - if (found == NULL) { - return zv::Val::null(); - } + if (found == NULL) return zv::Val::null(); zv::Ref holder = zv::Ref(found).deref(); - if (UNEXPECTED(!pt_check_holder(holder.raw()))) { - return zv::Val(); - } - if (pt_holder_certainty_value(holder.asObject()) != PT_TRI_YES) { - return zv::Val::null(); - } + if (UNEXPECTED(!pt_check_holder(holder.raw()))) return zv::Val(); + if (pt_holder_certainty_value(holder.asObject()) != PT_TRI_YES) return zv::Val::null(); return zv::Val::copyOf(zv::ObjRef(holder.asObject()).propAt(PT_ETH_PROP_TYPE)); } @@ -193,62 +173,42 @@ class ScopeOps static zv::Val hasExpressionType(zval *scope, zend_object *node, zval *exprPrinter) { pt_node_class_info *info = pt_get_node_class_info(node->ce); - if (UNEXPECTED(info == NULL)) { - return zv::Val(); - } + if (UNEXPECTED(info == NULL)) return zv::Val(); if (info->is_variable && info->name_offset >= 0) { zv::Ref name = zv::ObjRef(node).propAtOffset((uint32_t) info->name_offset).deref(); - if (name.isString()) { - return hasVariableType(scope, name.asString()); - } + if (name.isString()) return hasVariableType(scope, name.asString()); } zv::Str key = zv::Str::adopt(pt_node_key(node, exprPrinter)); - if (UNEXPECTED(key.isNull())) { - return zv::Val(); - } + if (UNEXPECTED(key.isNull())) return zv::Val(); zval *table = scopeArrayProp(scope, "expressionTypes", sizeof("expressionTypes") - 1); - if (UNEXPECTED(table == NULL)) { - return zv::Val(); - } + if (UNEXPECTED(table == NULL)) return zv::Val(); zval *found = zend_symtable_find(Z_ARRVAL_P(table), key.get()); - if (found == NULL) { - return trinarySingleton(PT_TRI_NO); - } + if (found == NULL) return trinarySingleton(PT_TRI_NO); zv::Ref holder = zv::Ref(found).deref(); - if (UNEXPECTED(!pt_check_holder(holder.raw()))) { - return zv::Val(); - } + if (UNEXPECTED(!pt_check_holder(holder.raw()))) return zv::Val(); return zv::Val::copyOf(zv::ObjRef(holder.asObject()).propAt(PT_ETH_PROP_CERTAINTY)); } /* Mirrors ScopeOps::hasVariableType(). */ static zv::Val hasVariableType(zval *scope, zend_string *variableName) { - if (pt_is_superglobal_name(variableName)) { - return trinarySingleton(PT_TRI_YES); - } + if (pt_is_superglobal_name(variableName)) return trinarySingleton(PT_TRI_YES); zval *table = scopeProp(scope, "expressionTypes", sizeof("expressionTypes") - 1); - if (UNEXPECTED(table == NULL)) { - return zv::Val(); - } + if (UNEXPECTED(table == NULL)) return zv::Val(); if (EXPECTED(Z_TYPE_P(table) == IS_ARRAY)) { zv::Str varKey = dollarPrefixed(variableName); zval *found = zend_hash_find(Z_ARRVAL_P(table), varKey.get()); if (found != NULL) { zv::Ref holder = zv::Ref(found).deref(); - if (UNEXPECTED(!pt_check_holder(holder.raw()))) { - return zv::Val(); - } + if (UNEXPECTED(!pt_check_holder(holder.raw()))) return zv::Val(); return zv::Val::copyOf(zv::ObjRef(holder.asObject()).propAt(PT_ETH_PROP_CERTAINTY)); } } bool canExist; - if (UNEXPECTED(!pt_call_scope_bool(scope, "cananyvariableexist", sizeof("cananyvariableexist") - 1, 0, NULL, &canExist))) { - return zv::Val(); - } + if (UNEXPECTED(!pt_call_scope_bool(scope, "cananyvariableexist", sizeof("cananyvariableexist") - 1, 0, NULL, &canExist))) return zv::Val(); return trinarySingleton(canExist ? PT_TRI_MAYBE : PT_TRI_NO); } @@ -283,9 +243,7 @@ class ScopeOps } zend_object *clone = zend_objects_clone_obj(Z_OBJ_P(scope)); - if (UNEXPECTED(EG(exception))) { - return zv::Val(); - } + if (UNEXPECTED(EG(exception))) return zv::Val(); zv::ObjRef cloneObj(clone); setTableProp(cloneObj, off->expression_types, expressionTypes); @@ -322,9 +280,7 @@ class ScopeOps static zv::Val mergeVariableHolders(zv::TableRef ours, zv::TableRef theirs, HashTable *differing) { zv::Arr merged = zv::Arr::create(ours.size()); - if (UNEXPECTED(!mergeVariableHoldersInto(merged, ours, theirs, differing))) { - return zv::Val(); - } + if (UNEXPECTED(!mergeVariableHoldersInto(merged, ours, theirs, differing))) return zv::Val(); return zv::Val(std::move(merged)); } @@ -332,9 +288,7 @@ class ScopeOps static zv::Val finishMerge(zv::TableRef merged, zv::TableRef oursExpr, zv::TableRef theirsExpr, zv::TableRef oursNative, zv::TableRef theirsNative) { zv::Arr filteredMerged; - if (UNEXPECTED(!filterHolders(merged, filteredMerged))) { - return zv::Val(); - } + if (UNEXPECTED(!filterHolders(merged, filteredMerged))) return zv::Val(); zv::Arr oursNativeRemaining = zv::Arr::adoptTable(zend_array_dup(oursNative.table())); zv::Arr theirsNativeRemaining = zv::Arr::adoptTable(zend_array_dup(theirsNative.table())); @@ -345,54 +299,32 @@ class ScopeOps zend_ulong idx = entry.indexKey(); zv::Ref holder = entry.value().deref(); - if (UNEXPECTED(!pt_check_holder(holder.raw()))) { - return zv::Val(); - } + if (UNEXPECTED(!pt_check_holder(holder.raw()))) return zv::Val(); zval *theirNativeSlot = pt_ht_find(theirsNative.table(), key, idx); - if (theirNativeSlot == NULL) { - continue; - } + if (theirNativeSlot == NULL) continue; zval *ourExprSlot = pt_ht_find(oursExpr.table(), key, idx); - if (ourExprSlot == NULL) { - continue; - } + if (ourExprSlot == NULL) continue; zval *theirExprSlot = pt_ht_find(theirsExpr.table(), key, idx); - if (theirExprSlot == NULL) { - continue; - } + if (theirExprSlot == NULL) continue; bool equal; { zv::Ref ourExprHolder = zv::Ref(ourExprSlot).deref(); - if (UNEXPECTED(!pt_check_holder(ourExprHolder.raw()))) { - return zv::Val(); - } - if (UNEXPECTED(!pt_holder_equals(holder.raw(), ourExprHolder.raw(), &equal))) { - return zv::Val(); - } - } - if (!equal) { - continue; + if (UNEXPECTED(!pt_check_holder(ourExprHolder.raw()))) return zv::Val(); + if (UNEXPECTED(!pt_holder_equals(holder.raw(), ourExprHolder.raw(), &equal))) return zv::Val(); } + if (!equal) continue; { zv::Ref theirNativeHolder = zv::Ref(theirNativeSlot).deref(); zv::Ref theirExprHolder = zv::Ref(theirExprSlot).deref(); - if (UNEXPECTED(!pt_check_holder(theirNativeHolder.raw())) || UNEXPECTED(!pt_check_holder(theirExprHolder.raw()))) { - return zv::Val(); - } - if (UNEXPECTED(!pt_holder_equals(theirNativeHolder.raw(), theirExprHolder.raw(), &equal))) { - return zv::Val(); - } - } - if (!equal) { - continue; + if (UNEXPECTED(!pt_check_holder(theirNativeHolder.raw())) || UNEXPECTED(!pt_check_holder(theirExprHolder.raw()))) return zv::Val(); + if (UNEXPECTED(!pt_holder_equals(theirNativeHolder.raw(), theirExprHolder.raw(), &equal))) return zv::Val(); } + if (!equal) continue; zval *mergedHolder = pt_ht_find(filteredMerged.table(), key, idx); - if (mergedHolder == NULL) { - continue; - } + if (mergedHolder == NULL) continue; tableUpdateCopy(mergedNative.table(), key, idx, zv::Ref(mergedHolder)); pt_ht_del(oursNativeRemaining.table(), key, idx); @@ -402,13 +334,9 @@ class ScopeOps /* mergedNative += filter(mergeVariableHolders(oursRemaining, theirsRemaining)) */ { zv::Val remainingMerged = mergeVariableHolders(zv::TableRef(oursNativeRemaining.table()), zv::TableRef(theirsNativeRemaining.table()), NULL); - if (UNEXPECTED(remainingMerged.isUndef())) { - return zv::Val(); - } + if (UNEXPECTED(remainingMerged.isUndef())) return zv::Val(); zv::Arr remainingFiltered; - if (UNEXPECTED(!filterHolders(zv::TableRef(Z_ARRVAL_P(remainingMerged.raw())), remainingFiltered))) { - return zv::Val(); - } + if (UNEXPECTED(!filterHolders(zv::TableRef(Z_ARRVAL_P(remainingMerged.raw())), remainingFiltered))) return zv::Val(); for (auto entry : zv::ArrRef(remainingFiltered.raw())) { tableUpdateCopy(mergedNative.table(), entry.stringKeyOrNull(), entry.indexKey(), entry.value()); } @@ -430,32 +358,24 @@ class ScopeOps zend_ulong idx = entry.indexKey(); zval *otherHoldersSlot = pt_ht_find(theirs.table(), key, idx); - if (otherHoldersSlot == NULL) { - continue; - } + if (otherHoldersSlot == NULL) continue; zv::Ref holders = entry.value().deref(); zv::Ref otherHolders = zv::Ref(otherHoldersSlot).deref(); - if (!holders.isArray() || !otherHolders.isArray()) { - continue; - } + if (!holders.isArray() || !otherHolders.isArray()) continue; HashTable *otherTable = otherHolders.asArrayTable(); zv::Arr intersected; /* stays UNDEF until the first shared holder */ for (auto holderEntry : zv::TableRef(holders.asArrayTable())) { zend_string *holderKey = holderEntry.stringKeyOrNull(); zend_ulong holderIdx = holderEntry.indexKey(); - if (!pt_ht_exists(otherTable, holderKey, holderIdx)) { - continue; - } + if (!pt_ht_exists(otherTable, holderKey, holderIdx)) continue; if (intersected.isUndef()) { intersected = zv::Arr::create(0); } tableAddNewCopy(intersected.table(), holderKey, holderIdx, holderEntry.value()); } - if (intersected.isUndef()) { - continue; - } + if (intersected.isUndef()) continue; tableAddNew(result.table(), key, idx, std::move(intersected)); } @@ -472,9 +392,7 @@ class ScopeOps { zend_class_entry *virtualNodeCe = pt_class(PT_CLASS_VIRTUAL_NODE); zend_class_entry *neverTypeCe = pt_class(PT_CLASS_NEVER_TYPE); - if (UNEXPECTED(virtualNodeCe == NULL || neverTypeCe == NULL)) { - return zv::Val(); - } + if (UNEXPECTED(virtualNodeCe == NULL || neverTypeCe == NULL)) return zv::Val(); /* A guard is only ever consumed paired with a target: a *different* key * in the first target loop below, any key in the second one. Deriving a @@ -494,9 +412,7 @@ class ScopeOps if (ourSlot == NULL) { if (mergedSlot != NULL) { zv::Ref mergedHolder = zv::Ref(mergedSlot).deref(); - if (UNEXPECTED(!pt_check_holder(mergedHolder.raw()))) { - return zv::Val(); - } + if (UNEXPECTED(!pt_check_holder(mergedHolder.raw()))) return zv::Val(); if (!instanceof_function(holderExpr(mergedHolder)->ce, virtualNodeCe)) { hasUndefinedTarget = true; } @@ -504,29 +420,19 @@ class ScopeOps continue; } zv::Ref holder = zv::Ref(ourSlot).deref(); - if (UNEXPECTED(!pt_check_holder(holder.raw()))) { - return zv::Val(); - } - if (instanceof_function(holderExpr(holder)->ce, virtualNodeCe)) { - continue; - } + if (UNEXPECTED(!pt_check_holder(holder.raw()))) return zv::Val(); + if (instanceof_function(holderExpr(holder)->ce, virtualNodeCe)) continue; if (mergedSlot != NULL) { zv::Ref mergedHolder = zv::Ref(mergedSlot).deref(); bool equal; - if (UNEXPECTED(!pt_holder_equals(mergedHolder.raw(), holder.raw(), &equal))) { - return zv::Val(); - } - if (equal) { - continue; - } + if (UNEXPECTED(!pt_holder_equals(mergedHolder.raw(), holder.raw(), &equal))) return zv::Val(); + if (equal) continue; } zval targetTrue; ZVAL_TRUE(&targetTrue); pt_ht_update(targets.table(), key, idx, &targetTrue); } - if (!hasUndefinedTarget && targets.size() == 0) { - return zv::Arr::copyOfTable(conditional.table()); - } + if (!hasUndefinedTarget && targets.size() == 0) return zv::Arr::copyOfTable(conditional.table()); bool onlySelfIsTarget = !hasUndefinedTarget && targets.size() == 1; zv::ScratchTable guardsToExclude(8); @@ -545,45 +451,27 @@ class ScopeOps zend_ulong idx = diffEntry.indexKey(); zval *theirSlot = pt_ht_find(theirs.table(), key, idx); - if (theirSlot == NULL) { - continue; - } + if (theirSlot == NULL) continue; zval *mergedSlot = pt_ht_find(merged.table(), key, idx); - if (mergedSlot == NULL) { - continue; - } + if (mergedSlot == NULL) continue; zv::Ref holder = zv::Ref(theirSlot).deref(); - if (UNEXPECTED(!pt_check_holder(holder.raw()))) { - return zv::Val(); - } + if (UNEXPECTED(!pt_check_holder(holder.raw()))) return zv::Val(); bool equalTypes; { zv::Ref mergedHolder = zv::Ref(mergedSlot).deref(); - if (UNEXPECTED(!pt_check_holder(mergedHolder.raw()))) { - return zv::Val(); - } - if (UNEXPECTED(!pt_holder_equal_types(mergedHolder.raw(), holder.raw(), &equalTypes))) { - return zv::Val(); - } - } - if (!equalTypes) { - continue; + if (UNEXPECTED(!pt_check_holder(mergedHolder.raw()))) return zv::Val(); + if (UNEXPECTED(!pt_holder_equal_types(mergedHolder.raw(), holder.raw(), &equalTypes))) return zv::Val(); } + if (!equalTypes) continue; zval *ourSlot = pt_ht_find(ours.table(), key, idx); if (ourSlot != NULL) { zv::Ref ourHolder = zv::Ref(ourSlot).deref(); - if (UNEXPECTED(!pt_check_holder(ourHolder.raw()))) { - return zv::Val(); - } + if (UNEXPECTED(!pt_check_holder(ourHolder.raw()))) return zv::Val(); if (pt_holder_certainty_value(ourHolder.asObject()) != pt_holder_certainty_value(holder.asObject())) { bool ourEqualTypes; - if (UNEXPECTED(!pt_holder_equal_types(ourHolder.raw(), holder.raw(), &ourEqualTypes))) { - return zv::Val(); - } - if (ourEqualTypes) { - continue; - } + if (UNEXPECTED(!pt_holder_equal_types(ourHolder.raw(), holder.raw(), &ourEqualTypes))) return zv::Val(); + if (ourEqualTypes) continue; } } @@ -598,26 +486,14 @@ class ScopeOps zend_ulong idx = diffEntry.indexKey(); zval *ourSlot = pt_ht_find(ours.table(), key, idx); - if (ourSlot == NULL) { - continue; - } + if (ourSlot == NULL) continue; zv::Ref holder = zv::Ref(ourSlot).deref(); - if (UNEXPECTED(!pt_check_holder(holder.raw()))) { - return zv::Val(); - } - if (instanceof_function(holderExpr(holder)->ce, virtualNodeCe)) { - continue; - } + if (UNEXPECTED(!pt_check_holder(holder.raw()))) return zv::Val(); + if (instanceof_function(holderExpr(holder)->ce, virtualNodeCe)) continue; zval *mergedSlot = pt_ht_find(merged.table(), key, idx); - if (mergedSlot == NULL) { - continue; - } - if (pt_holder_certainty_value(holder.asObject()) != PT_TRI_YES) { - continue; - } - if (pt_ht_exists(guardsToExclude.table(), key, idx)) { - continue; - } + if (mergedSlot == NULL) continue; + if (pt_holder_certainty_value(holder.asObject()) != PT_TRI_YES) continue; + if (pt_ht_exists(guardsToExclude.table(), key, idx)) continue; zval *theirSlot = pt_ht_find(theirs.table(), key, idx); if (theirSlot == NULL) { /* with no their-branch entry the merged holder keeps our type @@ -626,19 +502,11 @@ class ScopeOps continue; } zv::Ref theirHolder = zv::Ref(theirSlot).deref(); - if (UNEXPECTED(!pt_check_holder(theirHolder.raw()))) { - return zv::Val(); - } - if (pt_holder_certainty_value(theirHolder.asObject()) != PT_TRI_YES) { - continue; - } + if (UNEXPECTED(!pt_check_holder(theirHolder.raw()))) return zv::Val(); + if (pt_holder_certainty_value(theirHolder.asObject()) != PT_TRI_YES) continue; bool equalTypes; - if (UNEXPECTED(!pt_holder_equal_types(holder.raw(), theirHolder.raw(), &equalTypes))) { - return zv::Val(); - } - if (equalTypes) { - continue; - } + if (UNEXPECTED(!pt_holder_equal_types(holder.raw(), theirHolder.raw(), &equalTypes))) return zv::Val(); + if (equalTypes) continue; if (onlySelfIsTarget && pt_ht_exists(targets.table(), key, idx)) { /* the sole target is this very key, which the target loop unsets @@ -649,24 +517,14 @@ class ScopeOps /* the branch set difference — see the twin for why an unchanged * remainder falls back to the merged-type comparison */ zv::Val remainder = typeCombinatorRemove(holderType(holder), holderType(theirHolder)); - if (UNEXPECTED(remainder.isUndef())) { - return zv::Val(); - } - if (remainder.ref().instanceOf(neverTypeCe)) { - continue; - } + if (UNEXPECTED(remainder.isUndef())) return zv::Val(); + if (remainder.ref().instanceOf(neverTypeCe)) continue; { zv::Ref mergedHolder = zv::Ref(mergedSlot).deref(); - if (UNEXPECTED(!pt_check_holder(mergedHolder.raw()))) { - return zv::Val(); - } + if (UNEXPECTED(!pt_check_holder(mergedHolder.raw()))) return zv::Val(); bool mergedEqualsRemainder = pt_types_identical_or_equal(holderType(mergedHolder), remainder.raw()); - if (UNEXPECTED(EG(exception))) { - return zv::Val(); - } - if (mergedEqualsRemainder) { - continue; - } + if (UNEXPECTED(EG(exception))) return zv::Val(); + if (mergedEqualsRemainder) continue; } if (Z_OBJ_P(remainder.raw()) == Z_OBJ_P(holderType(holder))) { @@ -689,9 +547,7 @@ class ScopeOps } } - if (typeGuards.size() == 0) { - return zv::Arr::copyOfTable(conditional.table()); - } + if (typeGuards.size() == 0) return zv::Arr::copyOfTable(conditional.table()); /* Both isSuperTypeOf() results depend only on the guard, not on the * target expression — cache them per guard across the target loop. */ @@ -707,15 +563,11 @@ class ScopeOps zend_ulong idx = targetEntry.indexKey(); zval *ourSlot = pt_ht_find(ours.table(), key, idx); - if (UNEXPECTED(ourSlot == NULL)) { - continue; - } + if (UNEXPECTED(ourSlot == NULL)) continue; zv::Ref holder = zv::Ref(ourSlot).deref(); bool hasSelfGuard = pt_ht_exists(typeGuards.table(), key, idx); - if (typeGuards.size() - (hasSelfGuard ? 1 : 0) == 0) { - continue; - } + if (typeGuards.size() - (hasSelfGuard ? 1 : 0) == 0) continue; bool exprIsGuardExcluded = pt_ht_exists(guardsToExclude.table(), key, idx); for (auto guardEntry : zv::TableRef(typeGuards.table())) { @@ -723,9 +575,7 @@ class ScopeOps zend_ulong guardIdx = guardEntry.indexKey(); zv::Ref guardHolder = guardEntry.value(); - if (sameDualKey(guardKey, guardIdx, key, idx)) { - continue; - } + if (sameDualKey(guardKey, guardIdx, key, idx)) continue; if (exprIsGuardExcluded) { /* a subtype-absorbed target paired with a constant-array @@ -735,16 +585,12 @@ class ScopeOps if (cached != NULL) { isConstantArray = Z_TYPE_P(cached) == IS_TRUE; } else { - if (UNEXPECTED(!isConstantArrayYes(holderType(guardHolder), &isConstantArray))) { - return zv::Val(); - } + if (UNEXPECTED(!isConstantArrayYes(holderType(guardHolder), &isConstantArray))) return zv::Val(); zval cacheVal; ZVAL_BOOL(&cacheVal, isConstantArray); pt_ht_update(guardIsConstantArrayCache.table(), guardKey, guardIdx, &cacheVal); } - if (isConstantArray) { - continue; - } + if (isConstantArray) continue; } zval *theirGuardSlot = pt_ht_find(theirs.table(), guardKey, guardIdx); @@ -764,9 +610,7 @@ class ScopeOps pt_ht_update(guardIsSuperTypeOfTheirExprCache.table(), guardKey, guardIdx, &cacheVal); } - if (guardIsSuperTypeOfTheirExpr == PT_TRI_YES) { - continue; - } + if (guardIsSuperTypeOfTheirExpr == PT_TRI_YES) continue; bool skip = false; zval *theirExprSlot = pt_ht_find(theirs.table(), key, idx); @@ -777,9 +621,7 @@ class ScopeOps } } else if (guardIsSuperTypeOfTheirExpr != PT_TRI_NO) { bool typesEqual = pt_types_identical_or_equal(holderType(holder), holderType(guardHolder)); - if (UNEXPECTED(EG(exception))) { - return zv::Val(); - } + if (UNEXPECTED(EG(exception))) return zv::Val(); if (typesEqual) { skip = true; } @@ -805,15 +647,11 @@ class ScopeOps } } - if (skip) { - continue; - } + if (skip) continue; } } - if (UNEXPECTED(!appendConditional(result, conditional, key, idx, guardKey, guardIdx, guardHolder, holder))) { - return zv::Val(); - } + if (UNEXPECTED(!appendConditional(result, conditional, key, idx, guardKey, guardIdx, guardHolder, holder))) return zv::Val(); } } @@ -823,34 +661,22 @@ class ScopeOps zend_ulong idx = diffEntry.indexKey(); zval *mergedSlot = pt_ht_find(merged.table(), key, idx); - if (mergedSlot == NULL) { - continue; - } - if (pt_ht_exists(ours.table(), key, idx)) { - continue; - } + if (mergedSlot == NULL) continue; + if (pt_ht_exists(ours.table(), key, idx)) continue; zv::Ref mergedHolder = zv::Ref(mergedSlot).deref(); - if (UNEXPECTED(!pt_check_holder(mergedHolder.raw()))) { - return zv::Val(); - } - if (instanceof_function(holderExpr(mergedHolder)->ce, virtualNodeCe)) { - continue; - } + if (UNEXPECTED(!pt_check_holder(mergedHolder.raw()))) return zv::Val(); + if (instanceof_function(holderExpr(mergedHolder)->ce, virtualNodeCe)) continue; for (auto guardEntry : zv::TableRef(typeGuards.table())) { zv::Val noHolder = createNoErrorHolder(zv::ObjRef(mergedHolder.asObject()).propAt(PT_ETH_PROP_EXPR).raw()); - if (UNEXPECTED(noHolder.isUndef())) { - return zv::Val(); - } + if (UNEXPECTED(noHolder.isUndef())) return zv::Val(); if (UNEXPECTED(!appendConditional(result, conditional, key, idx, guardEntry.stringKeyOrNull(), guardEntry.indexKey(), guardEntry.value(), noHolder.ref()))) { return zv::Val(); } } } - if (!result.isUndef()) { - return zv::Val(std::move(result)); - } + if (!result.isUndef()) return zv::Val(std::move(result)); return zv::Arr::copyOfTable(conditional.table()); } @@ -862,9 +688,7 @@ class ScopeOps static zv::Val invalidateMethodsOnExpression(zval *exprPrinter, zend_string *exprStringToInvalidate, zv::TableRef expressionTypes, zv::TableRef nativeExpressionTypes) { zend_class_entry *methodCallCe = pt_class(PT_CLASS_METHOD_CALL); - if (UNEXPECTED(methodCallCe == NULL)) { - return zv::Val(); - } + if (UNEXPECTED(methodCallCe == NULL)) return zv::Val(); bool invalidated = false; zv::Arr resultExpr, resultNative; /* stay UNDEF until the first hit */ @@ -884,28 +708,16 @@ class ScopeOps continue; } zv::Ref holder = entry.value().deref(); - if (UNEXPECTED(!pt_check_holder(holder.raw()))) { - return zv::Val(); - } + if (UNEXPECTED(!pt_check_holder(holder.raw()))) return zv::Val(); zend_object *expr = holderExpr(holder); - if (!instanceof_function(expr->ce, methodCallCe)) { - continue; - } + if (!instanceof_function(expr->ce, methodCallCe)) continue; int32_t varOffset = pt_instance_prop_offset(expr->ce, "var", sizeof("var") - 1); - if (varOffset < 0) { - continue; - } + if (varOffset < 0) continue; zv::Ref var = zv::ObjRef(expr).propAtOffset((uint32_t) varOffset).deref(); - if (!var.isObject()) { - continue; - } + if (!var.isObject()) continue; zv::Str varKey = zv::Str::adopt(pt_node_key(var.asObject(), exprPrinter)); - if (UNEXPECTED(varKey.isNull())) { - return zv::Val(); - } - if (!zend_string_equals(varKey.get(), exprStringToInvalidate)) { - continue; - } + if (UNEXPECTED(varKey.isNull())) return zv::Val(); + if (!zend_string_equals(varKey.get(), exprStringToInvalidate)) continue; if (resultExpr.isUndef()) { resultExpr = zv::Arr::adoptTable(zend_array_dup(expressionTypes.table())); @@ -916,9 +728,7 @@ class ScopeOps invalidated = true; } - if (!invalidated) { - return zv::Val::null(); - } + if (!invalidated) return zv::Val::null(); zv::Arr result = zv::Arr::create(2); result.push(std::move(resultExpr)); @@ -978,9 +788,7 @@ class ScopeOps zv::Ref holder = entry.value().deref(); - if (UNEXPECTED(!pt_check_holder(holder.raw()))) { - return zv::Val(); - } + if (UNEXPECTED(!pt_check_holder(holder.raw()))) return zv::Val(); zend_object *expr = holderExpr(holder); zend_string *entryKey = key != NULL ? key : zend_long_to_str((zend_long) idx); bool failed = false; @@ -989,9 +797,7 @@ class ScopeOps zend_string_release(entryKey); } if (!should) { - if (UNEXPECTED(failed)) { - return zv::Val(); - } + if (UNEXPECTED(failed)) return zv::Val(); continue; } if (resultExpr.isUndef()) { @@ -1009,13 +815,9 @@ class ScopeOps zend_ulong idx = entry.indexKey(); zv::Ref holders = entry.value().deref(); - if (!holders.isArray()) { - continue; - } + if (!holders.isArray()) continue; zv::TableRef holdersTable(holders.asArrayTable()); - if (holdersTable.size() == 0) { - continue; - } + if (holdersTable.size() == 0) continue; /* first holder's type-holder expr decides whole-group invalidation */ if (!canUseKeyPrefilter @@ -1028,19 +830,13 @@ class ScopeOps return zv::Val(); } zv::Ref firstTypeHolder = zv::ObjRef(firstHolder.asObject()).propAt(PT_CEH_PROP_TYPEHOLDER).deref(); - if (UNEXPECTED(!pt_check_holder(firstTypeHolder.raw()))) { - return zv::Val(); - } + if (UNEXPECTED(!pt_check_holder(firstTypeHolder.raw()))) return zv::Val(); zend_object *firstExpr = holderExpr(firstTypeHolder); zv::Str firstKey = zv::Str::adopt(pt_node_key(firstExpr, exprPrinter)); - if (UNEXPECTED(firstKey.isNull())) { - return zv::Val(); - } + if (UNEXPECTED(firstKey.isNull())) return zv::Val(); bool failed = false; bool drop = shouldInvalidate(query, firstKey.get(), firstExpr, requireMoreCharacters, &failed); - if (UNEXPECTED(failed)) { - return zv::Val(); - } + if (UNEXPECTED(failed)) return zv::Val(); if (drop) { invalidated = true; continue; @@ -1080,9 +876,7 @@ class ScopeOps if (conditions.isArray()) { for (auto conditionEntry : zv::TableRef(conditions.asArrayTable())) { zv::Ref conditionHolder = conditionEntry.value().deref(); - if (UNEXPECTED(!pt_check_holder(conditionHolder.raw()))) { - return zv::Val(); - } + if (UNEXPECTED(!pt_check_holder(conditionHolder.raw()))) return zv::Val(); zend_object *conditionExpr = holderExpr(conditionHolder); zend_string *conditionKey = conditionEntry.stringKeyOrNull(); zend_string *conditionKeyStr = conditionKey != NULL ? conditionKey : zend_long_to_str((zend_long) conditionEntry.indexKey()); @@ -1096,9 +890,7 @@ class ScopeOps keep = false; break; } - if (UNEXPECTED(failed)) { - return zv::Val(); - } + if (UNEXPECTED(failed)) return zv::Val(); } } if (keep) { @@ -1115,9 +907,7 @@ class ScopeOps filtered = zv::Arr::create(keptCount); uint32_t copied = 0; for (auto keptEntry : holdersTable) { - if (copied == keptCount) { - break; - } + if (copied == keptCount) break; tableAddNewCopy(filtered.table(), keptEntry.stringKeyOrNull(), keptEntry.indexKey(), keptEntry.value().deref()); copied++; } @@ -1129,15 +919,11 @@ class ScopeOps tableAddNewCopy(resultConditional.table(), key, idx, holders); continue; } - if (zend_hash_num_elements(filtered.table()) == 0) { - continue; - } + if (zend_hash_num_elements(filtered.table()) == 0) continue; tableAddNew(resultConditional.table(), key, idx, std::move(filtered)); } - if (!invalidated) { - return zv::Val::null(); - } + if (!invalidated) return zv::Val::null(); if (resultExpr.isUndef()) { /* only conditional expressions were invalidated */ @@ -1175,9 +961,7 @@ class ScopeOps /* borrowed (points into a property) — copy for the caller */ return zv::Val::string(name); } - if (UNEXPECTED(EG(exception))) { - return zv::Val(); - } + if (UNEXPECTED(EG(exception))) return zv::Val(); return zv::Val::null(); } @@ -1211,13 +995,9 @@ class ScopeOps zend_string *conditionalKey = entry.stringKeyOrNull(); zend_ulong conditionalIdx = entry.indexKey(); - if (pt_ht_exists(conditions.table(), conditionalKey, conditionalIdx)) { - continue; - } + if (pt_ht_exists(conditions.table(), conditionalKey, conditionalIdx)) continue; zv::Ref holders = entry.value().deref(); - if (UNEXPECTED(!holders.isArray())) { - continue; - } + if (UNEXPECTED(!holders.isArray())) continue; zv::TableRef holdersTable(holders.asArrayTable()); /* Pass 1: prefer exact matches */ @@ -1233,9 +1013,7 @@ class ScopeOps continue; } zv::Ref conditionHolders = zv::ObjRef(holder.asObject()).propAt(PT_CEH_PROP_CONDS); - if (UNEXPECTED(!conditionHolders.isArray())) { - continue; - } + if (UNEXPECTED(!conditionHolders.isArray())) continue; bool all = true; for (auto conditionEntry : zv::TableRef(conditionHolders.asArrayTable())) { zval *specifiedSlot = pt_ht_find(specified.table(), conditionEntry.stringKeyOrNull(), conditionEntry.indexKey()); @@ -1245,40 +1023,28 @@ class ScopeOps } zv::Ref conditionHolder = conditionEntry.value().deref(); zv::Ref specifiedHolder = zv::Ref(specifiedSlot).deref(); - if (UNEXPECTED(!pt_check_holder(conditionHolder.raw())) || UNEXPECTED(!pt_check_holder(specifiedHolder.raw()))) { - return zv::Val(); - } + if (UNEXPECTED(!pt_check_holder(conditionHolder.raw())) || UNEXPECTED(!pt_check_holder(specifiedHolder.raw()))) return zv::Val(); bool equal; - if (UNEXPECTED(!pt_holder_equals(conditionHolder.raw(), specifiedHolder.raw(), &equal))) { - return zv::Val(); - } + if (UNEXPECTED(!pt_holder_equals(conditionHolder.raw(), specifiedHolder.raw(), &equal))) return zv::Val(); if (!equal) { all = false; break; } } - if (!all) { - continue; - } + if (!all) continue; recordMatchedCondition(conditions, specified, conditionalKey, conditionalIdx, holder, typeHolder); } - if (pt_ht_exists(conditions.table(), conditionalKey, conditionalIdx)) { - continue; - } + if (pt_ht_exists(conditions.table(), conditionalKey, conditionalIdx)) continue; /* Pass 2: supertype match, only when Pass 1 found nothing */ for (auto holderEntry : holdersTable) { zv::Ref holder = holderEntry.value().deref(); zv::Ref typeHolder = zv::ObjRef(holder.asObject()).propAt(PT_CEH_PROP_TYPEHOLDER); - if (pt_holder_certainty_value(typeHolder.asObject()) == PT_TRI_NO) { - continue; - } + if (pt_holder_certainty_value(typeHolder.asObject()) == PT_TRI_NO) continue; zv::Ref conditionHolders = zv::ObjRef(holder.asObject()).propAt(PT_CEH_PROP_CONDS); - if (UNEXPECTED(!conditionHolders.isArray())) { - continue; - } + if (UNEXPECTED(!conditionHolders.isArray())) continue; bool all = true; for (auto conditionEntry : zv::TableRef(conditionHolders.asArrayTable())) { zval *specifiedSlot = pt_ht_find(specified.table(), conditionEntry.stringKeyOrNull(), conditionEntry.indexKey()); @@ -1291,25 +1057,19 @@ class ScopeOps /* Pass 1 validates only the entries it reaches before * its first mismatch, so these can be unchecked here; * the twin raises a catchable Error on wrong types */ - if (UNEXPECTED(!pt_check_holder(conditionHolder.raw()) || !pt_check_holder(specifiedHolder.raw()))) { - return zv::Val(); - } + if (UNEXPECTED(!pt_check_holder(conditionHolder.raw()) || !pt_check_holder(specifiedHolder.raw()))) return zv::Val(); if (pt_holder_certainty_value(conditionHolder.asObject()) != pt_holder_certainty_value(specifiedHolder.asObject())) { all = false; break; } zend_long superTypeOf; - if (UNEXPECTED(!isSuperTypeOfValue(holderType(conditionHolder), holderType(specifiedHolder), &superTypeOf))) { - return zv::Val(); - } + if (UNEXPECTED(!isSuperTypeOfValue(holderType(conditionHolder), holderType(specifiedHolder), &superTypeOf))) return zv::Val(); if (superTypeOf != PT_TRI_YES) { all = false; break; } } - if (!all) { - continue; - } + if (!all) continue; recordMatchedCondition(conditions, specified, conditionalKey, conditionalIdx, holder, typeHolder); } @@ -1345,9 +1105,7 @@ class ScopeOps static zval *scopeArrayProp(zval *scope, const char *name, size_t len) { zval *table = scopeProp(scope, name, len); - if (UNEXPECTED(table == NULL)) { - return NULL; - } + if (UNEXPECTED(table == NULL)) return NULL; if (UNEXPECTED(Z_TYPE_P(table) != IS_ARRAY)) { zend_throw_error(NULL, "phpstan_turbo: %s is not an array", name); return NULL; @@ -1395,18 +1153,14 @@ class ScopeOps /* $obj->prop = [] — a memo reset to the fresh-constructor default */ static void resetToEmptyArray(zv::ObjRef obj, int32_t offset) { - if (offset < 0) { - return; - } + if (offset < 0) return; obj.propAtOffset((uint32_t) offset).assign(zv::Arr::empty()); } /* $obj->prop = null — a memo reset to the fresh-constructor default */ static void resetToNull(zv::ObjRef obj, int32_t offset) { - if (offset < 0) { - return; - } + if (offset < 0) return; obj.propAtOffset((uint32_t) offset).assign(zv::Val::null()); } @@ -1439,9 +1193,7 @@ class ScopeOps /* $differing[$key] = true (marker insert, overwrites) */ static void markDiffering(HashTable *differing, zend_string *skey, zend_ulong idx) { - if (differing == NULL) { - return; - } + if (differing == NULL) return; zval trueZv; ZVAL_TRUE(&trueZv); pt_ht_update(differing, skey, idx, &trueZv); @@ -1455,35 +1207,25 @@ class ScopeOps zend_ulong idx = entry.indexKey(); zv::Ref holder = entry.value().deref(); - if (UNEXPECTED(!pt_check_holder(holder.raw()))) { - return false; - } + if (UNEXPECTED(!pt_check_holder(holder.raw()))) return false; zval *theirSlot = pt_ht_find(theirs.table(), key, idx); if (theirSlot != NULL) { zv::Ref theirHolder = zv::Ref(theirSlot).deref(); - if (UNEXPECTED(!pt_check_holder(theirHolder.raw()))) { - return false; - } + if (UNEXPECTED(!pt_check_holder(theirHolder.raw()))) return false; if (holder.asObject() == theirHolder.asObject()) { tableAddNewCopy(merged.table(), key, idx, holder); } else { markDiffering(differing, key, idx); zval andHolder; - if (UNEXPECTED(!pt_holder_and(holder.raw(), theirHolder.raw(), &andHolder))) { - return false; - } + if (UNEXPECTED(!pt_holder_and(holder.raw(), theirHolder.raw(), &andHolder))) return false; tableAddNew(merged.table(), key, idx, zv::Val::adopt(andHolder)); } } else { markDiffering(differing, key, idx); bool containsSuperGlobal = pt_expr_contains_superglobal(holderExpr(holder)); - if (UNEXPECTED(EG(exception))) { - return false; - } - if (containsSuperGlobal) { - continue; - } + if (UNEXPECTED(EG(exception))) return false; + if (containsSuperGlobal) continue; tableAddNew(merged.table(), key, idx, createMaybeHolder(holder)); } } @@ -1492,21 +1234,13 @@ class ScopeOps zend_string *key = entry.stringKeyOrNull(); zend_ulong idx = entry.indexKey(); - if (pt_ht_exists(merged.table(), key, idx)) { - continue; - } + if (pt_ht_exists(merged.table(), key, idx)) continue; markDiffering(differing, key, idx); zv::Ref holder = entry.value().deref(); - if (UNEXPECTED(!pt_check_holder(holder.raw()))) { - return false; - } + if (UNEXPECTED(!pt_check_holder(holder.raw()))) return false; bool containsSuperGlobal = pt_expr_contains_superglobal(holderExpr(holder)); - if (UNEXPECTED(EG(exception))) { - return false; - } - if (containsSuperGlobal) { - continue; - } + if (UNEXPECTED(EG(exception))) return false; + if (containsSuperGlobal) continue; tableAddNew(merged.table(), key, idx, createMaybeHolder(holder)); } @@ -1523,9 +1257,7 @@ class ScopeOps zend_class_entry *variableCe = pt_class(PT_CLASS_VARIABLE); zend_class_entry *funcCallCe = pt_class(PT_CLASS_FUNC_CALL); zend_class_entry *virtualNodeCe = pt_class(PT_CLASS_VIRTUAL_NODE); - if (UNEXPECTED(variableCe == NULL || funcCallCe == NULL || virtualNodeCe == NULL)) { - return false; - } + if (UNEXPECTED(variableCe == NULL || funcCallCe == NULL || virtualNodeCe == NULL)) return false; zend_class_entry *exprCe = zv::ObjRef(holder).propAt(PT_ETH_PROP_EXPR).asObject()->ce; *keep = instanceof_function(exprCe, variableCe) || instanceof_function(exprCe, funcCallCe) @@ -1539,13 +1271,9 @@ class ScopeOps filtered = zv::Arr::create(input.size()); for (auto entry : input) { zv::Ref holder = entry.value().deref(); - if (UNEXPECTED(!pt_check_holder(holder.raw()))) { - return false; - } + if (UNEXPECTED(!pt_check_holder(holder.raw()))) return false; bool keep; - if (UNEXPECTED(!filterKeepsHolder(holder.asObject(), &keep))) { - return false; - } + if (UNEXPECTED(!filterKeepsHolder(holder.asObject(), &keep))) return false; if (keep) { tableAddNewCopy(filtered.table(), entry.stringKeyOrNull(), entry.indexKey(), holder); } @@ -1570,9 +1298,7 @@ class ScopeOps static zv::Val typeCombinatorRemove(zval *fromType, zval *typeToRemove) { zval retval; - if (UNEXPECTED(!pt_type_combinator_binary("remove", sizeof("remove") - 1, fromType, typeToRemove, &retval))) { - return zv::Val(); - } + if (UNEXPECTED(!pt_type_combinator_binary("remove", sizeof("remove") - 1, fromType, typeToRemove, &retval))) return zv::Val(); zv::Val result = zv::Val::adopt(retval); if (UNEXPECTED(!result.ref().isObject())) { zend_throw_error(NULL, "phpstan_turbo: TypeCombinator::remove did not return an object"); @@ -1586,9 +1312,7 @@ class ScopeOps { zval arg, retval; ZVAL_COPY_VALUE(&arg, otherType); - if (UNEXPECTED(!callObjectMethod(type, "issupertypeof", sizeof("issupertypeof") - 1, 1, &arg, &retval))) { - return false; - } + if (UNEXPECTED(!callObjectMethod(type, "issupertypeof", sizeof("issupertypeof") - 1, 1, &arg, &retval))) return false; zv::Val result = zv::Val::adopt(retval); if (UNEXPECTED(!result.ref().isObject())) { zend_throw_error(NULL, "phpstan_turbo: isSuperTypeOf did not return an object"); @@ -1612,9 +1336,7 @@ class ScopeOps static bool isConstantArrayYes(zval *type, bool *out) { zval retval; - if (UNEXPECTED(!callObjectMethod(type, "isconstantarray", sizeof("isconstantarray") - 1, 0, NULL, &retval))) { - return false; - } + if (UNEXPECTED(!callObjectMethod(type, "isconstantarray", sizeof("isconstantarray") - 1, 0, NULL, &retval))) return false; zv::Val result = zv::Val::adopt(retval); if (UNEXPECTED(!result.ref().instanceOf(pt_ce_trinary))) { zend_throw_error(NULL, "phpstan_turbo: isConstantArray did not return a TrinaryLogic"); @@ -1628,17 +1350,13 @@ class ScopeOps static zv::Val createNoErrorHolder(zval *exprSlot) { zend_class_entry *errorTypeCe = pt_class(PT_CLASS_ERROR_TYPE); - if (UNEXPECTED(errorTypeCe == NULL)) { - return zv::Val(); - } + if (UNEXPECTED(errorTypeCe == NULL)) return zv::Val(); zval errorTypeRaw; object_init_ex(&errorTypeRaw, errorTypeCe); zv::Val errorType = zv::Val::adopt(errorTypeRaw); if (errorTypeCe->constructor != NULL) { zend_call_known_instance_method(errorTypeCe->constructor, errorType.ref().asObject(), NULL, 0, NULL); - if (UNEXPECTED(EG(exception))) { - return zv::Val(); - } + if (UNEXPECTED(EG(exception))) return zv::Val(); } /* pt_holder_create copies the type; the local ErrorType ref is released */ zval holder; @@ -1657,9 +1375,7 @@ class ScopeOps tableAddNewCopy(conditions.table(), guardKey, guardIdx, guardHolder); zv::Str cehKey = zv::Str::adopt(pt_ceh_key_build(conditions.table(), typeHolder.raw())); - if (UNEXPECTED(cehKey.isNull())) { - return false; - } + if (UNEXPECTED(cehKey.isNull())) return false; zval cehRaw; object_init_ex(&cehRaw, pt_ce_cond_expr_holder); @@ -1732,9 +1448,7 @@ class ScopeOps const char *end = pos + ZSTR_LEN(key); for (;;) { const char *found = zend_memnstr(pos, "__phpstan", sizeof("__phpstan") - 1, end); - if (found == NULL) { - return false; - } + if (found == NULL) return false; bool isCompositional = false; for (const auto &candidate : compositionalPrefixes) { if ((size_t) (end - found) >= candidate.len && memcmp(found, candidate.prefix, candidate.len) == 0) { @@ -1743,9 +1457,7 @@ class ScopeOps break; } } - if (!isCompositional) { - return true; - } + if (!isCompositional) return true; } } @@ -1832,16 +1544,12 @@ class ScopeOps return false; } zv::Val isRet = zv::Val::adopt(isRetRaw); - if (zend_is_true(isRet.raw())) { - return true; - } + if (zend_is_true(isRet.raw())) return true; } } } - if (!instanceof_function(node->ce, ctx->target_ce)) { - return false; - } + if (!instanceof_function(node->ce, ctx->target_ce)) return false; zv::Str nodeKey = zv::Str::adopt(pt_node_key(node, ctx->expr_printer)); if (UNEXPECTED(nodeKey.isNull())) { @@ -1868,40 +1576,26 @@ class ScopeOps return false; } - if (!instanceof_function(expr->ce, propertyFetchCe) && !instanceof_function(expr->ce, nullsafeCe)) { - return false; - } + if (!instanceof_function(expr->ce, propertyFetchCe) && !instanceof_function(expr->ce, nullsafeCe)) return false; while (instanceof_function(expr->ce, propertyFetchCe) || instanceof_function(expr->ce, nullsafeCe)) { int32_t nameOffset = pt_instance_prop_offset(expr->ce, "name", sizeof("name") - 1); int32_t varOffset = pt_instance_prop_offset(expr->ce, "var", sizeof("var") - 1); - if (UNEXPECTED(nameOffset < 0 || varOffset < 0)) { - return false; - } + if (UNEXPECTED(nameOffset < 0 || varOffset < 0)) return false; zv::Ref name = zv::ObjRef(expr).propAtOffset((uint32_t) nameOffset).deref(); - if (!name.isObject()) { - return false; - } + if (!name.isObject()) return false; zend_class_entry *nameCe = name.asObject()->ce; if (!instanceof_function(nameCe, identifierCe)) { - if (!instanceof_function(nameCe, variableCe)) { - return false; - } + if (!instanceof_function(nameCe, variableCe)) return false; pt_node_class_info *nameInfo = pt_get_node_class_info(nameCe); - if (nameInfo == NULL || nameInfo->name_offset < 0) { - return false; - } + if (nameInfo == NULL || nameInfo->name_offset < 0) return false; zv::Ref variableName = zv::ObjRef(name.asObject()).propAtOffset((uint32_t) nameInfo->name_offset).deref(); - if (!variableName.isString()) { - return false; - } + if (!variableName.isString()) return false; } zv::Ref var = zv::ObjRef(expr).propAtOffset((uint32_t) varOffset).deref(); - if (!var.isObject()) { - return false; - } + if (!var.isObject()) return false; expr = var.asObject(); } @@ -1944,26 +1638,20 @@ class ScopeOps int32_t assignedExprOffset = pt_instance_prop_offset(expr->ce, "assignedExpr", sizeof("assignedExpr") - 1); if (variableNameOffset >= 0) { zv::Ref variableName = zv::ObjRef(expr).propAtOffset((uint32_t) variableNameOffset).deref(); - if (variableName.isString() && zend_string_equals(variableName.asString(), name)) { - return false; - } + if (variableName.isString() && zend_string_equals(variableName.asString(), name)) return false; } if (exprOffset >= 0) { zv::Ref innerExpr = zv::ObjRef(expr).propAtOffset((uint32_t) exprOffset).deref(); if (innerExpr.isObject()) { zend_string *root = intertwinedRootVariableName(innerExpr.asObject()); - if (root != NULL && zend_string_equals(root, name)) { - return false; - } + if (root != NULL && zend_string_equals(root, name)) return false; } } if (assignedExprOffset >= 0) { zv::Ref assignedExpr = zv::ObjRef(expr).propAtOffset((uint32_t) assignedExprOffset).deref(); if (assignedExpr.isObject()) { zend_string *root = intertwinedRootVariableName(assignedExpr.asObject()); - if (root != NULL && zend_string_equals(root, name)) { - return false; - } + if (root != NULL && zend_string_equals(root, name)) return false; } } } @@ -1971,9 +1659,7 @@ class ScopeOps } } - if (requireMoreCharacters && zend_string_equals(query.exprStringToInvalidate, exprString)) { - return false; - } + if (requireMoreCharacters && zend_string_equals(query.exprStringToInvalidate, exprString)) return false; /* Variables will not contain traversable expressions: direct compare */ { @@ -1995,9 +1681,7 @@ class ScopeOps if (query.keepPropertyFetches) { bool isChain = isPropertyFetchChainOn(expr, query.exprStringToInvalidate, query.exprPrinter, failed); - if (UNEXPECTED(*failed) || isChain) { - return false; - } + if (UNEXPECTED(*failed) || isChain) return false; } /* Compositional-key substring gate */ @@ -2030,9 +1714,7 @@ class ScopeOps *failed = true; return false; } - if (found == NULL) { - return false; - } + if (found == NULL) return false; } /* Post-checks calling back into the scope (rare paths) */ @@ -2051,9 +1733,7 @@ class ScopeOps *failed = true; return false; } - if (isReadonly) { - return false; - } + if (isReadonly) return false; } if (query.invalidatingClass != NULL && Z_TYPE_P(query.invalidatingClass) == IS_OBJECT) { @@ -2065,9 +1745,7 @@ class ScopeOps *failed = true; return false; } - if (isPrivateOfOtherClass) { - return false; - } + if (isPrivateOfOtherClass) return false; } } @@ -2080,28 +1758,20 @@ class ScopeOps zend_class_entry *variableCe = pt_class(PT_CLASS_VARIABLE); zend_class_entry *arrayDimFetchCe = pt_class(PT_CLASS_ARRAY_DIM_FETCH); - if (UNEXPECTED(variableCe == NULL || arrayDimFetchCe == NULL)) { - return NULL; - } + if (UNEXPECTED(variableCe == NULL || arrayDimFetchCe == NULL)) return NULL; for (;;) { if (instanceof_function(expr->ce, variableCe)) { pt_node_class_info *info = pt_get_node_class_info(expr->ce); - if (info == NULL || info->name_offset < 0) { - return NULL; - } + if (info == NULL || info->name_offset < 0) return NULL; zv::Ref name = zv::ObjRef(expr).propAtOffset((uint32_t) info->name_offset).deref(); return name.isString() ? name.asString() : NULL; /* borrowed */ } if (instanceof_function(expr->ce, arrayDimFetchCe)) { int32_t varOffset = pt_instance_prop_offset(expr->ce, "var", sizeof("var") - 1); - if (varOffset < 0) { - return NULL; - } + if (varOffset < 0) return NULL; zv::Ref var = zv::ObjRef(expr).propAtOffset((uint32_t) varOffset).deref(); - if (!var.isObject()) { - return NULL; - } + if (!var.isObject()) return NULL; expr = var.asObject(); continue; } @@ -2181,9 +1851,7 @@ void pt_register_scope_ops() differing = Z_ARRVAL_P(inner); } zv::Val result = ScopeOps::mergeVariableHolders(zv::TableRef(ours), zv::TableRef(theirs), differing); - if (UNEXPECTED(result.isUndef())) { - RETURN_THROWS(); - } + if (UNEXPECTED(result.isUndef())) RETURN_THROWS(); result.intoReturnValue(return_value); }); @@ -2197,9 +1865,7 @@ void pt_register_scope_ops() Z_PARAM_ARRAY_HT(theirs_native) ZEND_PARSE_PARAMETERS_END(); zv::Val result = ScopeOps::finishMerge(zv::TableRef(merged), zv::TableRef(ours_expr), zv::TableRef(theirs_expr), zv::TableRef(ours_native), zv::TableRef(theirs_native)); - if (UNEXPECTED(result.isUndef())) { - RETURN_THROWS(); - } + if (UNEXPECTED(result.isUndef())) RETURN_THROWS(); result.intoReturnValue(return_value); }); @@ -2233,9 +1899,7 @@ void pt_register_scope_ops() ZEND_PARSE_PARAMETERS_END(); pt_init_strs(); zv::Val result = ScopeOps::invalidateExpressionEntries(scope, expr_printer, invalidate_str, expr_to_invalidate, require_more_characters, invalidating_class, zv::TableRef(expression_types), zv::TableRef(native_expression_types), zv::TableRef(conditional_expressions), keep_property_fetches); - if (UNEXPECTED(result.isUndef())) { - RETURN_THROWS(); - } + if (UNEXPECTED(result.isUndef())) RETURN_THROWS(); result.intoReturnValue(return_value); }); @@ -2259,9 +1923,7 @@ void pt_register_scope_ops() pt_init_strs(); bool failed = false; bool result = ScopeOps::shouldInvalidateExpression(scope, expr_printer, invalidate_str, expr_to_invalidate, Z_OBJ_P(expr), expr_string, require_more_characters, invalidating_class, keep_property_fetches, &failed); - if (UNEXPECTED(failed)) { - RETURN_THROWS(); - } + if (UNEXPECTED(failed)) RETURN_THROWS(); RETURN_BOOL(result); }); @@ -2271,9 +1933,7 @@ void pt_register_scope_ops() Z_PARAM_OBJECT(expr) ZEND_PARSE_PARAMETERS_END(); zv::Val result = ScopeOps::getIntertwinedRefRootVariableName(Z_OBJ_P(expr)); - if (UNEXPECTED(result.isUndef())) { - RETURN_THROWS(); - } + if (UNEXPECTED(result.isUndef())) RETURN_THROWS(); result.intoReturnValue(return_value); }); @@ -2284,9 +1944,7 @@ void pt_register_scope_ops() Z_PARAM_ARRAY_HT(specified_input) ZEND_PARSE_PARAMETERS_END(); zv::Val result = ScopeOps::matchConditionalExpressions(zv::TableRef(conditional), zv::TableRef(specified_input)); - if (UNEXPECTED(result.isUndef())) { - RETURN_THROWS(); - } + if (UNEXPECTED(result.isUndef())) RETURN_THROWS(); result.intoReturnValue(return_value); }); @@ -2300,9 +1958,7 @@ void pt_register_scope_ops() Z_PARAM_ARRAY_HT(differing_keys) ZEND_PARSE_PARAMETERS_END(); zv::Val result = ScopeOps::createConditionalExpressions(zv::TableRef(conditional), zv::TableRef(ours), zv::TableRef(theirs), zv::TableRef(merged), zv::TableRef(differing_keys)); - if (UNEXPECTED(result.isUndef())) { - RETURN_THROWS(); - } + if (UNEXPECTED(result.isUndef())) RETURN_THROWS(); result.intoReturnValue(return_value); }); @@ -2313,9 +1969,7 @@ void pt_register_scope_ops() Z_PARAM_OBJECT(expr_printer) ZEND_PARSE_PARAMETERS_END(); zv::Val result = ScopeOps::nodeKey(Z_OBJ_P(node), expr_printer); - if (UNEXPECTED(result.isUndef())) { - RETURN_THROWS(); - } + if (UNEXPECTED(result.isUndef())) RETURN_THROWS(); result.intoReturnValue(return_value); }); @@ -2328,9 +1982,7 @@ void pt_register_scope_ops() ZEND_PARSE_PARAMETERS_END(); zend_string *key = NULL; zv::Val result = ScopeOps::getTypeFromCache(scope, Z_OBJ_P(node), &key); - if (UNEXPECTED(result.isUndef())) { - RETURN_THROWS(); - } + if (UNEXPECTED(result.isUndef())) RETURN_THROWS(); if (key != NULL) { /* hand the computed key to the by-ref parameter (hit and miss) */ if (Z_ISREF_P(key_out)) { @@ -2350,9 +2002,7 @@ void pt_register_scope_ops() Z_PARAM_STR(variable_name) ZEND_PARSE_PARAMETERS_END(); zv::Val result = ScopeOps::hasVariableType(scope, variable_name); - if (UNEXPECTED(result.isUndef())) { - RETURN_THROWS(); - } + if (UNEXPECTED(result.isUndef())) RETURN_THROWS(); result.intoReturnValue(return_value); }); @@ -2373,9 +2023,7 @@ void pt_register_scope_ops() Z_PARAM_BOOL(after_extract_call) ZEND_PARSE_PARAMETERS_END(); zv::Val result = ScopeOps::scopeWith(scope, expression_types, native_expression_types, conditional_expressions, currently_assigned, currently_allowed_undefined, in_function_calls_stack, in_first_level_statement, after_extract_call); - if (UNEXPECTED(result.isUndef())) { - RETURN_THROWS(); - } + if (UNEXPECTED(result.isUndef())) RETURN_THROWS(); result.intoReturnValue(return_value); }); @@ -2391,9 +2039,7 @@ void pt_register_scope_ops() ZEND_PARSE_PARAMETERS_END(); pt_init_strs(); zv::Val result = ScopeOps::invalidateMethodsOnExpression(expr_printer, invalidate_str, zv::TableRef(expression_types), zv::TableRef(native_expression_types)); - if (UNEXPECTED(result.isUndef())) { - RETURN_THROWS(); - } + if (UNEXPECTED(result.isUndef())) RETURN_THROWS(); result.intoReturnValue(return_value); }); @@ -2406,9 +2052,7 @@ void pt_register_scope_ops() Z_PARAM_STR(expr_string) ZEND_PARSE_PARAMETERS_END(); zv::Val result = ScopeOps::expressionTypeByKey(scope, Z_OBJ_P(node), expr_string); - if (UNEXPECTED(result.isUndef())) { - RETURN_THROWS(); - } + if (UNEXPECTED(result.isUndef())) RETURN_THROWS(); result.intoReturnValue(return_value); }); @@ -2420,9 +2064,7 @@ void pt_register_scope_ops() Z_PARAM_OBJECT(expr_printer) ZEND_PARSE_PARAMETERS_END(); zv::Val result = ScopeOps::hasExpressionType(scope, Z_OBJ_P(node), expr_printer); - if (UNEXPECTED(result.isUndef())) { - RETURN_THROWS(); - } + if (UNEXPECTED(result.isUndef())) RETURN_THROWS(); result.intoReturnValue(return_value); }); diff --git a/turbo-ext/src/Shadow.cpp b/turbo-ext/src/Shadow.cpp index 6e7401e06ff..d29ede7bef3 100644 --- a/turbo-ext/src/Shadow.cpp +++ b/turbo-ext/src/Shadow.cpp @@ -68,18 +68,14 @@ static const char *pt_short_name(const char *fqcn) /* the declared name: the twin's real name, or the prefixed short name */ static std::string pt_shadow_declared_name(const char *realName, zend_string *prefix) { - if (prefix == NULL) { - return realName; - } + if (prefix == NULL) return realName; return std::string(ZSTR_VAL(prefix), ZSTR_LEN(prefix)) + pt_short_name(realName); } static reg::ShadowPlan *pt_shadow_plan_by_name(const char *realName) { for (reg::ShadowPlan &plan : pt_shadow_plans()) { - if (strcmp(plan.name, realName) == 0) { - return &plan; - } + if (strcmp(plan.name, realName) == 0) return &plan; } return NULL; } @@ -89,16 +85,12 @@ static bool pt_shadow_materialize(reg::ShadowPlan &plan, HashTable *twinFiles, z /* declares one plan; a parent that is itself a plan is declared first */ static bool pt_shadow_materialize(reg::ShadowPlan &plan, HashTable *twinFiles, zend_string *prefix) { - if (plan.ce != NULL) { - return true; - } + if (plan.ce != NULL) return true; std::string parentDeclared; if (plan.parentName != NULL) { reg::ShadowPlan *parentPlan = pt_shadow_plan_by_name(plan.parentName); - if (parentPlan != NULL && !pt_shadow_materialize(*parentPlan, twinFiles, prefix)) { - return false; - } + if (parentPlan != NULL && !pt_shadow_materialize(*parentPlan, twinFiles, prefix)) return false; parentDeclared = parentPlan != NULL ? pt_shadow_declared_name(plan.parentName, prefix) : std::string(plan.parentName); } @@ -193,9 +185,7 @@ bool pt_shadow_activate(HashTable *twinFiles, zend_string *prefix) return false; } for (reg::ShadowPlan &plan : pt_shadow_plans()) { - if (!pt_shadow_materialize(plan, twinFiles, prefix)) { - return false; - } + if (!pt_shadow_materialize(plan, twinFiles, prefix)) return false; } pt_shadow_active = true; return true; diff --git a/turbo-ext/src/SymbolFinderInFiles.cpp b/turbo-ext/src/SymbolFinderInFiles.cpp index 39f6ec8d837..77edb11a493 100644 --- a/turbo-ext/src/SymbolFinderInFiles.cpp +++ b/turbo-ext/src/SymbolFinderInFiles.cpp @@ -67,18 +67,14 @@ bool SymbolFinderInFiles::readFile(const char *path, size_t pathLen) { source.clear(); - if (pathLen == 0 || memchr(path, '\0', pathLen) != NULL) { - return false; - } + if (pathLen == 0 || memchr(path, '\0', pathLen) != NULL) return false; #ifdef PHP_WIN32 int fd = _open(path, _O_RDONLY | _O_BINARY); #else int fd = open(path, O_RDONLY); #endif - if (fd < 0) { - return false; - } + if (fd < 0) return false; char chunk[65536]; for (;;) { @@ -96,9 +92,7 @@ bool SymbolFinderInFiles::readFile(const char *path, size_t pathLen) source.clear(); return false; } - if (got == 0) { - break; - } + if (got == 0) break; source.append(chunk, (size_t) got); } @@ -115,21 +109,15 @@ void SymbolFinderInFiles::scan(bool supportsEnums) { symbols.clear(); - if (source.empty()) { - return; - } + if (source.empty()) return; CommentStripper stripper(source.data(), source.size(), shortOpenTagEnabled()); stripper.strip(stripped); - if (stripped.empty()) { - return; - } + if (stripped.empty()) return; size_t matches = prefilterCount(stripped.data(), stripped.size(), supportsEnums); - if (matches == 0) { - return; - } + if (matches == 0) return; PhpFileCleaner cleaner(stripped.data(), stripped.size()); cleaner.clean((zend_long) matches, cleaned); @@ -163,9 +151,7 @@ zv::Val SymbolFinderInFiles::findSymbols(HashTable *files, bool supportsEnums) for (zv::ArrayEntry file : zv::TableRef(files)) { zv::Ref value = file.value().deref(); - if (!value.isString()) { - continue; - } + if (!value.isString()) continue; zend_string *path = value.asString(); if (readFile(ZSTR_VAL(path), ZSTR_LEN(path))) { diff --git a/turbo-ext/src/SymbolScan.h b/turbo-ext/src/SymbolScan.h index 15eff332ec2..bf592577cc9 100644 --- a/turbo-ext/src/SymbolScan.h +++ b/turbo-ext/src/SymbolScan.h @@ -81,9 +81,7 @@ inline bool equalsIgnoreCase(const char *a, const char *lowercaseB, size_t n) if (c >= 'A' && c <= 'Z') { c = (char) (c - 'A' + 'a'); } - if (c != lowercaseB[i]) { - return false; - } + if (c != lowercaseB[i]) return false; } return true; } @@ -134,9 +132,7 @@ class PhpFileCleaner * keyword starts with one) and must not be $, : or >. */ bool prevByteOpensKeyword(size_t at) const { - if (at == 0 || at > len) { - return false; - } + if (at == 0 || at > len) return false; unsigned char prev = (unsigned char) contents[at - 1]; return !isWordByte(prev) && prev != '$' && prev != ':' && prev != '>'; } @@ -151,9 +147,7 @@ class PhpFileCleaner while (p < len && isSpaceByte((unsigned char) contents[p])) { p++; } - if (p == from || p >= len || !isNameStart((unsigned char) contents[p])) { - return false; - } + if (p == from || p >= len || !isNameStart((unsigned char) contents[p])) return false; p++; while (p < len && isNameByte((unsigned char) contents[p])) { p++; @@ -213,9 +207,7 @@ inline void PhpFileCleaner::skipString(char delimiter) while (index < len && contents[index] != '\\' && contents[index] != delimiter) { index++; } - if (index >= len) { - break; - } + if (index >= len) break; if (contents[index] == '\\' && (peek('\\') || peek(delimiter))) { index += 2; continue; @@ -256,9 +248,7 @@ inline void PhpFileCleaner::skipToNewline() inline bool PhpFileCleaner::matchHeredocStart(size_t *labelStart, size_t *labelLen, size_t *end) const { size_t p = index; - if (p + 3 > len || contents[p] != '<' || contents[p + 1] != '<' || contents[p + 2] != '<') { - return false; - } + if (p + 3 > len || contents[p] != '<' || contents[p + 1] != '<' || contents[p + 2] != '<') return false; p += 3; while (p < len && (contents[p] == ' ' || contents[p] == '\t')) { p++; @@ -268,9 +258,7 @@ inline bool PhpFileCleaner::matchHeredocStart(size_t *labelStart, size_t *labelL quote = contents[p]; p++; } - if (p >= len || !isLabelStart((unsigned char) contents[p])) { - return false; - } + if (p >= len || !isLabelStart((unsigned char) contents[p])) return false; size_t start = p; p++; while (p < len && isLabelByte((unsigned char) contents[p])) { @@ -279,9 +267,7 @@ inline bool PhpFileCleaner::matchHeredocStart(size_t *labelStart, size_t *labelL *labelStart = start; *labelLen = p - start; if (quote != '\0') { - if (p >= len || contents[p] != quote) { - return false; - } + if (p >= len || contents[p] != quote) return false; p++; } if (p < len && contents[p] == '\r') { @@ -453,9 +439,7 @@ inline void PhpFileCleaner::clean(zend_long maxMatches, std::string &out) } for (const TypeConfig &type : types) { - if (type.firstByte != c) { - continue; - } + if (type.firstByte != c) continue; if (index + type.length <= len && memcmp(contents + index, type.name, type.length) == 0) { if (maxMatches == 1 && prevByteOpensKeyword(index)) { @@ -532,17 +516,13 @@ class CommentStripper /* length of the open tag at `at`, or 0 if there is none */ size_t openTagLength(size_t at) const { - if (at + 1 >= len || contents[at] != '<' || contents[at + 1] != '?') { - return 0; - } + if (at + 1 >= len || contents[at] != '<' || contents[at + 1] != '?') return 0; if (at + 4 < len && equalsIgnoreCase(contents + at + 2, "php", 3) && (at + 5 >= len || isSpaceByte((unsigned char) contents[at + 5])) ) { return 5; } - if (at + 2 < len && contents[at + 2] == '=') { - return 3; - } + if (at + 2 < len && contents[at + 2] == '=') return 3; return shortOpenTag ? 2 : 0; } @@ -553,12 +533,8 @@ class CommentStripper { while (index < len) { char c = contents[index]; - if (c == '\n' || c == '\r') { - return; - } - if (c == '?' && index + 1 < len && contents[index + 1] == '>') { - return; - } + if (c == '\n' || c == '\r') return; + if (c == '?' && index + 1 < len && contents[index + 1] == '>') return; index++; } } @@ -589,9 +565,7 @@ class CommentStripper continue; } index++; - if (c == delimiter) { - break; - } + if (c == delimiter) break; } out.append(contents + start, index - start); } @@ -621,9 +595,7 @@ inline void CommentStripper::copyHeredoc(std::string &out) } size_t labelLen = p - labelStart; if (quote != '\0') { - if (p >= len || contents[p] != quote) { - return; - } + if (p >= len || contents[p] != quote) return; p++; } if (p < len && contents[p] == '\r') { @@ -674,15 +646,11 @@ inline void CommentStripper::strip(std::string &out) size_t tagLength = 0; while (index < len) { tagLength = openTagLength(index); - if (tagLength != 0) { - break; - } + if (tagLength != 0) break; index++; } out.append(contents + htmlStart, index - htmlStart); - if (index >= len) { - return; - } + if (index >= len) return; out.append(contents + index, tagLength); index += tagLength; @@ -723,9 +691,7 @@ inline void CommentStripper::strip(std::string &out) if (c == '<' && index + 2 < len && contents[index + 1] == '<' && contents[index + 2] == '<') { size_t before = index; copyHeredoc(out); - if (index != before) { - continue; - } + if (index != before) continue; } size_t start = index; @@ -787,9 +753,7 @@ inline size_t prefilterCount(const char *contents, size_t len, bool supportsEnum break; } } - if (matched) { - continue; - } + if (matched) continue; } /* the define branch carries no \b — `mydefine(` counts too */ @@ -850,9 +814,7 @@ class SymbolMatcher bool guard(size_t at) const { - if (at == 0) { - return true; - } + if (at == 0) return true; unsigned char prev = (unsigned char) contents[at - 1]; return !isWordByte(prev) && prev != '$' && prev != ':' && prev != '>'; } @@ -874,9 +836,7 @@ class SymbolMatcher /* [a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff\-]*+ */ size_t readName(size_t at) const { - if (at >= len || !isNameStart((unsigned char) contents[at])) { - return 0; - } + if (at >= len || !isNameStart((unsigned char) contents[at])) return 0; size_t end = at + 1; while (end < len && isNameByte((unsigned char) contents[end])) { end++; @@ -887,9 +847,7 @@ class SymbolMatcher /* the define() name: identifiers joined by one or two backslashes */ size_t readDefineName(size_t at) const { - if (at >= len || !isNameStart((unsigned char) contents[at])) { - return 0; - } + if (at >= len || !isNameStart((unsigned char) contents[at])) return 0; size_t end = at + 1; while (end < len && isDefineNameByte((unsigned char) contents[end])) { end++; @@ -901,9 +859,7 @@ class SymbolMatcher p++; slashes++; } - if (slashes == 0 || p >= len || !isNameStart((unsigned char) contents[p])) { - break; - } + if (slashes == 0 || p >= len || !isNameStart((unsigned char) contents[p])) break; p++; while (p < len && isDefineNameByte((unsigned char) contents[p])) { p++; @@ -940,9 +896,7 @@ class SymbolMatcher * constant's own name keeps its case */ static std::string normalizeConstantName(const std::string &name) { - if (name.find('\\') == std::string::npos) { - return name; - } + if (name.find('\\') == std::string::npos) return name; std::vector parts; size_t start = 0; @@ -954,9 +908,7 @@ class SymbolMatcher start = i + 1; } } - if (parts.empty()) { - return std::string("\\"); - } + if (parts.empty()) return std::string("\\"); std::string result; for (size_t i = 0; i + 1 < parts.size(); i++) { @@ -1001,21 +953,13 @@ inline void SymbolMatcher::match(Symbols &out) static const size_t typeLengths[] = { 5, 9, 5, 4 }; bool matched = false; for (size_t t = 0; t < 4; t++) { - if (t == 3 && !supportsEnums) { - break; - } - if (!keyword(i, typeNames[t], typeLengths[t])) { - continue; - } + if (t == 3 && !supportsEnums) break; + if (!keyword(i, typeNames[t], typeLengths[t])) continue; size_t after = i + typeLengths[t]; size_t nameStart = skipSpaces(after); - if (nameStart == after) { - break; - } + if (nameStart == after) break; size_t nameEnd = readName(nameStart); - if (nameEnd == 0) { - break; - } + if (nameEnd == 0) break; size_t nameLen = nameEnd - nameStart; /* skip anonymous classes: `new class extends X` captures the * keyword that follows as if it were the name */ @@ -1028,9 +972,7 @@ inline void SymbolMatcher::match(Symbols &out) matched = true; break; } - if (matched) { - continue; - } + if (matched) continue; /* function \s++ (&\s*)? NAME \s*+ [&(] */ if (keyword(i, "function", 8)) { @@ -1098,13 +1040,9 @@ inline void SymbolMatcher::match(Symbols &out) } for (;;) { size_t p = skipSpaces(nameEnd); - if (p >= len || contents[p] != '\\') { - break; - } + if (p >= len || contents[p] != '\\') break; p = skipSpaces(p + 1); - if (p >= len || !isNameStart((unsigned char) contents[p])) { - break; - } + if (p >= len || !isNameStart((unsigned char) contents[p])) break; p++; while (p < len && isDefineNameByte((unsigned char) contents[p])) { p++; @@ -1121,9 +1059,7 @@ inline void SymbolMatcher::match(Symbols &out) currentNamespace.clear(); for (size_t p = nameStart; p < nameEnd; p++) { char ch = contents[p]; - if (isSpaceByte((unsigned char) ch)) { - continue; - } + if (isSpaceByte((unsigned char) ch)) continue; currentNamespace.push_back(ch >= 'A' && ch <= 'Z' ? (char) (ch - 'A' + 'a') : ch); } currentNamespace.push_back('\\'); diff --git a/turbo-ext/src/TrinaryLogic.cpp b/turbo-ext/src/TrinaryLogic.cpp index f358b897e74..caf35d363b5 100644 --- a/turbo-ext/src/TrinaryLogic.cpp +++ b/turbo-ext/src/TrinaryLogic.cpp @@ -95,23 +95,15 @@ class TrinaryLogic zv::Val compareTo(zval *thisZv, zval *otherZv) const { TrinaryLogic other(Z_OBJ_P(otherZv)); - if (value() > other.value()) { - return zv::Val::copyOf(zv::Ref(thisZv)); - } - if (other.value() > value()) { - return zv::Val::copyOf(zv::Ref(otherZv)); - } + if (value() > other.value()) return zv::Val::copyOf(zv::Ref(thisZv)); + if (other.value() > value()) return zv::Val::copyOf(zv::Ref(otherZv)); return zv::Val::null(); } const char *describe() const { - if (value() == YES) { - return "Yes"; - } - if (value() == MAYBE) { - return "Maybe"; - } + if (value() == YES) return "Yes"; + if (value() == MAYBE) return "Maybe"; return "No"; } @@ -119,9 +111,7 @@ class TrinaryLogic * UNDEF result means a pending exception */ zv::Val toBooleanType() const { - if (maybe()) { - return constructConfigured(PT_CLASS_BOOLEAN_TYPE, NULL, 0); - } + if (maybe()) return constructConfigured(PT_CLASS_BOOLEAN_TYPE, NULL, 0); zval arg; ZVAL_BOOL(&arg, yes()); return constructConfigured(PT_CLASS_CONSTANT_BOOLEAN_TYPE, &arg, 1); @@ -133,13 +123,9 @@ class TrinaryLogic static zv::Val constructConfigured(int classIdx, zval *args, uint32_t argc) { zend_class_entry *ce = pt_class(classIdx); - if (UNEXPECTED(ce == NULL)) { - return zv::Val(); - } + if (UNEXPECTED(ce == NULL)) return zv::Val(); zval obj; - if (UNEXPECTED(object_init_ex(&obj, ce) != SUCCESS)) { - return zv::Val(); - } + if (UNEXPECTED(object_init_ex(&obj, ce) != SUCCESS)) return zv::Val(); if (ce->constructor != NULL) { zend_call_known_instance_method(ce->constructor, Z_OBJ(obj), NULL, argc, args); if (UNEXPECTED(EG(exception))) { @@ -177,9 +163,7 @@ class LazyEvaluation fci.params = ¶m; fci.named_params = NULL; - if (UNEXPECTED(zend_call_function(&fci, &fcc) != SUCCESS || EG(exception))) { - return zv::Val(); - } + if (UNEXPECTED(zend_call_function(&fci, &fcc) != SUCCESS || EG(exception))) return zv::Val(); if (UNEXPECTED(Z_TYPE(retval) != IS_OBJECT || !instanceof_function(Z_OBJCE(retval), pt_ce_trinary))) { zval_ptr_dtor(&retval); zend_type_error("Return value of the callback must be of type %s", ZSTR_VAL(pt_ce_trinary->name)); @@ -194,29 +178,19 @@ class LazyEvaluation zend_long thisValue = 0; if (mode != MAX_MIN) { thisValue = TrinaryLogic(Z_OBJ_P(thisZv)).value(); - if (mode == AND && thisValue == TrinaryLogic::NO) { - return zv::Val::copyOf(zv::Ref(thisZv)); - } - if (mode == OR && thisValue == TrinaryLogic::YES) { - return zv::Val::copyOf(zv::Ref(thisZv)); - } + if (mode == AND && thisValue == TrinaryLogic::NO) return zv::Val::copyOf(zv::Ref(thisZv)); + if (mode == OR && thisValue == TrinaryLogic::YES) return zv::Val::copyOf(zv::Ref(thisZv)); } zend_long acc = mode == OR ? TrinaryLogic::NO : TrinaryLogic::YES; for (auto entry : objects) { zv::Val result = callbackResult(entry.value()); - if (result.isUndef()) { - return zv::Val(); - } + if (result.isUndef()) return zv::Val(); zend_long resultValue = TrinaryLogic(zv::Ref(result.raw()).asObject()).value(); - if (mode == AND && resultValue == TrinaryLogic::NO) { - return result; - } - if ((mode == OR || mode == MAX_MIN) && resultValue == TrinaryLogic::YES) { - return result; - } + if (mode == AND && resultValue == TrinaryLogic::NO) return result; + if ((mode == OR || mode == MAX_MIN) && resultValue == TrinaryLogic::YES) return result; if (mode == OR) { acc |= resultValue; @@ -240,16 +214,12 @@ class LazyEvaluation zv::Val last; for (auto entry : objects) { zv::Val result = callbackResult(entry.value()); - if (result.isUndef()) { - return zv::Val(); - } + if (result.isUndef()) return zv::Val(); if (last.isUndef()) { last = std::move(result); continue; } - if (zv::Ref(result.raw()).asObject() != zv::Ref(last.raw()).asObject()) { - return TrinaryLogic::create(TrinaryLogic::MAYBE); - } + if (zv::Ref(result.raw()).asObject() != zv::Ref(last.raw()).asObject()) return TrinaryLogic::create(TrinaryLogic::MAYBE); } return last; } @@ -299,9 +269,7 @@ static void pt_trinary_lazy(INTERNAL_FUNCTION_PARAMETERS, LazyEvaluation::Mode m zval objectsZv; ZVAL_ARR(&objectsZv, objects); zv::Val result = LazyEvaluation(fci, fcc).run(mode, ZEND_THIS, zv::ArrRef(&objectsZv)); - if (UNEXPECTED(result.isUndef())) { - RETURN_THROWS(); - } + if (UNEXPECTED(result.isUndef())) RETURN_THROWS(); result.intoReturnValue(return_value); } @@ -318,9 +286,7 @@ static void pt_trinary_variadic_op(INTERNAL_FUNCTION_PARAMETERS, bool extremeIde pt_throw_should_not_happen(); RETURN_THROWS(); } - if (UNEXPECTED(pt_verify_trinary_variadic(operands, count, 1) != SUCCESS)) { - RETURN_THROWS(); - } + if (UNEXPECTED(pt_verify_trinary_variadic(operands, count, 1) != SUCCESS)) RETURN_THROWS(); (extremeIdentity ? TrinaryLogic::extremeIdentity(operands, count) : TrinaryLogic::maxMin(operands, count)).intoReturnValue(return_value); } @@ -337,9 +303,7 @@ static void pt_trinary_and_or(INTERNAL_FUNCTION_PARAMETERS, bool isAnd) Z_PARAM_VARIADIC('+', rest, restCount) ZEND_PARSE_PARAMETERS_END(); - if (UNEXPECTED(pt_verify_trinary_variadic(rest, restCount, 2) != SUCCESS)) { - RETURN_THROWS(); - } + if (UNEXPECTED(pt_verify_trinary_variadic(rest, restCount, 2) != SUCCESS)) RETURN_THROWS(); TrinaryLogic self(Z_OBJ_P(ZEND_THIS)); TrinaryLogic operandHandle(operand != NULL ? Z_OBJ_P(operand) : NULL); @@ -403,9 +367,7 @@ void pt_register_trinary_logic() cls.method("toBooleanType", reg::Public, 0, {}, [](INTERNAL_FUNCTION_PARAMETERS) { ZEND_PARSE_PARAMETERS_NONE(); zv::Val result = TrinaryLogic(Z_OBJ_P(ZEND_THIS)).toBooleanType(); - if (UNEXPECTED(result.isUndef())) { - RETURN_THROWS(); - } + if (UNEXPECTED(result.isUndef())) RETURN_THROWS(); result.intoReturnValue(return_value); }); @@ -447,9 +409,7 @@ void pt_register_trinary_logic() zval objectsZv; ZVAL_ARR(&objectsZv, objects); zv::Val result = LazyEvaluation(fci, fcc).runExtremeIdentity(zv::ArrRef(&objectsZv)); - if (UNEXPECTED(result.isUndef())) { - RETURN_THROWS(); - } + if (UNEXPECTED(result.isUndef())) RETURN_THROWS(); result.intoReturnValue(return_value); }); diff --git a/turbo-ext/src/TrustedTypes.cpp b/turbo-ext/src/TrustedTypes.cpp index cd467063f8d..0657bdc911f 100644 --- a/turbo-ext/src/TrustedTypes.cpp +++ b/turbo-ext/src/TrustedTypes.cpp @@ -89,9 +89,7 @@ static bool pt_tt_signature_coerces(const zend_op_array *op_array) { uint32_t count = op_array->num_args + ((op_array->fn_flags & ZEND_ACC_VARIADIC) ? 1 : 0); for (uint32_t i = 0; i < count; i++) { - if (pt_tt_type_coerces(&op_array->arg_info[i].type)) { - return true; - } + if (pt_tt_type_coerces(&op_array->arg_info[i].type)) return true; } if ((op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) && pt_tt_type_coerces(&op_array->arg_info[-1].type)) { @@ -107,12 +105,8 @@ static void pt_tt_strip(zend_op_array *op_array) pt_tt_strip(op_array->dynamic_func_defs[i]); } /* top-level code has no signature */ - if (op_array->function_name == NULL) { - return; - } - if (pt_tt_signature_coerces(op_array)) { - return; - } + if (op_array->function_name == NULL) return; + if (pt_tt_signature_coerces(op_array)) return; op_array->fn_flags &= ~ZEND_ACC_HAS_TYPE_HINTS; @@ -153,9 +147,7 @@ static void pt_tt_strip_class(zend_class_entry *ce) #if PHP_VERSION_ID >= 80400 ZEND_HASH_FOREACH_VAL(&ce->properties_info, zv) { zend_property_info *prop = (zend_property_info *) Z_PTR_P(zv); - if (prop->ce != ce || prop->hooks == NULL) { - continue; - } + if (prop->ce != ce || prop->hooks == NULL) continue; for (int i = 0; i < ZEND_PROPERTY_HOOK_COUNT; i++) { if (prop->hooks[i] != NULL && prop->hooks[i]->type == ZEND_USER_FUNCTION) { pt_tt_strip(&prop->hooks[i]->op_array); @@ -168,9 +160,7 @@ static void pt_tt_strip_class(zend_class_entry *ce) static void pt_tt_pass(zend_script *script, void *ctx) { (void) ctx; - if (!pt_tt_matches(script->filename)) { - return; - } + if (!pt_tt_matches(script->filename)) return; pt_tt_strip(&script->main_op_array); @@ -194,9 +184,7 @@ static void pt_tt_pass(zend_script *script, void *ctx) * is absent on hosts without OPcache, so it is resolved by name, not linked. */ static bool pt_tt_register_pass() { - if (pt_tt_pass_registered) { - return true; - } + if (pt_tt_pass_registered) return true; pt_tt_register_pass_t register_pass = NULL; #ifdef PHP_WIN32 @@ -215,13 +203,9 @@ static bool pt_tt_register_pass() #else register_pass = reinterpret_cast(dlsym(RTLD_DEFAULT, "zend_optimizer_register_pass")); #endif - if (register_pass == NULL) { - return false; - } + if (register_pass == NULL) return false; /* -1 when the (32-slot) table is full */ - if (register_pass(pt_tt_pass) < 0) { - return false; - } + if (register_pass(pt_tt_pass) < 0) return false; pt_tt_pass_registered = true; return true; } @@ -229,12 +213,8 @@ static bool pt_tt_register_pass() bool pt_trusted_types_set_prefix(zend_string *prefix) { PT_G(trusted_types_prefix_len) = 0; - if (ZSTR_LEN(prefix) == 0 || ZSTR_LEN(prefix) >= sizeof(PT_G(trusted_types_prefix))) { - return false; - } - if (!pt_tt_register_pass()) { - return false; - } + if (ZSTR_LEN(prefix) == 0 || ZSTR_LEN(prefix) >= sizeof(PT_G(trusted_types_prefix))) return false; + if (!pt_tt_register_pass()) return false; memcpy(PT_G(trusted_types_prefix), ZSTR_VAL(prefix), ZSTR_LEN(prefix)); PT_G(trusted_types_prefix_len) = ZSTR_LEN(prefix); return true; diff --git a/turbo-ext/src/TypeCombinatorCache.cpp b/turbo-ext/src/TypeCombinatorCache.cpp index bcc2bab553d..0a5f93b8ff0 100644 --- a/turbo-ext/src/TypeCombinatorCache.cpp +++ b/turbo-ext/src/TypeCombinatorCache.cpp @@ -247,9 +247,7 @@ static uint64_t objSerial(zend_object *obj) * results non-deterministic. Each such object instead gets a serial that is * never reused, held as a plain IS_LONG in the weak map. */ zval *known = zend_hash_index_find(&pt_obj_serials, zend_object_to_weakref_key(obj)); - if (known != NULL) { - return (uint64_t) Z_LVAL_P(known); - } + if (known != NULL) return (uint64_t) Z_LVAL_P(known); uint64_t serial = pt_next_serial; zval value; @@ -316,9 +314,7 @@ static bool hashZval(zval *value, Hash128 &h, uint32_t depth) mixU64(h, (uint64_t) entry.indexKey()); } zval *slot = entry.value().raw(); - if (!hashZval(slot, h, depth + 1)) { - return false; - } + if (!hashZval(slot, h, depth + 1)) return false; } return true; } @@ -326,9 +322,7 @@ static bool hashZval(zval *value, Hash128 &h, uint32_t depth) zend_object *obj = Z_OBJ_P(value); if (cePlan(obj->ce).kind == CE_STRUCTURAL) { Hash128 inner; - if (!hashObject(obj, inner, depth + 1)) { - return false; - } + if (!hashObject(obj, inner, depth + 1)) return false; mixByte(h, 11); mixU64(h, inner.a); mixU64(h, inner.b); @@ -348,9 +342,7 @@ static bool hashZval(zval *value, Hash128 &h, uint32_t depth) * guarantee the object is CE_STRUCTURAL, so the plan does not need consulting. */ static zend_always_inline bool hashZeroSlotObject(zend_object *obj, Hash128 &out) { - if (obj->ce->default_properties_count != 0) { - return false; - } + if (obj->ce->default_properties_count != 0) return false; Hash128 h = { FNV_OFFSET_A, FNV_OFFSET_B }; mixU64(h, (uint64_t) (uintptr_t) obj->ce); out = h; @@ -360,17 +352,13 @@ static zend_always_inline bool hashZeroSlotObject(zend_object *obj, Hash128 &out static bool hashObject(zend_object *obj, Hash128 &out, uint32_t depth) { - if (UNEXPECTED(depth > HASH_DEPTH_LIMIT)) { - return false; - } + if (UNEXPECTED(depth > HASH_DEPTH_LIMIT)) return false; /* Argless leaf types (MixedType, NullType, …) are ~30% of hashed objects; * recomputing their class-only hash is cheaper than a table lookup, and * caching it would spend a map entry plus an EG(weakrefs) registration per * instance to save nothing. */ - if (hashZeroSlotObject(obj, out)) { - return true; - } + if (hashZeroSlotObject(obj, out)) return true; Hash128 *cached = (Hash128 *) zend_hash_index_find_ptr(&pt_type_hashes, zend_object_to_weakref_key(obj)); if (cached != NULL) { @@ -384,9 +372,7 @@ static bool hashObject(zend_object *obj, Hash128 &out, uint32_t depth) mixU64(h, (uint64_t) (uintptr_t) obj->ce); for (uint32_t i = 0; i < plan.slots; i++) { - if (!hashZval(OBJ_PROP_NUM(obj, i), h, depth + 1)) { - return false; - } + if (!hashZval(OBJ_PROP_NUM(obj, i), h, depth + 1)) return false; } /* The 16 hash bytes live behind a real IS_PTR value; they cannot go into the @@ -425,22 +411,16 @@ static bool guardActive() pt_guard_unavailable = true; zend_class_entry *ce = pt_class(PT_CLASS_RECURSION_GUARD); - if (ce == NULL) { - return true; - } + if (ce == NULL) return true; zend_property_info *info = (zend_property_info *) zend_hash_str_find_ptr(&ce->properties_info, "context", sizeof("context") - 1); - if (info == NULL || (info->flags & ZEND_ACC_STATIC) == 0) { - return true; - } + if (info == NULL || (info->flags & ZEND_ACC_STATIC) == 0) return true; pt_guard_ce = ce; pt_guard_offset = info->offset; pt_guard_unavailable = false; } - if (UNEXPECTED(pt_guard_unavailable)) { - return true; - } + if (UNEXPECTED(pt_guard_unavailable)) return true; if (UNEXPECTED(CE_STATIC_MEMBERS(pt_guard_ce) == NULL)) { zend_class_init_statics(pt_guard_ce); @@ -472,12 +452,8 @@ static zend_always_inline MemoSlot *memoLookup(Hash128 key) uint32_t idx = (uint32_t) (key.a ^ (key.a >> 32)) & pt_memo_mask; for (;;) { MemoSlot *slot = &pt_memo_slots[idx]; - if (slot->result == NULL) { - return NULL; - } - if (slot->result != MEMO_TOMBSTONE && slot->key.a == key.a && slot->key.b == key.b) { - return slot; - } + if (slot->result == NULL) return NULL; + if (slot->result != MEMO_TOMBSTONE && slot->key.a == key.a && slot->key.b == key.b) return slot; idx = (idx + 1) & pt_memo_mask; } } @@ -490,9 +466,7 @@ static zend_always_inline MemoSlot *memoInsertPos(Hash128 key) MemoSlot *tombstone = NULL; for (;;) { MemoSlot *slot = &pt_memo_slots[idx]; - if (slot->result == NULL) { - return tombstone != NULL ? tombstone : slot; - } + if (slot->result == NULL) return tombstone != NULL ? tombstone : slot; if (slot->result == MEMO_TOMBSTONE) { if (tombstone == NULL) { tombstone = slot; @@ -532,9 +506,7 @@ static void memoInvalidate(const KeyList *list) uint32_t idx = (uint32_t) (key.a ^ (key.a >> 32)) & pt_memo_mask; for (;;) { MemoSlot *slot = &pt_memo_slots[idx]; - if (slot->result == NULL) { - break; - } + if (slot->result == NULL) break; if (slot->result != MEMO_TOMBSTONE && memoSlotObject(slot->result) == list->obj && slot->key.a == key.a && slot->key.b == key.b) { slot->result = MEMO_TOMBSTONE; pt_memo_count--; @@ -652,13 +624,9 @@ class TypeCombinatorCache } zend_class_entry *ce = pt_class(PT_CLASS_TYPE_COMBINATOR); - if (UNEXPECTED(ce == NULL || fn == NULL)) { - return; - } + if (UNEXPECTED(ce == NULL || fn == NULL)) return; zend_call_known_function(fn, NULL, ce, return_value, argc, args, NULL); - if (UNEXPECTED(EG(exception)) || Z_TYPE_P(return_value) != IS_OBJECT) { - return; - } + if (UNEXPECTED(EG(exception)) || Z_TYPE_P(return_value) != IS_OBJECT) return; uintptr_t operandTag = 0; if (memoizable) { @@ -668,9 +636,7 @@ class TypeCombinatorCache for (uint32_t i = 0; i < argc; i++) { zval *arg = &args[i]; ZVAL_DEREF(arg); - if (Z_OBJ_P(arg) != Z_OBJ_P(return_value)) { - continue; - } + if (Z_OBJ_P(arg) != Z_OBJ_P(return_value)) continue; if (operandTag != 0 || i >= MEMO_OPERAND_POSITIONS_LIMIT) { memoizable = false; break; @@ -701,9 +667,7 @@ class TypeCombinatorCache static void clear() { - if (!pt_cache_inited) { - return; - } + if (!pt_cache_inited) return; memoResultsClean(); if (pt_memo_mask + 1 > MEMO_INITIAL_CAPACITY_LIMIT) { efree(pt_memo_slots); @@ -746,9 +710,7 @@ using phpstanturbo::MEMO_INITIAL_CAPACITY_LIMIT; void pt_type_combinator_cache_rinit() { - if (pt_cache_inited) { - return; - } + if (pt_cache_inited) return; zend_hash_init(&pt_type_hashes, 4096, NULL, typeHashDtor, 0); zend_hash_init(&pt_ce_kinds, 128, NULL, NULL, 0); zend_hash_init(&pt_obj_serials, 1024, NULL, NULL, 0); @@ -767,9 +729,7 @@ void pt_type_combinator_cache_rinit() void pt_type_combinator_cache_rshutdown() { - if (!pt_cache_inited) { - return; - } + if (!pt_cache_inited) return; TypeCombinatorCache::clear(); phpstanturbo::pt_invalidate_active = false; pt_weakrefs_hash_destroy(&pt_memo_results); @@ -799,9 +759,7 @@ static zend_function *resolveOp(zend_function **slot, const char *lcname, size_t { if (*slot == NULL) { zend_class_entry *ce = pt_class(PT_CLASS_TYPE_COMBINATOR); - if (ce == NULL) { - return NULL; - } + if (ce == NULL) return NULL; *slot = pt_find_method(ce, lcname, len); } return *slot; diff --git a/turbo-ext/src/main.cpp b/turbo-ext/src/main.cpp index e385010eb33..5efd3f1c988 100644 --- a/turbo-ext/src/main.cpp +++ b/turbo-ext/src/main.cpp @@ -49,9 +49,7 @@ static void ZEND_FASTCALL runtimeConfigure(INTERNAL_FUNCTION_PARAMETERS) zval *value; ZEND_HASH_FOREACH_STR_KEY_VAL(map, key, value) { ZVAL_DEREF(value); - if (key == NULL || Z_TYPE_P(value) != IS_STRING) { - continue; - } + if (key == NULL || Z_TYPE_P(value) != IS_STRING) continue; pt_class_map_configure(key, Z_STR_P(value)); } ZEND_HASH_FOREACH_END(); } @@ -72,9 +70,7 @@ static void ZEND_FASTCALL runtimeActivateShadowing(INTERNAL_FUNCTION_PARAMETERS) Z_PARAM_STR_OR_NULL(prefix) ZEND_PARSE_PARAMETERS_END(); - if (!pt_shadow_activate(twinFiles, prefix)) { - RETURN_THROWS(); - } + if (!pt_shadow_activate(twinFiles, prefix)) RETURN_THROWS(); } /* PHPStanTurbo\Runtime::isShadowing() — whether activateShadowing() ran */ diff --git a/turbo-ext/src/parser/ParserRunner.cpp b/turbo-ext/src/parser/ParserRunner.cpp index 37cb8ac57c4..13c47cdfe17 100644 --- a/turbo-ext/src/parser/ParserRunner.cpp +++ b/turbo-ext/src/parser/ParserRunner.cpp @@ -46,9 +46,7 @@ static bool g_keysReady = false; static void initAttributeKeys(void) { - if (g_keysReady) { - return; - } + if (g_keysReady) return; g_key_startLine = zend_string_init("startLine", sizeof("startLine") - 1, 1); g_key_startTokenPos = zend_string_init("startTokenPos", sizeof("startTokenPos") - 1, 1); g_key_startFilePos = zend_string_init("startFilePos", sizeof("startFilePos") - 1, 1); @@ -98,12 +96,8 @@ ParserEngine::~ParserEngine() bool ParserEngine::reduce(int rule, int stackPos) { - if (rule < PN_REDUCE_SPLIT_1) { - return reduceRange1(rule, stackPos); - } - if (rule < PN_REDUCE_SPLIT_2) { - return reduceRange2(rule, stackPos); - } + if (rule < PN_REDUCE_SPLIT_1) return reduceRange1(rule, stackPos); + if (rule < PN_REDUCE_SPLIT_2) return reduceRange2(rule, stackPos); return reduceRange3(rule, stackPos); } @@ -163,9 +157,7 @@ zv::Arr ParserEngine::getAttributesAt(int stackPos) zv::Arr ParserEngine::getAttributesForToken(int tokenPos) { - if (tokenPos < numTokens - 1) { - return getAttributes(tokenPos, tokenPos); - } + if (tokenPos < numTokens - 1) return getAttributes(tokenPos, tokenPos); initAttributeKeys(); const Token *token = &tokens[tokenPos]; zv::Arr attrs = zv::Arr::create(6); @@ -198,9 +190,7 @@ zv::Ref ParserEngine::prop(zv::Ref node, const char *name) void ParserEngine::propWrite(zv::Ref node, const char *name, zv::Val value) { zv::Ref slot = prop(node, name); - if (slot.raw() == NULL) { - return; /* the Val releases the value */ - } + if (slot.raw() == NULL) return; /* the Val releases the value */ zval_ptr_dtor(slot.raw()); zval v = value.take(); ZVAL_COPY_VALUE(slot.raw(), &v); @@ -209,9 +199,7 @@ void ParserEngine::propWrite(zv::Ref node, const char *name, zv::Val value) zv::Arr ParserEngine::getNodeAttributes(zv::Ref node) { zv::Ref attrs = prop(node, "attributes"); - if (attrs.raw() == NULL || !attrs.isArray()) { - return zv::Arr::create(0); - } + if (attrs.raw() == NULL || !attrs.isArray()) return zv::Arr::create(0); zv::Arr copy; ZVAL_COPY(copy.raw(), attrs.raw()); return copy; @@ -220,9 +208,7 @@ zv::Arr ParserEngine::getNodeAttributes(zv::Ref node) void ParserEngine::setNodeAttribute(zv::Ref node, const char *key, zv::Val value) { zv::Ref attrs = prop(node, "attributes"); - if (attrs.raw() == NULL || !attrs.isArray()) { - return; /* the Val releases the value */ - } + if (attrs.raw() == NULL || !attrs.isArray()) return; /* the Val releases the value */ SEPARATE_ARRAY(attrs.raw()); zval v = value.take(); zend_hash_str_update(Z_ARRVAL_P(attrs.raw()), key, strlen(key), &v); @@ -283,9 +269,7 @@ NodeClassInfo *ParserEngine::resolveNodeClass(const char *alias, bool useCtor) ce = lookupClassPrefixed(rel, rl); efree(rel); } - if (ce == NULL) { - return NULL; - } + if (ce == NULL) return NULL; cls = (NodeClassInfo *) malloc(sizeof(NodeClassInfo)); memset(cls, 0, sizeof(*cls)); @@ -314,9 +298,7 @@ NodeClassInfo *ParserEngine::resolveNodeClass(const char *alias, bool useCtor) bool ok = true; for (uint32_t i = 0; i < numArgs; i++) { zend_string *argName = ctor->op_array.arg_info[i].name; - if (zend_string_equals_literal(argName, "attributes")) { - continue; - } + if (zend_string_equals_literal(argName, "attributes")) continue; if (nprops >= 16) { ok = false; break; @@ -425,13 +407,9 @@ void ParserEngine::abortForPendingException() bool ParserEngine::isInstanceOf(zv::Ref value, const char *alias) { - if (!value.isObject()) { - return false; - } + if (!value.isObject()) return false; NodeClassInfo *cls = resolveNodeClass(alias, true); - if (cls == NULL) { - return false; - } + if (cls == NULL) return false; return instanceof_function(Z_OBJCE_P(value.raw()), cls->ce); } @@ -481,9 +459,7 @@ void ParserEngine::emitError(const char *msg, zv::Val attributes) void ParserEngine::fatalError(const char *msg, zv::Val attributes) { - if (aborted) { - return; /* the Val releases the attributes */ - } + if (aborted) return; /* the Val releases the attributes */ aborted = true; zval m; ZVAL_STRING(&m, msg); @@ -521,9 +497,7 @@ void ParserEngine::parenthesizedArrowFunctionsAdd(zv::Ref expr) static bool readIntProp(zval *obj, const char *name, int *out) { zv::Ref slot = zv::ObjRef(obj).prop(name, strlen(name)); - if (slot.raw() == NULL || !slot.isLong()) { - return false; - } + if (slot.raw() == NULL || !slot.isLong()) return false; *out = (int) slot.asLong(); return true; } @@ -537,9 +511,7 @@ static bool readIntProp(zval *obj, const char *name, int *out) static bool readIntArrayProp(zval *obj, const char *name, int **out, int *outSize, int *outBias) { zv::Ref slot = zv::ObjRef(obj).prop(name, strlen(name)); - if (slot.raw() == NULL || !slot.isArray()) { - return false; - } + if (slot.raw() == NULL || !slot.isArray()) return false; HashTable *ht = slot.asArrayTable(); zend_long maxKey = -1; zend_long minKey = 0; @@ -548,9 +520,7 @@ static bool readIntArrayProp(zval *obj, const char *name, int **out, int *outSiz zval *v; ZEND_HASH_FOREACH_KEY_VAL(ht, idx, strKey, v) { (void) v; - if (strKey != NULL) { - return false; - } + if (strKey != NULL) return false; zend_long key = (zend_long) idx; if (key > maxKey) { maxKey = key; @@ -559,9 +529,7 @@ static bool readIntArrayProp(zval *obj, const char *name, int **out, int *outSiz minKey = key; } } ZEND_HASH_FOREACH_END(); - if (minKey < 0 && outBias == NULL) { - return false; - } + if (minKey < 0 && outBias == NULL) return false; int bias = (int) -minKey; int size = (int) (maxKey - minKey) + 1; if (zend_hash_num_elements(ht) == 0) { @@ -628,16 +596,12 @@ static bool extractTables(zval *parserObj) && readIntArrayProp(parserObj, "gotoDefault", &t->gotoDefault, &unusedSize, NULL) && readIntArrayProp(parserObj, "ruleToNonTerminal", &t->ruleToNonTerminal, &unusedSize, NULL) && readIntArrayProp(parserObj, "ruleToLength", &t->ruleToLength, &t->numRules, NULL); - if (!ok) { - return false; - } + if (!ok) return false; /* dropTokens: bool array indexed by php token id */ { zv::Ref slot = zv::ObjRef(parserObj).prop("dropTokens", sizeof("dropTokens") - 1); - if (slot.raw() == NULL || !slot.isArray()) { - return false; - } + if (slot.raw() == NULL || !slot.isArray()) return false; int size = t->phpTokenToSymbolSize > 1024 ? t->phpTokenToSymbolSize : 1024; t->dropTokens = (bool *) malloc(sizeof(bool) * (size_t) size); t->dropTokensSize = size; @@ -655,9 +619,7 @@ static bool extractTables(zval *parserObj) /* symbolToName: persistent copies for error messages */ { zv::Ref slot = zv::ObjRef(parserObj).prop("symbolToName", sizeof("symbolToName") - 1); - if (slot.raw() == NULL || !slot.isArray()) { - return false; - } + if (slot.raw() == NULL || !slot.isArray()) return false; HashTable *ht = slot.asArrayTable(); int size = (int) zend_hash_num_elements(ht); t->symbolToName = (zend_string **) malloc(sizeof(zend_string *) * (size_t) size); @@ -678,9 +640,7 @@ static bool extractTables(zval *parserObj) { const char *rel = "PhpParser\\Error"; zend_class_entry *errorCe = lookupClassPrefixed(rel, strlen(rel)); - if (errorCe == NULL) { - return false; - } + if (errorCe == NULL) return false; g_errorCe = errorCe; } @@ -707,9 +667,7 @@ bool ParserEngine::prepareTables(zval *parserObj) void ParserEngine::growStacks(int needed) { - if (needed < stackCap) { - return; - } + if (needed < stackCap) return; int newCap = stackCap < 8 ? 16 : stackCap * 2; while (newCap <= needed) { newCap *= 2; @@ -758,9 +716,7 @@ zend_string *ParserEngine::getErrorMessage(int symbol, int state) idx = t->actionBase[state + t->numNonLeafStates] + sym; found = (idx >= 0 && idx < t->actionTableSize && t->actionCheck[idx] == sym); } - if (!found) { - continue; - } + if (!found) continue; if (t->action[idx] != t->unexpectedTokenRule && t->action[idx] != t->defaultAction && sym != t->errorSymbol) { if (numExpected == 4) { tooMany = true; @@ -860,9 +816,7 @@ zv::Val ParserEngine::doParse() --errorState; } - if (action < t->numNonLeafStates) { - continue; - } + if (action < t->numNonLeafStates) continue; rule = action - t->numNonLeafStates; } else { rule = -action; @@ -908,9 +862,7 @@ zv::Val ParserEngine::doParse() } return zv::Val(); } - if (EG(exception) != NULL) { - return zv::Val(); - } + if (EG(exception) != NULL) return zv::Val(); /* goto - shift nonterminal */ int lastTokenEnd = tokenEndStack[stackPos]; @@ -941,9 +893,7 @@ zv::Val ParserEngine::doParse() zend_string *msg = getErrorMessage(symbol, state); emitError(msg, getAttributesForToken(tokenPos)); zend_string_release(msg); - if (aborted || EG(exception) != NULL) { - return zv::Val(); - } + if (aborted || EG(exception) != NULL) return zv::Val(); } ZEND_FALLTHROUGH; case 1: @@ -968,29 +918,21 @@ zv::Val ParserEngine::doParse() tokenEndStack[stackPos] = tokenEndStack[stackPos - 1]; break; } - if (stackPos <= 0) { - return zv::Val(); - } + if (stackPos <= 0) return zv::Val(); state = stateStack[--stackPos]; } break; } case 3: - if (symbol == 0) { - return zv::Val(); - } + if (symbol == 0) return zv::Val(); symbol = PN_SYMBOL_NONE; discardSymbol = true; break; } - if (discardSymbol) { - break; /* break 2 in PHP: leave the inner loop */ - } + if (discardSymbol) break; /* break 2 in PHP: leave the inner loop */ } - if (state < t->numNonLeafStates) { - break; - } + if (state < t->numNonLeafStates) break; rule = state - t->numNonLeafStates; } } @@ -1041,14 +983,10 @@ bool ParserEngine::commentEnterNode(CommentState &st, zend_object *node) int nextCommentPos = st.positions[st.index]; pt_node_class_info *info = pt_node_class_info_for_object(node); - if (info == NULL || info->attributes_offset < 0) { - return true; - } + if (info == NULL || info->attributes_offset < 0) return true; zval *attrs = OBJ_PROP(node, (uint32_t) info->attributes_offset); ZVAL_DEINDIRECT(attrs); - if (Z_TYPE_P(attrs) != IS_ARRAY) { - return true; - } + if (Z_TYPE_P(attrs) != IS_ARRAY) return true; zval *startPosZv = zend_hash_find(Z_ARRVAL_P(attrs), g_key_startTokenPos); int pos = (startPosZv != NULL && Z_TYPE_P(startPosZv) == IS_LONG) ? (int) Z_LVAL_P(startPosZv) : -1; @@ -1070,9 +1008,7 @@ bool ParserEngine::commentEnterNode(CommentState &st, zend_object *node) collected++; continue; } - if ((zend_long) tok->id != tWhitespace) { - break; - } + if ((zend_long) tok->id != tWhitespace) break; } if (collected > 0) { /* array_reverse */ @@ -1110,20 +1046,12 @@ bool ParserEngine::commentEnterNode(CommentState &st, zend_object *node) void ParserEngine::commentWalkNode(CommentState &st, zend_object *node) { - if (st.stopped) { - return; - } - if (!commentEnterNode(st, node)) { - return; - } + if (st.stopped) return; + if (!commentEnterNode(st, node)) return; pt_node_class_info *info = pt_node_class_info_for_object(node); - if (info == NULL || !PT_HAS_SUBNODES(info)) { - return; - } + if (info == NULL || !PT_HAS_SUBNODES(info)) return; for (uint32_t i = 0; i < info->subnode_count; i++) { - if (st.stopped) { - return; - } + if (st.stopped) return; zval *sub = OBJ_PROP(node, info->subnode_offsets[i]); ZVAL_DEINDIRECT(sub); if (Z_TYPE_P(sub) == IS_OBJECT) { @@ -1138,9 +1066,7 @@ void ParserEngine::commentWalkArray(CommentState &st, HashTable *ht) { zval *item; ZEND_HASH_FOREACH_VAL(ht, item) { - if (st.stopped) { - return; - } + if (st.stopped) return; ZVAL_DEREF(item); if (Z_TYPE_P(item) == IS_OBJECT) { commentWalkNode(st, Z_OBJ_P(item)); @@ -1160,9 +1086,7 @@ void ParserEngine::annotateComments(zv::Ref stmts) numComments++; } } - if (numComments == 0) { - return; - } + if (numComments == 0) return; int *positions = (int *) emalloc(sizeof(int) * (size_t) numComments); int n = 0; for (int i = 0; i < numTokens; i++) { @@ -1191,15 +1115,11 @@ void ParserEngine::checkCreatedArrays() zval *arrayNode; ZEND_HASH_FOREACH_VAL(&createdArrays, arrayNode) { zv::Ref items = prop(zv::Ref(arrayNode), "items"); - if (items.raw() == NULL || !items.isArray()) { - continue; - } + if (items.raw() == NULL || !items.isArray()) continue; zval *item; ZEND_HASH_FOREACH_VAL(items.asArrayTable(), item) { ZVAL_DEREF(item); - if (Z_TYPE_P(item) != IS_OBJECT) { - continue; - } + if (Z_TYPE_P(item) != IS_OBJECT) continue; zv::Ref value = prop(zv::Ref(item), "value"); if (value.raw() != NULL && value.isObject() && isInstanceOf(value, "Expr\\Error")) { emitError("Cannot use empty array elements in arrays", getNodeAttributes(zv::Ref(item))); @@ -1218,9 +1138,7 @@ bool ParserEngine::buildTokens() int i = 0; zval *tokZv; ZEND_HASH_FOREACH_VAL(ht, tokZv) { - if (Z_TYPE_P(tokZv) != IS_OBJECT || i >= num) { - return false; - } + if (Z_TYPE_P(tokZv) != IS_OBJECT || i >= num) return false; zv::ObjRef tok(tokZv); zv::Ref idZv = tok.prop("id", sizeof("id") - 1); zv::Ref textZv = tok.prop("text", sizeof("text") - 1); @@ -1245,13 +1163,9 @@ bool ParserEngine::parse(zval *code, zval *return_value) { /* tokenize via the parser's own lexer (one boundary crossing) */ zv::Ref lexer = zv::ObjRef(parserObj).prop("lexer", sizeof("lexer") - 1); - if (lexer.raw() == NULL || !lexer.isObject()) { - return false; - } + if (lexer.raw() == NULL || !lexer.isObject()) return false; zend_function *tokenizeFn = pt_find_method(Z_OBJCE_P(lexer.raw()), "tokenize", sizeof("tokenize") - 1); - if (tokenizeFn == NULL) { - return false; - } + if (tokenizeFn == NULL) return false; zval tokensLocal; zval args[2]; ZVAL_COPY_VALUE(&args[0], code); @@ -1282,9 +1196,7 @@ bool ParserEngine::parse(zval *code, zval *return_value) } } - if (!buildTokens()) { - return false; - } + if (!buildTokens()) return false; initAttributeKeys(); zv::Val result = doParse(); @@ -1330,9 +1242,7 @@ void pt_register_parser_runner(void) if (Z_TYPE_P(code) == IS_STRING && ParserEngine::prepareTables(parserObj)) { ParserEngine engine(parserObj, errorHandler); - if (engine.parse(code, return_value)) { - return; - } + if (engine.parse(code, return_value)) return; } /* fallback: delegate to the PHP implementation */ diff --git a/turbo-ext/src/parser/ParserRunnerHelpers.cpp b/turbo-ext/src/parser/ParserRunnerHelpers.cpp index 80b023a4548..34d28af6cf1 100644 --- a/turbo-ext/src/parser/ParserRunnerHelpers.cpp +++ b/turbo-ext/src/parser/ParserRunnerHelpers.cpp @@ -43,14 +43,10 @@ static inline char toLowerAscii(char c) /* Case-insensitive ASCII equality against a lowercase literal. */ static bool iequals(zend_string *s, const char *lit, size_t litLen) { - if (ZSTR_LEN(s) != litLen) { - return false; - } + if (ZSTR_LEN(s) != litLen) return false; const char *v = ZSTR_VAL(s); for (size_t i = 0; i < litLen; i++) { - if (toLowerAscii(v[i]) != lit[i]) { - return false; - } + if (toLowerAscii(v[i]) != lit[i]) return false; } return true; } @@ -60,17 +56,13 @@ static bool containsLower(zend_string *hay, const char *needle, size_t nlen) { const char *h = ZSTR_VAL(hay); size_t hlen = ZSTR_LEN(hay); - if (nlen > hlen) { - return false; - } + if (nlen > hlen) return false; for (size_t i = 0; i + nlen <= hlen; i++) { size_t j = 0; while (j < nlen && toLowerAscii(h[i + j]) == needle[j]) { j++; } - if (j == nlen) { - return true; - } + if (j == nlen) return true; } return false; } @@ -84,9 +76,7 @@ static bool isHexDigit(char c) static zend_string *nodeNameString(zv::Ref node) { zv::Ref n = zv::ObjRef(node.raw()).prop("name", sizeof("name") - 1); - if (n.raw() == NULL || !n.isString()) { - return NULL; - } + if (n.raw() == NULL || !n.isString()) return NULL; return n.asString(); } @@ -179,9 +169,7 @@ static ParsedNum baseToNum(const char *s, size_t len, int base) } else { continue; } - if (c >= base) { - continue; - } + if (c >= base) continue; if (mode == 0) { if (num < cutoff || (num == cutoff && c <= cutlim)) { num = num * base + c; @@ -224,13 +212,9 @@ static zend_long strtolBase(const char *s, size_t len, int base) const zend_ulong limit = neg ? ((zend_ulong) ZEND_LONG_MAX + 1) : (zend_ulong) ZEND_LONG_MAX; for (; i < len; i++) { char ch = s[i]; - if (ch < '0' || ch > '9') { - break; - } + if (ch < '0' || ch > '9') break; zend_ulong d = (zend_ulong) (ch - '0'); - if ((int) d >= base) { - break; - } + if ((int) d >= base) break; if (!over) { if (acc > (limit - d) / (zend_ulong) base) { over = true; @@ -239,12 +223,8 @@ static zend_long strtolBase(const char *s, size_t len, int base) } } } - if (over) { - return neg ? ZEND_LONG_MIN : ZEND_LONG_MAX; - } - if (neg) { - return (zend_long) (0 - acc); - } + if (over) return neg ? ZEND_LONG_MIN : ZEND_LONG_MAX; + if (neg) return (zend_long) (0 - acc); return (zend_long) acc; } @@ -253,9 +233,7 @@ static zend_long strtolBase(const char *s, size_t len, int base) /* str_replace($str, '_', '') — returns owned string */ static zend_string *stripUnderscores(zend_string *in) { - if (memchr(ZSTR_VAL(in), '_', ZSTR_LEN(in)) == NULL) { - return zend_string_copy(in); - } + if (memchr(ZSTR_VAL(in), '_', ZSTR_LEN(in)) == NULL) return zend_string_copy(in); smart_str out = {}; const char *s = ZSTR_VAL(in); size_t n = ZSTR_LEN(in); @@ -297,9 +275,7 @@ static zend_string *stripTrailingNewline(zend_string *s) } else if (n >= 1 && (v[n - 1] == '\n' || v[n - 1] == '\r')) { cut = 1; } - if (cut == 0) { - return s; - } + if (cut == 0) return s; zend_string *r = zend_string_init(v, n - cut, 0); zend_string_release(s); return r; @@ -387,9 +363,7 @@ zend_string *ParserEngine::parseEscapeSequences(zend_string *strIn, bool hasQuot while (j < 2 && i + 2 + j < n && isHexDigit(s[i + 2 + j])) { j++; } - if (j == 0) { - break; /* no match: literal backslash */ - } + if (j == 0) break; /* no match: literal backslash */ unsigned val = 0; for (size_t t = 0; t < j; t++) { char h = s[i + 2 + t]; @@ -408,21 +382,15 @@ zend_string *ParserEngine::parseEscapeSequences(zend_string *strIn, bool hasQuot continue; } case 'u': { - if (!parseUnicodeEscape) { - break; - } - if (i + 2 >= n || s[i + 2] != '{') { - break; - } + if (!parseUnicodeEscape) break; + if (i + 2 >= n || s[i + 2] != '{') break; size_t k = i + 3; size_t digits = 0; while (k < n && isHexDigit(s[k])) { k++; digits++; } - if (digits == 0 || k >= n || s[k] != '}') { - break; - } + if (digits == 0 || k >= n || s[k] != '}') break; ParsedNum cp = baseToNum(s + i + 3, digits, 16); /* hexdec overflow → PHP_INT_MAX → codePointToUtf8 throws; > 0x10FFFF throws */ if (cp.isDouble || cp.lval > 0x10FFFF) { @@ -467,18 +435,12 @@ zend_string *ParserEngine::parseEscapeSequences(zend_string *strIn, bool hasQuot void ParserEngine::parseEscapeSequencesInPart(zv::Ref partNode, const char *quote) { NodeClassInfo *cls = resolveNodeClass("Scalar\\String_", true); - if (cls == NULL || cls->ce == NULL) { - return; - } + if (cls == NULL || cls->ce == NULL) return; zend_function *fn = (zend_function *) zend_hash_str_find_ptr( &cls->ce->function_table, "parseescapesequences", sizeof("parseescapesequences") - 1); - if (fn == NULL) { - return; - } + if (fn == NULL) return; zv::Ref value = prop(partNode, "value"); - if (value.raw() == NULL) { - return; - } + if (value.raw() == NULL) return; zval args[3]; ZVAL_COPY(&args[0], value.raw()); ZVAL_STRING(&args[1], quote); @@ -496,9 +458,7 @@ void ParserEngine::parseEscapeSequencesInPart(zv::Ref partNode, const char *quot abortForPendingException(); return; } - if (Z_TYPE(retval) == IS_UNDEF) { - return; - } + if (Z_TYPE(retval) == IS_UNDEF) return; propWrite(partNode, "value", zv::Val::adopt(retval)); } @@ -517,9 +477,7 @@ void ParserEngine::parseEscapeSequencesInPart(zv::Ref partNode, const char *quot */ zend_string *ParserEngine::stripIndentation(zend_string *str, zend_long indentLen, char indentChar, bool newlineAtStart, bool newlineAtEnd, zv::Ref attrsBorrowed) { - if (indentLen == 0) { - return zend_string_copy(str); - } + if (indentLen == 0) return zend_string_copy(str); const char *s = ZSTR_VAL(str); size_t n = ZSTR_LEN(str); @@ -551,9 +509,7 @@ zend_string *ParserEngine::stripIndentation(zend_string *str, zend_long indentLe } pos = q; } - if (pos >= n) { - break; - } + if (pos >= n) break; const char *nl = (const char *) memchr(s + pos, '\n', n - pos); if (nl == NULL) { smart_str_appendl(&out, s + pos, n - pos); @@ -582,14 +538,10 @@ enum static bool isHashbangInlineHtml(zv::Ref stmt) { zv::Ref value = zv::ObjRef(stmt.raw()).prop("value", sizeof("value") - 1); - if (value.raw() == NULL || !value.isString()) { - return false; - } + if (value.raw() == NULL || !value.isString()) return false; const char *s = Z_STRVAL_P(value.raw()); size_t n = Z_STRLEN_P(value.raw()); - if (n < 3 || s[0] != '#' || s[1] != '!' || s[n - 1] != '\n') { - return false; - } + if (n < 3 || s[0] != '#' || s[1] != '!' || s[n - 1] != '\n') return false; return memchr(s, '\n', n - 1) == NULL; } @@ -674,17 +626,11 @@ int ParserEngine::getNamespacingStyle(zv::Ref stmts) void ParserEngine::fixupNamespaceAttributes(zv::Ref nsNode) { zv::Ref stmts = prop(nsNode, "stmts"); - if (stmts.raw() == NULL || !stmts.isArray()) { - return; - } + if (stmts.raw() == NULL || !stmts.isArray()) return; uint32_t count = zend_hash_num_elements(stmts.asArrayTable()); - if (count == 0) { - return; - } + if (count == 0) return; zv::Ref lastStmt = itemAt(stmts, count - 1); - if (lastStmt.raw() == NULL || !lastStmt.isObject()) { - return; - } + if (lastStmt.raw() == NULL || !lastStmt.isObject()) return; zv::Arr lastAttrs = getNodeAttributes(lastStmt); static const char *const endKeys[3] = {"endLine", "endFilePos", "endTokenPos"}; for (int k = 0; k < 3; k++) { @@ -833,36 +779,26 @@ zv::Val ParserEngine::handleBuiltinTypes(zv::Ref nameNode) zend_long ParserEngine::getFloatCastKind(zv::Ref castTokenText) { zend_string *s = castTokenText.asString(); - if (containsLower(s, "float", 5)) { - return 2; /* Double::KIND_FLOAT */ - } - if (containsLower(s, "real", 4)) { - return 3; /* Double::KIND_REAL */ - } + if (containsLower(s, "float", 5)) return 2; /* Double::KIND_FLOAT */ + if (containsLower(s, "real", 4)) return 3; /* Double::KIND_REAL */ return 1; /* Double::KIND_DOUBLE */ } zend_long ParserEngine::getIntCastKind(zv::Ref castTokenText) { - if (containsLower(castTokenText.asString(), "integer", 7)) { - return 2; /* Cast\Int_::KIND_INTEGER */ - } + if (containsLower(castTokenText.asString(), "integer", 7)) return 2; /* Cast\Int_::KIND_INTEGER */ return 1; /* Cast\Int_::KIND_INT */ } zend_long ParserEngine::getBoolCastKind(zv::Ref castTokenText) { - if (containsLower(castTokenText.asString(), "boolean", 7)) { - return 2; /* Cast\Bool_::KIND_BOOLEAN */ - } + if (containsLower(castTokenText.asString(), "boolean", 7)) return 2; /* Cast\Bool_::KIND_BOOLEAN */ return 1; /* Cast\Bool_::KIND_BOOL */ } zend_long ParserEngine::getStringCastKind(zv::Ref castTokenText) { - if (containsLower(castTokenText.asString(), "binary", 6)) { - return 2; /* Cast\String_::KIND_BINARY */ - } + if (containsLower(castTokenText.asString(), "binary", 6)) return 2; /* Cast\String_::KIND_BINARY */ return 1; /* Cast\String_::KIND_STRING */ } @@ -1059,9 +995,7 @@ zv::Val ParserEngine::parseDocString(zv::Ref startTokenRef, zv::Ref contents, zv if (kind == 3 /* KIND_HEREDOC */) { value = parseEscapeSequences(stripped, false, 0, parseUnicodeEscape); zend_string_release(stripped); - if (value == NULL) { - return zv::Val(); - } + if (value == NULL) return zv::Val(); } else { value = stripped; } @@ -1107,14 +1041,10 @@ zv::Val ParserEngine::parseDocString(zv::Ref startTokenRef, zv::Ref contents, zv setNodeAttribute(part, "rawValue", zv::Val::string(stripped)); zend_string *parsed = parseEscapeSequences(stripped, false, 0, parseUnicodeEscape); zend_string_release(stripped); - if (parsed == NULL) { - return zv::Val(); - } + if (parsed == NULL) return zv::Val(); bool isEmpty = ZSTR_LEN(parsed) == 0; propWrite(part, "value", zv::Val::adoptString(parsed)); - if (isEmpty) { - continue; - } + if (isEmpty) continue; } newContents.push(part); } @@ -1136,12 +1066,8 @@ int ParserEngine::getCommentBeforeToken(int tokenPos) zend_long tDocComment = tokenIdDocComment(); while (--tokenPos >= 0) { const Token *t = &tokens[tokenPos]; - if (!isDropToken(tables, t->id)) { - break; - } - if ((zend_long) t->id == tComment || (zend_long) t->id == tDocComment) { - return tokenPos; - } + if (!isDropToken(tables, t->id)) break; + if ((zend_long) t->id == tComment || (zend_long) t->id == tDocComment) return tokenPos; } return -1; } @@ -1149,9 +1075,7 @@ int ParserEngine::getCommentBeforeToken(int tokenPos) zv::Val ParserEngine::maybeCreateZeroLengthNop(int tokenPos) { int ci = getCommentBeforeToken(tokenPos); - if (ci < 0) { - return zv::Val::null(); - } + if (ci < 0) return zv::Val::null(); const Token *t = &tokens[ci]; const char *text = ZSTR_VAL(t->text); size_t tlen = ZSTR_LEN(t->text); @@ -1183,9 +1107,7 @@ zv::Val ParserEngine::maybeCreateZeroLengthNop(int tokenPos) zv::Val ParserEngine::maybeCreateNop(int tokenStartPos, int tokenEndPos) { - if (getCommentBeforeToken(tokenStartPos) < 0) { - return zv::Val::null(); - } + if (getCommentBeforeToken(tokenStartPos) < 0) return zv::Val::null(); return newNode("Node\\Stmt\\Nop", getAttributes(tokenStartPos, tokenEndPos)); } @@ -1205,9 +1127,7 @@ zv::Val ParserEngine::handleHaltCompiler() /* Prevent the lexer from returning any further tokens. */ tokenPos = numTokens - 2; - if (text != NULL) { - return zv::Val::string(text); - } + if (text != NULL) return zv::Val::string(text); return zv::Val::string("", 0); } @@ -1247,9 +1167,7 @@ zv::Val ParserEngine::fixupArrayDestructuring(zv::Ref arrayNode) if (value.raw() != NULL && value.isObject() && isInstanceOf(value, "Node\\Expr\\Array_")) { zv::Val inner = fixupArrayDestructuring(value); - if (aborted) { - return zv::Val(); - } + if (aborted) return zv::Val(); zv::Ref key = prop(item, "key"); if (key.raw() != NULL && Z_TYPE_P(key.raw()) == IS_NULL) { key = zv::Ref(NULL); @@ -1258,9 +1176,7 @@ zv::Val ParserEngine::fixupArrayDestructuring(zv::Ref arrayNode) /* new ArrayItem($fixedUp, $item->key, $item->byRef, $item->getAttributes()) */ zv::Val newItem = newNode("Node\\ArrayItem", getNodeAttributes(item), inner, key.raw() != NULL ? Borrowed(key) : Borrowed(nullptr), byRef, zv::Val::boolean(false)); - if (aborted) { - return zv::Val(); - } + if (aborted) return zv::Val(); newItems.push(std::move(newItem)); continue; } @@ -1298,16 +1214,12 @@ zv::Val ParserEngine::fixupArrayDestructuring(zv::Ref arrayNode) void ParserEngine::postprocessList(zv::Ref listNode) { zv::Ref items = prop(listNode, "items"); - if (items.raw() == NULL || !items.isArray()) { - return; - } + if (items.raw() == NULL || !items.isArray()) return; bool any = false; for (auto entry : zv::ArrRef(items.raw())) { zv::Ref item = entry.value(); - if (!item.isObject()) { - continue; - } + if (!item.isObject()) continue; zv::Ref value = prop(item, "value"); if (value.raw() != NULL && value.isObject() && isInstanceOf(value, "Node\\Expr\\Error")) { @@ -1315,18 +1227,14 @@ void ParserEngine::postprocessList(zv::Ref listNode) break; } } - if (!any) { - return; - } + if (!any) return; /* $node->items[$i] = null for the Error placeholders */ zv::Arr newItems = dupArray(items); zend_ulong idx; zval *it; ZEND_HASH_FOREACH_NUM_KEY_VAL(newItems.table(), idx, it) { - if (Z_TYPE_P(it) != IS_OBJECT) { - continue; - } + if (Z_TYPE_P(it) != IS_OBJECT) continue; zv::Ref value = prop(zv::Ref(it), "value"); if (value.raw() != NULL && value.isObject() && isInstanceOf(value, "Node\\Expr\\Error")) { @@ -1344,13 +1252,9 @@ void ParserEngine::fixupAlternativeElse(zv::Ref node) { /* Make sure a trailing nop statement carrying comments is part of the node. */ zv::Ref stmts = prop(node, "stmts"); - if (stmts.raw() == NULL || !stmts.isArray()) { - return; - } + if (stmts.raw() == NULL || !stmts.isArray()) return; uint32_t numStmts = zend_hash_num_elements(stmts.asArrayTable()); - if (numStmts == 0) { - return; - } + if (numStmts == 0) return; zv::Ref last = itemAt(stmts, numStmts - 1); if (last.raw() == NULL || !last.isObject() || !isInstanceOf(last, "Node\\Stmt\\Nop")) { @@ -1482,9 +1386,7 @@ void ParserEngine::checkTryCatch(zv::Ref node) void ParserEngine::checkNamespace(zv::Ref node) { zv::Ref stmts = prop(node, "stmts"); - if (stmts.raw() == NULL || !stmts.isArray()) { - return; - } + if (stmts.raw() == NULL || !stmts.isArray()) return; for (auto entry : zv::ArrRef(stmts.raw())) { zv::Ref stmt = entry.value(); if (stmt.isObject() && isInstanceOf(stmt, "Node\\Stmt\\Namespace_")) { @@ -1495,13 +1397,9 @@ void ParserEngine::checkNamespace(zv::Ref node) void ParserEngine::checkClassName(zv::Ref name, int namePos) { - if (name.raw() == NULL || !name.isObject()) { - return; - } + if (name.raw() == NULL || !name.isObject()) return; zend_string *n = nodeNameString(name); - if (n == NULL || !isSpecialClassName(n)) { - return; - } + if (n == NULL || !isSpecialClassName(n)) return; zend_string *msg = zend_strpprintf(0, "Cannot use '%s' as class name as it is reserved", ZSTR_VAL(n)); emitError(msg, getAttributesAt(namePos)); zend_string_release(msg); @@ -1509,18 +1407,12 @@ void ParserEngine::checkClassName(zv::Ref name, int namePos) void ParserEngine::checkImplementedInterfaces(zv::Ref interfaces) { - if (interfaces.raw() == NULL || !interfaces.isArray()) { - return; - } + if (interfaces.raw() == NULL || !interfaces.isArray()) return; for (auto entry : zv::ArrRef(interfaces.raw())) { zv::Ref iface = entry.value(); - if (!iface.isObject()) { - continue; - } + if (!iface.isObject()) continue; zend_string *n = nodeNameString(iface); - if (n == NULL || !isSpecialClassName(n)) { - continue; - } + if (n == NULL || !isSpecialClassName(n)) continue; zend_string *msg = zend_strpprintf(0, "Cannot use '%s' as interface name as it is reserved", ZSTR_VAL(n)); emitError(msg, getNodeAttributes(iface)); zend_string_release(msg); @@ -1562,9 +1454,7 @@ void ParserEngine::checkClassMethod(zv::Ref node, int modifierPos) zend_long f = flags.raw() != NULL ? flags.toLong() : 0; zv::Ref name = prop(node, "name"); zend_string *n = (name.raw() != NULL && name.isObject()) ? nodeNameString(name) : NULL; - if (n == NULL) { - return; - } + if (n == NULL) return; if ((f & PN_MOD_STATIC) != 0) { const char *fmt = NULL; @@ -1607,13 +1497,9 @@ void ParserEngine::checkClassConst(zv::Ref node, int modifierPos) void ParserEngine::checkUseUse(zv::Ref node, int namePos) { zv::Ref alias = prop(node, "alias"); - if (alias.raw() == NULL || !alias.isObject()) { - return; - } + if (alias.raw() == NULL || !alias.isObject()) return; zend_string *aliasStr = nodeNameString(alias); - if (aliasStr == NULL || !isSpecialClassName(aliasStr)) { - return; - } + if (aliasStr == NULL || !isSpecialClassName(aliasStr)) return; zv::Ref name = prop(node, "name"); zend_string *nameStr = (name.raw() != NULL && name.isObject()) ? nodeNameString(name) : NULL; /* sprintf('Cannot use %s as %s because \'%2$s\' is a special class name', ...) */ @@ -1643,13 +1529,9 @@ void ParserEngine::checkEmptyPropertyHookList(zv::Ref hooks, int hookPos) void ParserEngine::checkPropertyHook(zv::Ref hook, int paramListPos, bool hasParamList) { zv::Ref name = prop(hook, "name"); - if (name.raw() == NULL || !name.isObject()) { - return; - } + if (name.raw() == NULL || !name.isObject()) return; zend_string *n = nodeNameString(name); - if (n == NULL) { - return; - } + if (n == NULL) return; bool isGet = iequals(n, "get", 3); bool isSet = iequals(n, "set", 3); if (!isGet && !isSet) { @@ -1677,15 +1559,9 @@ void ParserEngine::checkConstantAttributes(zv::Ref node) void ParserEngine::checkPipeOperatorParentheses(zv::Ref expr) { - if (!expr.isObject()) { - return; - } - if (!isInstanceOf(expr, "Node\\Expr\\ArrowFunction")) { - return; - } - if (zend_hash_index_exists(&parenthesizedArrowFns, (zend_ulong) Z_OBJ_HANDLE_P(expr.raw()))) { - return; - } + if (!expr.isObject()) return; + if (!isInstanceOf(expr, "Node\\Expr\\ArrowFunction")) return; + if (zend_hash_index_exists(&parenthesizedArrowFns, (zend_ulong) Z_OBJ_HANDLE_P(expr.raw()))) return; emitError("Arrow functions on the right hand side of |> must be parenthesized", getNodeAttributes(expr)); } @@ -1718,17 +1594,13 @@ void ParserEngine::addPropertyNameToHooks(zv::Ref node) } } } - if (nameVal.isUndef()) { - return; - } + if (nameVal.isUndef()) return; zv::Ref hooks = prop(node, "hooks"); if (hooks.raw() != NULL && hooks.isArray()) { for (auto entry : zv::ArrRef(hooks.raw())) { zv::Ref hook = entry.value(); - if (!hook.isObject()) { - continue; - } + if (!hook.isObject()) continue; setNodeAttribute(hook, "propertyName", zv::Val::copyOf(nameVal.ref())); } } @@ -1777,9 +1649,7 @@ zv::Val ParserEngine::createExitExpr(zv::Ref nameStr, int namePos, zv::Ref args, } zv::Val nameNode = newName(nameStr, getAttributesAt(namePos)); - if (aborted) { - return zv::Val(); - } + if (aborted) return zv::Val(); return newNode("Node\\Expr\\FuncCall", std::move(attributes), nameNode, args); } @@ -1816,9 +1686,7 @@ zend_string *ParserEngine::prepareName(zv::Ref nameVal) } if (nameVal.isObject() && isInstanceOf(nameVal, "Node\\Name")) { zv::Ref inner = prop(nameVal, "name"); - if (inner.raw() != NULL && inner.isString()) { - return zend_string_copy(inner.asString()); - } + if (inner.raw() != NULL && inner.isString()) return zend_string_copy(inner.asString()); } fatalError("Expected string, array of parts or Name instance", zv::Arr::empty()); return NULL; @@ -1827,9 +1695,7 @@ zend_string *ParserEngine::prepareName(zv::Ref nameVal) zv::Val ParserEngine::newNameVariant(const char *alias, zv::Ref strOrParts, zv::Val attributes) { zend_string *prepared = prepareName(strOrParts); - if (prepared == NULL) { - return zv::Val(); - } + if (prepared == NULL) return zv::Val(); /* Name's final ctor only runs prepareName + assigns; constructing with the * already-prepared string through the prop-slot path is byte-equivalent. */ return newNode(alias, std::move(attributes), zv::Val::adoptString(prepared)); @@ -1868,9 +1734,7 @@ zv::Val ParserEngine::stringFromString(zv::Ref raw, zv::Arr attributes, bool par value = parseEscapeSequences(inner, true, '"', parseUnicodeEscape); } zend_string_release(inner); - if (value == NULL) { - return zv::Val(); - } + if (value == NULL) return zv::Val(); return newNode("Node\\Scalar\\String_", std::move(attributes), zv::Val::adoptString(value)); } diff --git a/turbo-ext/src/support.cpp b/turbo-ext/src/support.cpp index 7f2f87e6cbb..2d06ed3a4c5 100644 --- a/turbo-ext/src/support.cpp +++ b/turbo-ext/src/support.cpp @@ -60,9 +60,7 @@ zend_class_entry *pt_class(int idx) zend_class_entry *ce; zend_string *name; - if (EXPECTED(ref->ce != NULL)) { - return ref->ce; - } + if (EXPECTED(ref->ce != NULL)) return ref->ce; if (ref->configured != NULL) { name = zend_string_copy(ref->configured); @@ -127,9 +125,7 @@ static bool pt_node_class_cache_inited = false; void pt_init_strs() { - if (pt_strs_inited) { - return; - } + if (pt_strs_inited) return; pt_str_cache_printer = zend_string_init("phpstan_cache_printer", sizeof("phpstan_cache_printer") - 1, 0); pt_str_contains_super_global = zend_string_init("containsSuperGlobal", sizeof("containsSuperGlobal") - 1, 0); pt_str_array_map_args = zend_string_init("arrayMapArgs", sizeof("arrayMapArgs") - 1, 0); @@ -209,12 +205,8 @@ zval *pt_trinary_singleton(zend_long value) PT_G(trinary_inited) = true; } - if (value == PT_TRI_YES) { - return &PT_G(trinary_yes); - } - if (value == PT_TRI_MAYBE) { - return &PT_G(trinary_maybe); - } + if (value == PT_TRI_YES) return &PT_G(trinary_yes); + if (value == PT_TRI_MAYBE) return &PT_G(trinary_maybe); return &PT_G(trinary_no); } @@ -238,14 +230,10 @@ bool pt_call_type_equals(zval *type_a, zval *type_b) zval ret, args[1]; bool result; - if (UNEXPECTED(fn == NULL)) { - return false; - } + if (UNEXPECTED(fn == NULL)) return false; ZVAL_COPY_VALUE(&args[0], type_b); zend_call_known_function(fn, Z_OBJ_P(type_a), ce, &ret, 1, args, NULL); - if (UNEXPECTED(EG(exception))) { - return false; - } + if (UNEXPECTED(EG(exception))) return false; result = Z_TYPE(ret) == IS_TRUE; zval_ptr_dtor(&ret); return result; @@ -253,9 +241,7 @@ bool pt_call_type_equals(zval *type_a, zval *type_b) bool pt_types_identical_or_equal(zval *type_a, zval *type_b) { - if (Z_OBJ_P(type_a) == Z_OBJ_P(type_b)) { - return true; - } + if (Z_OBJ_P(type_a) == Z_OBJ_P(type_b)) return true; return pt_call_type_equals(type_a, type_b); } @@ -265,13 +251,9 @@ bool pt_type_combinator_binary(const char *lcname, size_t len, zval *type_a, zva zend_function *fn; zval args[2]; - if (UNEXPECTED(ce == NULL)) { - return false; - } + if (UNEXPECTED(ce == NULL)) return false; fn = pt_find_method(ce, lcname, len); - if (UNEXPECTED(fn == NULL)) { - return false; - } + if (UNEXPECTED(fn == NULL)) return false; ZVAL_COPY_VALUE(&args[0], type_a); ZVAL_COPY_VALUE(&args[1], type_b); zend_call_known_function(fn, NULL, ce, result, 2, args, NULL); @@ -286,25 +268,17 @@ bool pt_type_describe_precise(zval *type, zval *result) if (UNEXPECTED(!PT_G(verbosity_inited))) { zend_class_entry *vce = pt_class(PT_CLASS_VERBOSITY_LEVEL); - if (UNEXPECTED(vce == NULL)) { - return false; - } + if (UNEXPECTED(vce == NULL)) return false; fn = pt_find_method(vce, "precise", sizeof("precise") - 1); - if (UNEXPECTED(fn == NULL)) { - return false; - } + if (UNEXPECTED(fn == NULL)) return false; zend_call_known_function(fn, NULL, vce, &PT_G(verbosity_precise), 0, NULL, NULL); - if (UNEXPECTED(EG(exception))) { - return false; - } + if (UNEXPECTED(EG(exception))) return false; PT_G(verbosity_inited) = true; } ce = Z_OBJCE_P(type); fn = pt_find_method(ce, "describe", sizeof("describe") - 1); - if (UNEXPECTED(fn == NULL)) { - return false; - } + if (UNEXPECTED(fn == NULL)) return false; ZVAL_COPY_VALUE(&args[0], &PT_G(verbosity_precise)); zend_call_known_function(fn, Z_OBJ_P(type), ce, result, 1, args, NULL); return !EG(exception); @@ -313,9 +287,7 @@ bool pt_type_describe_precise(zval *type, zval *result) void pt_throw_should_not_happen() { zend_class_entry *ce = pt_class(PT_CLASS_SHOULD_NOT_HAPPEN); - if (ce == NULL) { - return; /* error already thrown */ - } + if (ce == NULL) return; /* error already thrown */ zend_throw_exception(ce, "Internal error.", 0); } @@ -330,9 +302,7 @@ bool pt_call_scope_bool(zval *scope, const char *lcname, size_t len, uint32_t ar return false; } zend_call_known_function(fn, Z_OBJ_P(scope), ce, &ret, argc, argv, NULL); - if (UNEXPECTED(EG(exception))) { - return false; - } + if (UNEXPECTED(EG(exception))) return false; *out = zend_is_true(&ret); zval_ptr_dtor(&ret); return true; @@ -354,9 +324,7 @@ static void pt_node_class_info_free(zval *zv) int32_t pt_instance_prop_offset(zend_class_entry *ce, const char *name, size_t len) { zend_property_info *info = (zend_property_info *) zend_hash_str_find_ptr(&ce->properties_info, name, len); - if (info == NULL || (info->flags & ZEND_ACC_STATIC) != 0) { - return -1; - } + if (info == NULL || (info->flags & ZEND_ACC_STATIC) != 0) return -1; return (int32_t) info->offset; } @@ -371,9 +339,7 @@ pt_node_class_info *pt_get_node_class_info(zend_class_entry *ce) } info = (pt_node_class_info *) zend_hash_find_ptr(&pt_node_class_cache, ce->name); - if (EXPECTED(info != NULL)) { - return info; - } + if (EXPECTED(info != NULL)) return info; info = (pt_node_class_info *) ecalloc(1, sizeof(pt_node_class_info)); info->attributes_offset = pt_instance_prop_offset(ce, "attributes", sizeof("attributes") - 1); @@ -397,12 +363,8 @@ pt_node_class_info *pt_node_class_info_for_object(zend_object *obj) zend_function *fn; zval retval; - if (info == NULL) { - return NULL; - } - if (info->subnode_offsets != NULL || info->subnode_count == UINT32_MAX) { - return info; - } + if (info == NULL) return NULL; + if (info->subnode_offsets != NULL || info->subnode_count == UINT32_MAX) return info; fn = (zend_function *) zend_hash_str_find_ptr(&ce->function_table, "getsubnodenames", sizeof("getsubnodenames") - 1); if (fn == NULL || (fn->common.fn_flags & ZEND_ACC_ABSTRACT) != 0) { @@ -426,9 +388,7 @@ pt_node_class_info *pt_node_class_info_for_object(zend_object *obj) info->subnode_offsets = (uint32_t *) emalloc(sizeof(uint32_t) * (count > 0 ? count : 1)); ZEND_HASH_FOREACH_VAL(names, name_zv) { int32_t off; - if (Z_TYPE_P(name_zv) != IS_STRING) { - continue; - } + if (Z_TYPE_P(name_zv) != IS_STRING) continue; off = pt_instance_prop_offset(ce, Z_STRVAL_P(name_zv), Z_STRLEN_P(name_zv)); if (off >= 0) { info->subnode_offsets[i++] = (uint32_t) off; @@ -445,14 +405,10 @@ zval *pt_node_attribute(zend_object *node, zend_string *name) pt_node_class_info *info = pt_get_node_class_info(node->ce); zval *attrs; - if (info == NULL || info->attributes_offset < 0) { - return NULL; - } + if (info == NULL || info->attributes_offset < 0) return NULL; attrs = OBJ_PROP(node, info->attributes_offset); ZVAL_DEREF(attrs); - if (Z_TYPE_P(attrs) != IS_ARRAY) { - return NULL; - } + if (Z_TYPE_P(attrs) != IS_ARRAY) return NULL; return zend_hash_find(Z_ARRVAL_P(attrs), name); } @@ -461,14 +417,10 @@ bool pt_node_set_attribute(zend_object *node, zend_string *name, zval *value) pt_node_class_info *info = pt_get_node_class_info(node->ce); zval *attrs; - if (info == NULL || info->attributes_offset < 0) { - return false; - } + if (info == NULL || info->attributes_offset < 0) return false; attrs = OBJ_PROP(node, info->attributes_offset); ZVAL_DEREF(attrs); - if (Z_TYPE_P(attrs) != IS_ARRAY) { - return false; - } + if (Z_TYPE_P(attrs) != IS_ARRAY) return false; SEPARATE_ARRAY(attrs); Z_TRY_ADDREF_P(value); zend_hash_update(Z_ARRVAL_P(attrs), name, value); @@ -511,9 +463,7 @@ static zend_string *pt_node_printed_expr(zend_object *node, zval *expr_printer) { pt_node_class_info *info = pt_get_node_class_info(node->ce); - if (info == NULL) { - return NULL; - } + if (info == NULL) return NULL; /* fast path: '$' . $node->name for Variable with a string name */ if (info->is_variable && info->name_offset >= 0) { @@ -530,14 +480,10 @@ static zend_string *pt_node_printed_expr(zend_object *node, zval *expr_printer) } zval *attr = pt_node_attribute(node, pt_str_cache_printer); - if (attr != NULL && Z_TYPE_P(attr) == IS_STRING) { - return zend_string_copy(Z_STR_P(attr)); - } + if (attr != NULL && Z_TYPE_P(attr) == IS_STRING) return zend_string_copy(Z_STR_P(attr)); zval printed; - if (!pt_call_print_expr(expr_printer, node, &printed)) { - return NULL; - } + if (!pt_call_print_expr(expr_printer, node, &printed)) return NULL; return Z_STR(printed); /* take ownership */ } @@ -550,21 +496,15 @@ zend_string *pt_node_key(zend_object *node, zval *expr_printer) /* the Variable fast path returns before any suffix handling below, same * as the twin: a Variable node never carries the suffix attributes */ pt_node_class_info *info = pt_get_node_class_info(node->ce); - if (info == NULL) { - return NULL; - } + if (info == NULL) return NULL; if (info->is_variable && info->name_offset >= 0) { zval *name = OBJ_PROP(node, info->name_offset); ZVAL_DEREF(name); - if (Z_TYPE_P(name) == IS_STRING) { - return pt_node_printed_expr(node, expr_printer); - } + if (Z_TYPE_P(name) == IS_STRING) return pt_node_printed_expr(node, expr_printer); } key = pt_node_printed_expr(node, expr_printer); - if (key == NULL) { - return NULL; - } + if (key == NULL) return NULL; /* FunctionLike with arrayMapArgs + startFilePos: append the array_map * argument suffix exactly like MutatingScope::getNodeKey() */ @@ -591,20 +531,14 @@ zend_string *pt_node_key(zend_object *node, zval *expr_printer) zval *arg_deref = arg; zval *value_prop; ZVAL_DEREF(arg_deref); - if (Z_TYPE_P(arg_deref) != IS_OBJECT) { - continue; - } + if (Z_TYPE_P(arg_deref) != IS_OBJECT) continue; { int32_t voff = pt_instance_prop_offset(Z_OBJCE_P(arg_deref), "value", sizeof("value") - 1); - if (voff < 0) { - continue; - } + if (voff < 0) continue; value_prop = OBJ_PROP(Z_OBJ_P(arg_deref), voff); ZVAL_DEREF(value_prop); } - if (Z_TYPE_P(value_prop) != IS_OBJECT) { - continue; - } + if (Z_TYPE_P(value_prop) != IS_OBJECT) continue; smart_str_appendc(&str, ':'); { /* plain printExpr like the twin — NOT the full node @@ -641,17 +575,11 @@ zend_object *pt_find_first_recursive(zend_object *node, pt_node_matcher matcher, zend_class_entry *node_iface; uint32_t i; - if (matcher(node, ctx)) { - return node; - } - if (UNEXPECTED(((pt_find_ctx *) ctx)->failed)) { - return NULL; - } + if (matcher(node, ctx)) return node; + if (UNEXPECTED(((pt_find_ctx *) ctx)->failed)) return NULL; info = pt_node_class_info_for_object(node); - if (info == NULL || !PT_HAS_SUBNODES(info)) { - return NULL; - } + if (info == NULL || !PT_HAS_SUBNODES(info)) return NULL; node_iface = pt_class(PT_CLASS_NODE); if (UNEXPECTED(node_iface == NULL)) { @@ -665,9 +593,7 @@ zend_object *pt_find_first_recursive(zend_object *node, pt_node_matcher matcher, if (Z_TYPE_P(val) == IS_OBJECT) { if (instanceof_function(Z_OBJCE_P(val), node_iface)) { zend_object *found = pt_find_first_recursive(Z_OBJ_P(val), matcher, ctx); - if (found != NULL || ((pt_find_ctx *) ctx)->failed) { - return found; - } + if (found != NULL || ((pt_find_ctx *) ctx)->failed) return found; } } else if (Z_TYPE_P(val) == IS_ARRAY) { zval *el; @@ -676,9 +602,7 @@ zend_object *pt_find_first_recursive(zend_object *node, pt_node_matcher matcher, ZVAL_DEREF(el_deref); if (Z_TYPE_P(el_deref) == IS_OBJECT && instanceof_function(Z_OBJCE_P(el_deref), node_iface)) { zend_object *found = pt_find_first_recursive(Z_OBJ_P(el_deref), matcher, ctx); - if (found != NULL || ((pt_find_ctx *) ctx)->failed) { - return found; - } + if (found != NULL || ((pt_find_ctx *) ctx)->failed) return found; } } ZEND_HASH_FOREACH_END(); } @@ -715,14 +639,10 @@ static bool pt_superglobal_matcher(zend_object *node, void *ctx) zval *name; (void) ctx; - if (info == NULL || !info->is_variable || info->name_offset < 0) { - return false; - } + if (info == NULL || !info->is_variable || info->name_offset < 0) return false; name = OBJ_PROP(node, info->name_offset); ZVAL_DEREF(name); - if (Z_TYPE_P(name) != IS_STRING) { - return false; - } + if (Z_TYPE_P(name) != IS_STRING) return false; return pt_is_superglobal_name(Z_STR_P(name)); } @@ -736,9 +656,7 @@ bool pt_expr_contains_superglobal(zend_object *expr) pt_init_strs(); attr = pt_node_attribute(expr, pt_str_contains_super_global); - if (attr != NULL && (Z_TYPE_P(attr) == IS_TRUE || Z_TYPE_P(attr) == IS_FALSE)) { - return Z_TYPE_P(attr) == IS_TRUE; - } + if (attr != NULL && (Z_TYPE_P(attr) == IS_TRUE || Z_TYPE_P(attr) == IS_FALSE)) return Z_TYPE_P(attr) == IS_TRUE; memset(&ctx, 0, sizeof(ctx)); contains = pt_find_first_recursive(expr, pt_superglobal_matcher, &ctx) != NULL; @@ -788,14 +706,10 @@ bool pt_holder_and(zval *a, zval *b, zval *result) } return true; } - if (UNEXPECTED(EG(exception))) { - return false; - } + if (UNEXPECTED(EG(exception))) return false; { zval union_type; - if (UNEXPECTED(!pt_type_combinator_binary("union", sizeof("union") - 1, a_type, b_type, &union_type))) { - return false; - } + if (UNEXPECTED(!pt_type_combinator_binary("union", sizeof("union") - 1, a_type, b_type, &union_type))) return false; pt_holder_create(result, OBJ_PROP_NUM(ao, PT_ETH_PROP_EXPR), &union_type, ac & bc); zval_ptr_dtor(&union_type); } diff --git a/turbo-ext/src/zv.h b/turbo-ext/src/zv.h index 3ee51395495..fb1b5c78e40 100644 --- a/turbo-ext/src/zv.h +++ b/turbo-ext/src/zv.h @@ -534,9 +534,7 @@ class ObjRef Ref prop(const char *name, size_t len) const { int32_t offset = pt_instance_prop_offset(obj->ce, name, len); - if (offset < 0) { - return Ref(NULL); - } + if (offset < 0) return Ref(NULL); zval *slot = OBJ_PROP(obj, (uint32_t) offset); ZVAL_DEINDIRECT(slot); return Ref(slot); From 52b2ff1b80d95f7573a49397d0c15e03e6a6630b Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Tue, 15 Sep 2026 22:55:19 +0200 Subject: [PATCH 02/12] Generate the parameter-parsing glue of the native registrations A method that only parses its parameters and delegates them in order to a handle member is registered as cls.method<&Handle::member, zp::K...>(...): reg::detail::Bound generates the handler (zv::Val, void, or bool with a trailing bool & out parameter). Every other ZEND_PARSE_PARAMETERS block of the supported kinds is written zp::parse(execute_data, ...). Both expand to the engine's own ZPP macros per slot, so the handlers compile to the hand-written glue's code (a generated handler differs only in how its error paths are outlined). The 46 parameter blocks of the existing native sources parse through zp::parse; the ports to come register their delegating methods in the bound form, which side-by-side.php accepts. PT_RETURN_VAL, the zv::Val-into-return_value tail the generated handlers share with the hand-written ones, lives in reg.h. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01MShHKdUB19w38vboJLPKXy --- turbo-ext/CLAUDE.md | 12 +- turbo-ext/README.md | 7 +- turbo-ext/src/ArenaCache.cpp | 38 +-- turbo-ext/src/CombinationsHelper.cpp | 4 +- turbo-ext/src/ExpressionResultStorage.cpp | 13 +- turbo-ext/src/ExpressionTypeHolder.cpp | 10 +- turbo-ext/src/NodeScanner.cpp | 4 +- turbo-ext/src/NodeTraverser.cpp | 12 +- turbo-ext/src/PhpFileCleaner.cpp | 5 +- turbo-ext/src/ScopeOps.cpp | 72 +---- turbo-ext/src/SymbolFinderInFiles.cpp | 9 +- turbo-ext/src/TrinaryLogic.cpp | 8 +- turbo-ext/src/TypeCombinatorCache.cpp | 8 +- turbo-ext/src/main.cpp | 18 +- turbo-ext/src/parser/ParserRunner.cpp | 6 +- turbo-ext/src/reg.h | 311 +++++++++++++++++++++- 16 files changed, 366 insertions(+), 171 deletions(-) diff --git a/turbo-ext/CLAUDE.md b/turbo-ext/CLAUDE.md index 3f972d918e9..1388efae439 100644 --- a/turbo-ext/CLAUDE.md +++ b/turbo-ext/CLAUDE.md @@ -42,9 +42,15 @@ being ≥0.5% faster is. When the estimate is marginal, don't port. phpstan_turbo` that mirrors the PHP twin method for method (see `TrinaryLogic.cpp` as the reference; `and`/`or` keyword clashes get a trailing underscore); registration goes through the `reg::Class` builder - in `reg.h` — one `cls.method("name", flags, requiredArgs, { args... }, - lambda)` declaration per method, where the lambda body is only - ZEND_PARSE_PARAMETERS glue + one delegation line (see TrinaryLogic.cpp). + in `reg.h` — one declaration per method: a method that only parses its + parameters and hands them, in order, to a handle member returning + `zv::Val`, `void` or `bool` with a trailing `bool &` out parameter is + `cls.method<&Handle::member, zp::Obj, zp::Bool>("name", flags, { args... }, + returns)` with a generated handler; any other glue is a + `cls.method("name", flags, requiredArgs, { args... }, lambda)` whose + lambda parses with `zp::parse>(execute_data, + ...)` (the raw ZEND_PARSE_PARAMETERS macros only for kinds zp does not + cover). Both expand to the engine's own ZPP macros. Never introduce per-call argument boxing in a registration path — raw handler pointers only. Use the zero-cost wrappers in `zv.h` — borrowed `zv::Ref` views vs owned move-only diff --git a/turbo-ext/README.md b/turbo-ext/README.md index be228aff38a..93b54bfe735 100644 --- a/turbo-ext/README.md +++ b/turbo-ext/README.md @@ -379,7 +379,12 @@ interleaved A/B benchmark), so readability costs nothing. Classes register through the fluent builder in `src/reg.h`, which emits the raw zend structures with raw handler pointers — no per-call trampoline or argument boxing; each method's name, flags, signature and parameter-parsing glue live -together in one declaration. +together in one declaration. A method that only parses its parameters and +delegates them is declared by its handle member and parameter kinds +(`cls.method<&UnionType::accepts, zp::Obj, zp::Bool>(...)`) and gets a +generated handler; other glue parses with `zp::parse<...>()`. Both expand to +the engine's own `ZEND_PARSE_PARAMETERS` macros, so the handlers compile to +what the hand-written glue did. Raw zend form remains where an abstraction would not be provably free — always with a comment saying so. diff --git a/turbo-ext/src/ArenaCache.cpp b/turbo-ext/src/ArenaCache.cpp index 3a53b6a93db..1507f5e79d1 100644 --- a/turbo-ext/src/ArenaCache.cpp +++ b/turbo-ext/src/ArenaCache.cpp @@ -1157,17 +1157,13 @@ void pt_register_arena_cache() cls.method("create", reg::PublicStatic, 1, { reg::stringArg("runId") }, [](INTERNAL_FUNCTION_PARAMETERS) { zend_string *runId; - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STR(runId) - ZEND_PARSE_PARAMETERS_END(); + if (!zp::parse(execute_data, runId)) RETURN_THROWS(); phpstanturbo::ArenaCache::create(runId, return_value); }); cls.method("attach", reg::PublicStatic, 1, { reg::stringArg("name") }, [](INTERNAL_FUNCTION_PARAMETERS) { zend_string *name; - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STR(name) - ZEND_PARSE_PARAMETERS_END(); + if (!zp::parse(execute_data, name)) RETURN_THROWS(); RETURN_BOOL(phpstanturbo::ArenaCache::attach(name)); }); @@ -1183,55 +1179,39 @@ void pt_register_arena_cache() cls.method("hasRecord", reg::PublicStatic, 1, { reg::stringArg("key") }, [](INTERNAL_FUNCTION_PARAMETERS) { zend_string *key; - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STR(key) - ZEND_PARSE_PARAMETERS_END(); + if (!zp::parse(execute_data, key)) RETURN_THROWS(); RETURN_BOOL(phpstanturbo::ArenaCache::hasRecord(key)); }); cls.method("lookup", reg::PublicStatic, 1, { reg::stringArg("key") }, [](INTERNAL_FUNCTION_PARAMETERS) { zend_string *key; - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STR(key) - ZEND_PARSE_PARAMETERS_END(); + if (!zp::parse(execute_data, key)) RETURN_THROWS(); phpstanturbo::ArenaCache::lookup(key, return_value); }); cls.method("publish", reg::PublicStatic, 2, { reg::stringArg("key"), reg::any("value") }, [](INTERNAL_FUNCTION_PARAMETERS) { zend_string *key; zval *value; - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_STR(key) - Z_PARAM_ZVAL(value) - ZEND_PARSE_PARAMETERS_END(); + if (!zp::parse(execute_data, key, value)) RETURN_THROWS(); phpstanturbo::ArenaCache::publish(key, value); }); cls.method("lookupHash", reg::PublicStatic, 2, { reg::stringArg("recordKey"), reg::stringArg("entryKey") }, [](INTERNAL_FUNCTION_PARAMETERS) { - zend_string *recordKey; - zend_string *entryKey; - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_STR(recordKey) - Z_PARAM_STR(entryKey) - ZEND_PARSE_PARAMETERS_END(); + zend_string *recordKey, *entryKey; + if (!zp::parse(execute_data, recordKey, entryKey)) RETURN_THROWS(); phpstanturbo::ArenaCache::lookupHash(recordKey, entryKey, return_value); }); cls.method("lookupHashAll", reg::PublicStatic, 1, { reg::stringArg("recordKey") }, [](INTERNAL_FUNCTION_PARAMETERS) { zend_string *recordKey; - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STR(recordKey) - ZEND_PARSE_PARAMETERS_END(); + if (!zp::parse(execute_data, recordKey)) RETURN_THROWS(); phpstanturbo::ArenaCache::lookupHashAll(recordKey, return_value); }); cls.method("publishHash", reg::PublicStatic, 2, { reg::stringArg("recordKey"), reg::arrayArg("entries") }, [](INTERNAL_FUNCTION_PARAMETERS) { zend_string *recordKey; HashTable *entries; - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_STR(recordKey) - Z_PARAM_ARRAY_HT(entries) - ZEND_PARSE_PARAMETERS_END(); + if (!zp::parse(execute_data, recordKey, entries)) RETURN_THROWS(); phpstanturbo::ArenaCache::publishHash(recordKey, entries); }); diff --git a/turbo-ext/src/CombinationsHelper.cpp b/turbo-ext/src/CombinationsHelper.cpp index 727590cdc4a..32119545fb9 100644 --- a/turbo-ext/src/CombinationsHelper.cpp +++ b/turbo-ext/src/CombinationsHelper.cpp @@ -146,9 +146,7 @@ void pt_register_combinations_helper() cls.method("combinations", reg::PublicStatic, 1, { reg::arrayArg("arrays") }, [](INTERNAL_FUNCTION_PARAMETERS) { HashTable *arrays; - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ARRAY_HT(arrays) - ZEND_PARSE_PARAMETERS_END(); + if (!zp::parse(execute_data, arrays)) RETURN_THROWS(); zval arraysZv; ZVAL_ARR(&arraysZv, arrays); zv::Val result = CombinationsHelper::combinations(zv::ArrRef(&arraysZv)); diff --git a/turbo-ext/src/ExpressionResultStorage.cpp b/turbo-ext/src/ExpressionResultStorage.cpp index 6d3f4c09e91..da772661f52 100644 --- a/turbo-ext/src/ExpressionResultStorage.cpp +++ b/turbo-ext/src/ExpressionResultStorage.cpp @@ -114,26 +114,19 @@ void pt_register_expression_result_storage() cls.method("mergeResults", reg::Public, 1, { reg::any("other") }, [](INTERNAL_FUNCTION_PARAMETERS) { zval *other; - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_OBJECT(other) - ZEND_PARSE_PARAMETERS_END(); + if (!zp::parse(execute_data, other)) RETURN_THROWS(); ExpressionResultStorage(ZEND_THIS).mergeResults(other); }); cls.method("storeExpressionResult", reg::Public, 2, { reg::any("expr"), reg::any("expressionResult") }, [](INTERNAL_FUNCTION_PARAMETERS) { zval *expr, *expressionResult; - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_OBJECT(expr) - Z_PARAM_OBJECT(expressionResult) - ZEND_PARSE_PARAMETERS_END(); + if (!zp::parse(execute_data, expr, expressionResult)) RETURN_THROWS(); ExpressionResultStorage(ZEND_THIS).storeExpressionResult(expr, expressionResult); }); cls.method("findExpressionResult", reg::Public, 1, { reg::any("expr") }, [](INTERNAL_FUNCTION_PARAMETERS) { zval *expr; - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_OBJECT(expr) - ZEND_PARSE_PARAMETERS_END(); + if (!zp::parse(execute_data, expr)) RETURN_THROWS(); ExpressionResultStorage(ZEND_THIS).findExpressionResult(expr).intoReturnValue(return_value); }); diff --git a/turbo-ext/src/ExpressionTypeHolder.cpp b/turbo-ext/src/ExpressionTypeHolder.cpp index 60fec05e074..fbdac4654af 100644 --- a/turbo-ext/src/ExpressionTypeHolder.cpp +++ b/turbo-ext/src/ExpressionTypeHolder.cpp @@ -100,19 +100,13 @@ void pt_register_expression_type_holder() cls.method("createYes", reg::PublicStatic, 2, { reg::any("expr"), reg::any("type") }, [](INTERNAL_FUNCTION_PARAMETERS) { zval *expr, *type; - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_OBJECT(expr) - Z_PARAM_OBJECT(type) - ZEND_PARSE_PARAMETERS_END(); + if (!zp::parse(execute_data, expr, type)) RETURN_THROWS(); ExpressionTypeHolder::createYes(expr, type).intoReturnValue(return_value); }); cls.method("createMaybe", reg::PublicStatic, 2, { reg::any("expr"), reg::any("type") }, [](INTERNAL_FUNCTION_PARAMETERS) { zval *expr, *type; - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_OBJECT(expr) - Z_PARAM_OBJECT(type) - ZEND_PARSE_PARAMETERS_END(); + if (!zp::parse(execute_data, expr, type)) RETURN_THROWS(); ExpressionTypeHolder::createMaybe(expr, type).intoReturnValue(return_value); }); diff --git a/turbo-ext/src/NodeScanner.cpp b/turbo-ext/src/NodeScanner.cpp index 9813e7b15df..d864fcafb20 100644 --- a/turbo-ext/src/NodeScanner.cpp +++ b/turbo-ext/src/NodeScanner.cpp @@ -56,9 +56,7 @@ void pt_register_node_scanner() cls.method("nodeIsOrContainsYield", reg::PublicStatic, 1, { reg::objectArg("node") }, [](INTERNAL_FUNCTION_PARAMETERS) { zval *node; - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_OBJECT(node) - ZEND_PARSE_PARAMETERS_END(); + if (!zp::parse(execute_data, node)) RETURN_THROWS(); bool failed = false; bool result = NodeScanner::nodeIsOrContainsYield(zv::ObjRef(node), failed); if (UNEXPECTED(failed)) RETURN_THROWS(); diff --git a/turbo-ext/src/NodeTraverser.cpp b/turbo-ext/src/NodeTraverser.cpp index ca3e98b2876..4289901d5ba 100644 --- a/turbo-ext/src/NodeTraverser.cpp +++ b/turbo-ext/src/NodeTraverser.cpp @@ -828,25 +828,19 @@ void pt_register_node_traverser() cls.method("addVisitor", reg::Public, 1, { reg::obj("visitor", NODE_VISITOR_CLASS) }, [](INTERNAL_FUNCTION_PARAMETERS) { zval *visitor; - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_OBJECT(visitor) - ZEND_PARSE_PARAMETERS_END(); + if (!zp::parse(execute_data, visitor)) RETURN_THROWS(); NodeTraverser(Z_OBJ_P(ZEND_THIS)).addVisitor(zv::Ref(visitor)); }, &voidReturn); cls.method("removeVisitor", reg::Public, 1, { reg::obj("visitor", NODE_VISITOR_CLASS) }, [](INTERNAL_FUNCTION_PARAMETERS) { zval *visitor; - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_OBJECT(visitor) - ZEND_PARSE_PARAMETERS_END(); + if (!zp::parse(execute_data, visitor)) RETURN_THROWS(); NodeTraverser(Z_OBJ_P(ZEND_THIS)).removeVisitor(zv::Ref(visitor)); }, &voidReturn); cls.method("traverse", reg::Public, 1, { reg::arrayArg("nodes") }, [](INTERNAL_FUNCTION_PARAMETERS) { HashTable *nodes; - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ARRAY_HT(nodes) - ZEND_PARSE_PARAMETERS_END(); + if (!zp::parse(execute_data, nodes)) RETURN_THROWS(); NodeTraverser self(Z_OBJ_P(ZEND_THIS)); zv::Val result = self.traverse(nodes); if (UNEXPECTED(result.isUndef())) RETURN_THROWS(); diff --git a/turbo-ext/src/PhpFileCleaner.cpp b/turbo-ext/src/PhpFileCleaner.cpp index 1b95d83c668..b3ce2883fa7 100644 --- a/turbo-ext/src/PhpFileCleaner.cpp +++ b/turbo-ext/src/PhpFileCleaner.cpp @@ -32,10 +32,7 @@ void pt_register_php_file_cleaner() cls.method("clean", reg::Public, 2, { reg::stringArg("contents"), reg::longArg("maxMatches") }, [](INTERNAL_FUNCTION_PARAMETERS) { zend_string *contents; zend_long maxMatches; - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_STR(contents) - Z_PARAM_LONG(maxMatches) - ZEND_PARSE_PARAMETERS_END(); + if (!zp::parse(execute_data, contents, maxMatches)) RETURN_THROWS(); phpstanturbo::PhpFileCleaner cleaner(ZSTR_VAL(contents), ZSTR_LEN(contents)); std::string cleaned; diff --git a/turbo-ext/src/ScopeOps.cpp b/turbo-ext/src/ScopeOps.cpp index d104adc7d8e..06b8de9647d 100644 --- a/turbo-ext/src/ScopeOps.cpp +++ b/turbo-ext/src/ScopeOps.cpp @@ -1833,12 +1833,7 @@ void pt_register_scope_ops() cls.method("mergeVariableHolders", reg::PublicStatic, 2, { reg::arrayArg("ourVariableTypeHolders"), reg::arrayArg("theirVariableTypeHolders"), reg::any("differingKeys", true) }, [](INTERNAL_FUNCTION_PARAMETERS) { HashTable *ours, *theirs; zval *differing_zv = NULL; - ZEND_PARSE_PARAMETERS_START(2, 3) - Z_PARAM_ARRAY_HT(ours) - Z_PARAM_ARRAY_HT(theirs) - Z_PARAM_OPTIONAL - Z_PARAM_ZVAL(differing_zv) - ZEND_PARSE_PARAMETERS_END(); + if (!zp::parse>(execute_data, ours, theirs, differing_zv)) RETURN_THROWS(); HashTable *differing = NULL; if (differing_zv != NULL && Z_ISREF_P(differing_zv)) { /* the twin declares `array &$differingKeys = []`; vivify like PHP @@ -1857,13 +1852,7 @@ void pt_register_scope_ops() cls.method("finishMerge", reg::PublicStatic, 5, { reg::arrayArg("mergedExpressionTypes"), reg::arrayArg("ourExpressionTypes"), reg::arrayArg("theirExpressionTypes"), reg::arrayArg("ourNativeExpressionTypes"), reg::arrayArg("theirNativeExpressionTypes") }, [](INTERNAL_FUNCTION_PARAMETERS) { HashTable *merged, *ours_expr, *theirs_expr, *ours_native, *theirs_native; - ZEND_PARSE_PARAMETERS_START(5, 5) - Z_PARAM_ARRAY_HT(merged) - Z_PARAM_ARRAY_HT(ours_expr) - Z_PARAM_ARRAY_HT(theirs_expr) - Z_PARAM_ARRAY_HT(ours_native) - Z_PARAM_ARRAY_HT(theirs_native) - ZEND_PARSE_PARAMETERS_END(); + if (!zp::parse(execute_data, merged, ours_expr, theirs_expr, ours_native, theirs_native)) RETURN_THROWS(); zv::Val result = ScopeOps::finishMerge(zv::TableRef(merged), zv::TableRef(ours_expr), zv::TableRef(theirs_expr), zv::TableRef(ours_native), zv::TableRef(theirs_native)); if (UNEXPECTED(result.isUndef())) RETURN_THROWS(); result.intoReturnValue(return_value); @@ -1871,10 +1860,7 @@ void pt_register_scope_ops() cls.method("intersectConditionalExpressions", reg::PublicStatic, 2, { reg::arrayArg("ourConditionalExpressions"), reg::arrayArg("theirConditionalExpressions") }, [](INTERNAL_FUNCTION_PARAMETERS) { HashTable *ours, *theirs; - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_ARRAY_HT(ours) - Z_PARAM_ARRAY_HT(theirs) - ZEND_PARSE_PARAMETERS_END(); + if (!zp::parse(execute_data, ours, theirs)) RETURN_THROWS(); ScopeOps::intersectConditionalExpressions(zv::TableRef(ours), zv::TableRef(theirs)).intoReturnValue(return_value); }); @@ -1929,9 +1915,7 @@ void pt_register_scope_ops() cls.method("getIntertwinedRefRootVariableName", reg::PublicStatic, 1, { reg::objectArg("expr") }, [](INTERNAL_FUNCTION_PARAMETERS) { zval *expr; - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_OBJECT(expr) - ZEND_PARSE_PARAMETERS_END(); + if (!zp::parse(execute_data, expr)) RETURN_THROWS(); zv::Val result = ScopeOps::getIntertwinedRefRootVariableName(Z_OBJ_P(expr)); if (UNEXPECTED(result.isUndef())) RETURN_THROWS(); result.intoReturnValue(return_value); @@ -1939,10 +1923,7 @@ void pt_register_scope_ops() cls.method("matchConditionalExpressions", reg::PublicStatic, 2, { reg::arrayArg("conditionalExpressions"), reg::arrayArg("specifiedExpressions") }, [](INTERNAL_FUNCTION_PARAMETERS) { HashTable *conditional, *specified_input; - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_ARRAY_HT(conditional) - Z_PARAM_ARRAY_HT(specified_input) - ZEND_PARSE_PARAMETERS_END(); + if (!zp::parse(execute_data, conditional, specified_input)) RETURN_THROWS(); zv::Val result = ScopeOps::matchConditionalExpressions(zv::TableRef(conditional), zv::TableRef(specified_input)); if (UNEXPECTED(result.isUndef())) RETURN_THROWS(); result.intoReturnValue(return_value); @@ -1950,13 +1931,7 @@ void pt_register_scope_ops() cls.method("createConditionalExpressions", reg::PublicStatic, 5, { reg::arrayArg("conditionalExpressions"), reg::arrayArg("ourExpressionTypes"), reg::arrayArg("theirExpressionTypes"), reg::arrayArg("mergedExpressionTypes"), reg::arrayArg("differingKeys") }, [](INTERNAL_FUNCTION_PARAMETERS) { HashTable *conditional, *ours, *theirs, *merged, *differing_keys; - ZEND_PARSE_PARAMETERS_START(5, 5) - Z_PARAM_ARRAY_HT(conditional) - Z_PARAM_ARRAY_HT(ours) - Z_PARAM_ARRAY_HT(theirs) - Z_PARAM_ARRAY_HT(merged) - Z_PARAM_ARRAY_HT(differing_keys) - ZEND_PARSE_PARAMETERS_END(); + if (!zp::parse(execute_data, conditional, ours, theirs, merged, differing_keys)) RETURN_THROWS(); zv::Val result = ScopeOps::createConditionalExpressions(zv::TableRef(conditional), zv::TableRef(ours), zv::TableRef(theirs), zv::TableRef(merged), zv::TableRef(differing_keys)); if (UNEXPECTED(result.isUndef())) RETURN_THROWS(); result.intoReturnValue(return_value); @@ -1964,10 +1939,7 @@ void pt_register_scope_ops() cls.method("nodeKey", reg::PublicStatic, 2, { reg::objectArg("node"), reg::objectArg("exprPrinter") }, [](INTERNAL_FUNCTION_PARAMETERS) { zval *node, *expr_printer; - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_OBJECT(node) - Z_PARAM_OBJECT(expr_printer) - ZEND_PARSE_PARAMETERS_END(); + if (!zp::parse(execute_data, node, expr_printer)) RETURN_THROWS(); zv::Val result = ScopeOps::nodeKey(Z_OBJ_P(node), expr_printer); if (UNEXPECTED(result.isUndef())) RETURN_THROWS(); result.intoReturnValue(return_value); @@ -1975,11 +1947,7 @@ void pt_register_scope_ops() cls.method("getTypeFromCache", reg::PublicStatic, 3, { reg::objectArg("scope"), reg::objectArg("node"), reg::any("key", true) }, [](INTERNAL_FUNCTION_PARAMETERS) { zval *scope, *node, *key_out; - ZEND_PARSE_PARAMETERS_START(3, 3) - Z_PARAM_OBJECT(scope) - Z_PARAM_OBJECT(node) - Z_PARAM_ZVAL(key_out) - ZEND_PARSE_PARAMETERS_END(); + if (!zp::parse(execute_data, scope, node, key_out)) RETURN_THROWS(); zend_string *key = NULL; zv::Val result = ScopeOps::getTypeFromCache(scope, Z_OBJ_P(node), &key); if (UNEXPECTED(result.isUndef())) RETURN_THROWS(); @@ -1997,10 +1965,7 @@ void pt_register_scope_ops() cls.method("hasVariableType", reg::PublicStatic, 2, { reg::objectArg("scope"), reg::stringArg("variableName") }, [](INTERNAL_FUNCTION_PARAMETERS) { zval *scope; zend_string *variable_name; - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_OBJECT(scope) - Z_PARAM_STR(variable_name) - ZEND_PARSE_PARAMETERS_END(); + if (!zp::parse(execute_data, scope, variable_name)) RETURN_THROWS(); zv::Val result = ScopeOps::hasVariableType(scope, variable_name); if (UNEXPECTED(result.isUndef())) RETURN_THROWS(); result.intoReturnValue(return_value); @@ -2031,12 +1996,7 @@ void pt_register_scope_ops() zval *expr_printer; zend_string *invalidate_str; HashTable *expression_types, *native_expression_types; - ZEND_PARSE_PARAMETERS_START(4, 4) - Z_PARAM_OBJECT(expr_printer) - Z_PARAM_STR(invalidate_str) - Z_PARAM_ARRAY_HT(expression_types) - Z_PARAM_ARRAY_HT(native_expression_types) - ZEND_PARSE_PARAMETERS_END(); + if (!zp::parse(execute_data, expr_printer, invalidate_str, expression_types, native_expression_types)) RETURN_THROWS(); pt_init_strs(); zv::Val result = ScopeOps::invalidateMethodsOnExpression(expr_printer, invalidate_str, zv::TableRef(expression_types), zv::TableRef(native_expression_types)); if (UNEXPECTED(result.isUndef())) RETURN_THROWS(); @@ -2046,11 +2006,7 @@ void pt_register_scope_ops() cls.method("expressionTypeByKey", reg::PublicStatic, 3, { reg::objectArg("scope"), reg::objectArg("node"), reg::stringArg("exprString") }, [](INTERNAL_FUNCTION_PARAMETERS) { zval *scope, *node; zend_string *expr_string; - ZEND_PARSE_PARAMETERS_START(3, 3) - Z_PARAM_OBJECT(scope) - Z_PARAM_OBJECT(node) - Z_PARAM_STR(expr_string) - ZEND_PARSE_PARAMETERS_END(); + if (!zp::parse(execute_data, scope, node, expr_string)) RETURN_THROWS(); zv::Val result = ScopeOps::expressionTypeByKey(scope, Z_OBJ_P(node), expr_string); if (UNEXPECTED(result.isUndef())) RETURN_THROWS(); result.intoReturnValue(return_value); @@ -2058,11 +2014,7 @@ void pt_register_scope_ops() cls.method("hasExpressionType", reg::PublicStatic, 3, { reg::objectArg("scope"), reg::objectArg("node"), reg::objectArg("exprPrinter") }, [](INTERNAL_FUNCTION_PARAMETERS) { zval *scope, *node, *expr_printer; - ZEND_PARSE_PARAMETERS_START(3, 3) - Z_PARAM_OBJECT(scope) - Z_PARAM_OBJECT(node) - Z_PARAM_OBJECT(expr_printer) - ZEND_PARSE_PARAMETERS_END(); + if (!zp::parse(execute_data, scope, node, expr_printer)) RETURN_THROWS(); zv::Val result = ScopeOps::hasExpressionType(scope, Z_OBJ_P(node), expr_printer); if (UNEXPECTED(result.isUndef())) RETURN_THROWS(); result.intoReturnValue(return_value); diff --git a/turbo-ext/src/SymbolFinderInFiles.cpp b/turbo-ext/src/SymbolFinderInFiles.cpp index 77edb11a493..3b49789b6e3 100644 --- a/turbo-ext/src/SymbolFinderInFiles.cpp +++ b/turbo-ext/src/SymbolFinderInFiles.cpp @@ -189,19 +189,14 @@ void pt_register_symbol_finder_in_files() * this constructor while compiling the container (rule 6) */ cls.method("__construct", reg::Public, 1, { reg::obj("cleaner", CLEANER_CLASS) }, [](INTERNAL_FUNCTION_PARAMETERS) { zval *cleaner; - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_OBJECT(cleaner) - ZEND_PARSE_PARAMETERS_END(); + if (!zp::parse(execute_data, cleaner)) RETURN_THROWS(); (void) cleaner; }); cls.method("findSymbols", reg::Public, 2, { reg::arrayArg("files"), reg::boolArg("supportsEnums") }, [](INTERNAL_FUNCTION_PARAMETERS) { HashTable *files; bool supportsEnums; - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_ARRAY_HT(files) - Z_PARAM_BOOL(supportsEnums) - ZEND_PARSE_PARAMETERS_END(); + if (!zp::parse(execute_data, files, supportsEnums)) RETURN_THROWS(); phpstanturbo::SymbolFinderInFiles finder; finder.findSymbols(files, supportsEnums).intoReturnValue(return_value); diff --git a/turbo-ext/src/TrinaryLogic.cpp b/turbo-ext/src/TrinaryLogic.cpp index caf35d363b5..6e2143ffad1 100644 --- a/turbo-ext/src/TrinaryLogic.cpp +++ b/turbo-ext/src/TrinaryLogic.cpp @@ -320,9 +320,7 @@ void pt_register_trinary_logic() cls.method("__construct", reg::Private, 1, { reg::longArg("value") }, [](INTERNAL_FUNCTION_PARAMETERS) { zend_long value; - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_LONG(value) - ZEND_PARSE_PARAMETERS_END(); + if (!zp::parse(execute_data, value)) RETURN_THROWS(); ZVAL_LONG(OBJ_PROP_NUM(Z_OBJ_P(ZEND_THIS), PT_TRI_PROP_VALUE), value); }); @@ -343,9 +341,7 @@ void pt_register_trinary_logic() cls.method("createFromBoolean", reg::PublicStatic, 1, { reg::boolArg("value") }, [](INTERNAL_FUNCTION_PARAMETERS) { bool value; - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_BOOL(value) - ZEND_PARSE_PARAMETERS_END(); + if (!zp::parse(execute_data, value)) RETURN_THROWS(); TrinaryLogic::createFromBoolean(value).intoReturnValue(return_value); }); diff --git a/turbo-ext/src/TypeCombinatorCache.cpp b/turbo-ext/src/TypeCombinatorCache.cpp index 0a5f93b8ff0..0aca82eee8c 100644 --- a/turbo-ext/src/TypeCombinatorCache.cpp +++ b/turbo-ext/src/TypeCombinatorCache.cpp @@ -801,12 +801,8 @@ void pt_register_type_combinator_cache() }); cls.method("remove", reg::PublicStatic, 2, { reg::obj("fromType", TYPE_CLASS), reg::obj("typeToRemove", TYPE_CLASS) }, [](INTERNAL_FUNCTION_PARAMETERS) { - zval *fromType; - zval *typeToRemove; - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_OBJECT(fromType) - Z_PARAM_OBJECT(typeToRemove) - ZEND_PARSE_PARAMETERS_END(); + zval *fromType, *typeToRemove; + if (!zp::parse(execute_data, fromType, typeToRemove)) RETURN_THROWS(); zval args[2]; ZVAL_COPY_VALUE(&args[0], fromType); ZVAL_COPY_VALUE(&args[1], typeToRemove); diff --git a/turbo-ext/src/main.cpp b/turbo-ext/src/main.cpp index 5efd3f1c988..0c4d731e17b 100644 --- a/turbo-ext/src/main.cpp +++ b/turbo-ext/src/main.cpp @@ -41,9 +41,7 @@ static void ZEND_FASTCALL runtimeConfigure(INTERNAL_FUNCTION_PARAMETERS) { HashTable *map; - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ARRAY_HT(map) - ZEND_PARSE_PARAMETERS_END(); + if (!zp::parse(execute_data, map)) RETURN_THROWS(); zend_string *key; zval *value; @@ -64,11 +62,7 @@ static void ZEND_FASTCALL runtimeActivateShadowing(INTERNAL_FUNCTION_PARAMETERS) { HashTable *twinFiles; zend_string *prefix = NULL; - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_ARRAY_HT(twinFiles) - Z_PARAM_OPTIONAL - Z_PARAM_STR_OR_NULL(prefix) - ZEND_PARSE_PARAMETERS_END(); + if (!zp::parse>(execute_data, twinFiles, prefix)) RETURN_THROWS(); if (!pt_shadow_activate(twinFiles, prefix)) RETURN_THROWS(); } @@ -98,9 +92,7 @@ static void ZEND_FASTCALL runtimeClassRefs(INTERNAL_FUNCTION_PARAMETERS) static void ZEND_FASTCALL runtimeEnablePharForkGuard(INTERNAL_FUNCTION_PARAMETERS) { zend_string *path; - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STR(path) - ZEND_PARSE_PARAMETERS_END(); + if (!zp::parse(execute_data, path)) RETURN_THROWS(); pt_phar_fork_guard_register(path); } @@ -113,9 +105,7 @@ static void ZEND_FASTCALL runtimeEnablePharForkGuard(INTERNAL_FUNCTION_PARAMETER static void ZEND_FASTCALL runtimeTrustTypesUnder(INTERNAL_FUNCTION_PARAMETERS) { zend_string *prefix; - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STR(prefix) - ZEND_PARSE_PARAMETERS_END(); + if (!zp::parse(execute_data, prefix)) RETURN_THROWS(); RETURN_BOOL(pt_trusted_types_set_prefix(prefix)); } diff --git a/turbo-ext/src/parser/ParserRunner.cpp b/turbo-ext/src/parser/ParserRunner.cpp index 13c47cdfe17..d3c55420d1d 100644 --- a/turbo-ext/src/parser/ParserRunner.cpp +++ b/turbo-ext/src/parser/ParserRunner.cpp @@ -1234,11 +1234,7 @@ void pt_register_parser_runner(void) cls.method("parse", reg::PublicStatic, 3, { reg::objectArg("parser"), reg::stringArg("sourceCode"), reg::objectArg("errorHandler") }, [](INTERNAL_FUNCTION_PARAMETERS) { zval *parserObj, *code, *errorHandler; - ZEND_PARSE_PARAMETERS_START(3, 3) - Z_PARAM_OBJECT(parserObj) - Z_PARAM_ZVAL(code) - Z_PARAM_OBJECT(errorHandler) - ZEND_PARSE_PARAMETERS_END(); + if (!zp::parse(execute_data, parserObj, code, errorHandler)) RETURN_THROWS(); if (Z_TYPE_P(code) == IS_STRING && ParserEngine::prepareTables(parserObj)) { ParserEngine engine(parserObj, errorHandler); diff --git a/turbo-ext/src/reg.h b/turbo-ext/src/reg.h index 8c0c5732845..8e44c2f6e60 100644 --- a/turbo-ext/src/reg.h +++ b/turbo-ext/src/reg.h @@ -4,10 +4,11 @@ * zend_internal_arg_info / zend_function_entry structures the PHP_METHOD + * ZEND_BEGIN_ARG_INFO_EX + PHP_ME macro triple produced, and hands the * engine the raw handler pointers directly — no trampoline, no Php::Value - * boxing, byte-identical dispatch. Handlers are plain functions or + * boxing, byte-identical dispatch. Handlers are plain functions, * non-capturing lambdas with the (INTERNAL_FUNCTION_PARAMETERS) signature, - * so each method is declared exactly once: name, flags, signature and body - * together at the registration site. + * or generated from a handle member and its parameter kinds + * (Class::method()), so each method is declared exactly once: name, + * flags, signature and body together at the registration site. * * Two registration paths share the builder: * - register_() registers an internal class at module startup (extension-only @@ -25,11 +26,196 @@ #define PHPSTANTURBO_REG_H #include "support.h" +#include "zv.h" #include #include +#include +#include +#include #include +/* {{{ handler glue shared by every registration site */ + +/* a zv::Val result into return_value; RETURN_THROWS on UNDEF (pending exception) */ +#define PT_RETURN_VAL(expr) \ + do { \ + zv::Val pt_result__ = (expr); \ + if (UNEXPECTED(pt_result__.isUndef())) { \ + RETURN_THROWS(); \ + } \ + pt_result__.intoReturnValue(return_value); \ + return; \ + } while (0) + +/* }}} */ + +/* {{{ zp: typed parameter parsing + * + * zp::parse(execute_data, dests...) is the ZEND_PARSE_PARAMETERS_START + * ... END block of a glue function written once: each kind K names the + * Z_PARAM_* macro its slot expands to, zp::Opt puts Z_PARAM_OPTIONAL in + * front of it (the destination keeps its initializer when the argument is + * not passed), and the block is the engine's own macros — the code is the + * hand-written block's. false = pending exception. + */ +namespace zp { + +struct Obj { using type = zval *; }; /* Z_PARAM_OBJECT */ +struct ObjOrNull { using type = zval *; }; /* Z_PARAM_OBJECT_OR_NULL */ +struct Bool { using type = bool; }; /* Z_PARAM_BOOL */ +struct Str { using type = zend_string *; }; /* Z_PARAM_STR */ +struct StrOrNull { using type = zend_string *; }; /* Z_PARAM_STR_OR_NULL */ +struct Arr { using type = zval *; }; /* Z_PARAM_ARRAY */ +struct ArrOrNull { using type = zval *; }; /* Z_PARAM_ARRAY_OR_NULL */ +struct Ht { using type = HashTable *; }; /* Z_PARAM_ARRAY_HT */ +struct HtOrNull { using type = HashTable *; }; /* Z_PARAM_ARRAY_HT_OR_NULL */ +struct Zval { using type = zval *; }; /* Z_PARAM_ZVAL */ +struct Long { using type = zend_long; }; /* Z_PARAM_LONG */ +struct Double { using type = double; }; /* Z_PARAM_DOUBLE */ + +/* a parameter after Z_PARAM_OPTIONAL */ +template +struct Opt : K +{ +}; + +namespace detail { + +template +struct Base +{ + using type = K; + static constexpr bool optional = false; +}; + +template +struct Base> +{ + using type = K; + static constexpr bool optional = true; +}; + +template +constexpr bool is = std::is_same_v::type, Kind>; + +} // namespace detail + +template +constexpr uint32_t required() +{ + return (0u + ... + (detail::Base::optional ? 0u : 1u)); +} + +/* one Z_PARAM_* slot of a ZEND_PARSE_PARAMETERS block, chosen by the kind (the + * blocks' required() counts are parenthesized: the template arguments' + * commas would split the macro arguments) */ +#define PT_ZP_SLOT(K, dest) \ + if constexpr (detail::Base::optional) { \ + Z_PARAM_OPTIONAL \ + } \ + if constexpr (detail::is) { \ + Z_PARAM_OBJECT(dest) \ + } else if constexpr (detail::is) { \ + Z_PARAM_OBJECT_OR_NULL(dest) \ + } else if constexpr (detail::is) { \ + Z_PARAM_BOOL(dest) \ + } else if constexpr (detail::is) { \ + Z_PARAM_STR(dest) \ + } else if constexpr (detail::is) { \ + Z_PARAM_STR_OR_NULL(dest) \ + } else if constexpr (detail::is) { \ + Z_PARAM_ARRAY(dest) \ + } else if constexpr (detail::is) { \ + Z_PARAM_ARRAY_OR_NULL(dest) \ + } else if constexpr (detail::is) { \ + Z_PARAM_ARRAY_HT(dest) \ + } else if constexpr (detail::is) { \ + Z_PARAM_ARRAY_HT_OR_NULL(dest) \ + } else if constexpr (detail::is) { \ + Z_PARAM_ZVAL(dest) \ + } else if constexpr (detail::is) { \ + Z_PARAM_LONG(dest) \ + } else { \ + static_assert(detail::is, "not a zp kind"); \ + Z_PARAM_DOUBLE(dest) \ + } + +template +zend_always_inline bool parse(zend_execute_data *execute_data, typename K1::type &d1) +{ + ZEND_PARSE_PARAMETERS_START((required()), 1) + PT_ZP_SLOT(K1, d1) + ZEND_PARSE_PARAMETERS_END_EX(return false); + return true; +} + +template +zend_always_inline bool parse(zend_execute_data *execute_data, typename K1::type &d1, typename K2::type &d2) +{ + ZEND_PARSE_PARAMETERS_START((required()), 2) + PT_ZP_SLOT(K1, d1) + PT_ZP_SLOT(K2, d2) + ZEND_PARSE_PARAMETERS_END_EX(return false); + return true; +} + +template +zend_always_inline bool parse(zend_execute_data *execute_data, typename K1::type &d1, typename K2::type &d2, typename K3::type &d3) +{ + ZEND_PARSE_PARAMETERS_START((required()), 3) + PT_ZP_SLOT(K1, d1) + PT_ZP_SLOT(K2, d2) + PT_ZP_SLOT(K3, d3) + ZEND_PARSE_PARAMETERS_END_EX(return false); + return true; +} + +template +zend_always_inline bool parse(zend_execute_data *execute_data, typename K1::type &d1, typename K2::type &d2, typename K3::type &d3, typename K4::type &d4) +{ + ZEND_PARSE_PARAMETERS_START((required()), 4) + PT_ZP_SLOT(K1, d1) + PT_ZP_SLOT(K2, d2) + PT_ZP_SLOT(K3, d3) + PT_ZP_SLOT(K4, d4) + ZEND_PARSE_PARAMETERS_END_EX(return false); + return true; +} + +template +zend_always_inline bool parse(zend_execute_data *execute_data, typename K1::type &d1, typename K2::type &d2, typename K3::type &d3, typename K4::type &d4, typename K5::type &d5) +{ + ZEND_PARSE_PARAMETERS_START((required()), 5) + PT_ZP_SLOT(K1, d1) + PT_ZP_SLOT(K2, d2) + PT_ZP_SLOT(K3, d3) + PT_ZP_SLOT(K4, d4) + PT_ZP_SLOT(K5, d5) + ZEND_PARSE_PARAMETERS_END_EX(return false); + return true; +} + +template +zend_always_inline bool parse(zend_execute_data *execute_data, typename K1::type &d1, typename K2::type &d2, typename K3::type &d3, typename K4::type &d4, typename K5::type &d5, typename K6::type &d6) +{ + ZEND_PARSE_PARAMETERS_START((required()), 6) + PT_ZP_SLOT(K1, d1) + PT_ZP_SLOT(K2, d2) + PT_ZP_SLOT(K3, d3) + PT_ZP_SLOT(K4, d4) + PT_ZP_SLOT(K5, d5) + PT_ZP_SLOT(K6, d6) + ZEND_PARSE_PARAMETERS_END_EX(return false); + return true; +} + +#undef PT_ZP_SLOT + +} // namespace zp + +/* }}} */ + namespace reg { constexpr uint32_t Public = ZEND_ACC_PUBLIC; @@ -205,6 +391,114 @@ void pt_shadow_plan_add(reg::ShadowPlan &&plan); namespace reg { +namespace detail { + +/* a bound method's return type and the handle class it is a member of + * (void for a static member or a free function) */ +template +struct BoundSignature; + +template +struct BoundSignature +{ + using Return = R; + using Class = C; + static constexpr size_t arity = sizeof...(P); +}; + +template +struct BoundSignature : BoundSignature +{ +}; + +template +struct BoundSignature +{ + using Return = R; + using Class = void; + static constexpr size_t arity = sizeof...(P); +}; + +/* + * The handler Class::method() registers — what a glue lambda that + * only parses its parameters and delegates spells out by hand: the + * parameters parsed as the zp kinds K, then M called with them on the $this + * handle (directly when M is static or free). A zv::Val result is the + * return value (UNDEF = pending exception); a void one leaves null; a bool + * one with a trailing `bool &` out parameter is the success flag, the out + * parameter the returned bool. + */ +template +struct Bound +{ + using Signature = BoundSignature; + + template + static zend_always_inline decltype(auto) call(zend_execute_data *execute_data, A &&...args) + { + if constexpr (std::is_void_v) { + return M(std::forward(args)...); + } else { + return (typename Signature::Class(Z_OBJ_P(ZEND_THIS)).*M)(std::forward(args)...); + } + } + + template + static zend_always_inline void invoke(zend_execute_data *execute_data, zval *return_value, A &&...args) + { + using R = typename Signature::Return; + if constexpr (std::is_same_v) { + PT_RETURN_VAL(call(execute_data, std::forward(args)...)); + } else if constexpr (std::is_void_v) { + call(execute_data, std::forward(args)...); + } else { + static_assert(std::is_same_v && Signature::arity == sizeof...(K) + 1, "a bound method returns zv::Val, void, or bool with a trailing bool & out parameter"); + bool out; + if (UNEXPECTED(!call(execute_data, std::forward(args)..., out))) RETURN_THROWS(); + RETURN_BOOL(out); + } + } + + template + using At = std::tuple_element_t>; + + /* the destinations are uninitialized locals, as the declarations above a + * hand-written ZEND_PARSE_PARAMETERS block leave them */ + static void ZEND_FASTCALL handle(INTERNAL_FUNCTION_PARAMETERS) + { + if constexpr (sizeof...(K) == 0) { + ZEND_PARSE_PARAMETERS_NONE(); + invoke(execute_data, return_value); + } else if constexpr (sizeof...(K) == 1) { + typename At<0>::type a0; + if (!zp::parse(execute_data, a0)) RETURN_THROWS(); + invoke(execute_data, return_value, a0); + } else if constexpr (sizeof...(K) == 2) { + typename At<0>::type a0; + typename At<1>::type a1; + if (!zp::parse(execute_data, a0, a1)) RETURN_THROWS(); + invoke(execute_data, return_value, a0, a1); + } else if constexpr (sizeof...(K) == 3) { + typename At<0>::type a0; + typename At<1>::type a1; + typename At<2>::type a2; + if (!zp::parse(execute_data, a0, a1, a2)) RETURN_THROWS(); + invoke(execute_data, return_value, a0, a1, a2); + } else if constexpr (sizeof...(K) == 4) { + typename At<0>::type a0; + typename At<1>::type a1; + typename At<2>::type a2; + typename At<3>::type a3; + if (!zp::parse(execute_data, a0, a1, a2, a3)) RETURN_THROWS(); + invoke(execute_data, return_value, a0, a1, a2, a3); + } else { + static_assert(sizeof...(K) <= 4, "bind at most four parameters; write the glue by hand beyond that"); + } + } +}; + +} // namespace detail + /* * Builder for one class. Usage: * @@ -279,6 +573,17 @@ class Class return *this; } + /* + * A method with a generated handler: the parameters parsed as the zp + * kinds K and delegated to M (see detail::Bound), the required-args + * count derived from the kinds. + */ + template + Class &method(const char *methodName, uint32_t flags, std::initializer_list args, const Arg *returns = NULL) + { + return method(methodName, flags, zp::required(), args, &detail::Bound::handle, returns); + } + /* declaration order defines the OBJ_PROP_NUM slot, as with the macros */ Class &privateLongProperty(const char *propertyName, zend_long defaultValue) { From 968426ed2aff17842d05624f3f29d3052313e4b7 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Tue, 15 Sep 2026 23:00:05 +0200 Subject: [PATCH 03/12] Pack the arguments of engine calls with zv::Args The `zval args[N]` + one ZVAL_* line per slot of an engine call is a zv::Args pack: the type of each value picks the ZVAL_* macro, nothing is addref'ed, and the pack converts to the zval * the call takes. Everything is inline and fills the same zvals the macros did. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01MShHKdUB19w38vboJLPKXy --- turbo-ext/README.md | 4 ++- turbo-ext/src/parser/ParserRunner.cpp | 12 ++------ turbo-ext/src/zv.h | 40 +++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 10 deletions(-) diff --git a/turbo-ext/README.md b/turbo-ext/README.md index 93b54bfe735..435f88789c5 100644 --- a/turbo-ext/README.md +++ b/turbo-ext/README.md @@ -373,7 +373,9 @@ The native sources are C++ that mirrors the PHP implementations they replace: each shadowed class is a handle class in `namespace phpstanturbo` with the twin's methods (see `src/TrinaryLogic.cpp` for the reference shape), built on the zero-cost wrappers in `src/zv.h` — borrowed `zv::Ref` views, owned -move-only `zv::Val` RAII values, range-for HashTable iteration. The wrappers +move-only `zv::Val` RAII values, range-for HashTable iteration, `zv::Args` +argument packs for engine calls — and the shared bodies in `src/TypeTraits.h` +(a member the ports would otherwise repeat verbatim forwards there). The wrappers compile to the same instructions as the raw zend macros (verified by interleaved A/B benchmark), so readability costs nothing. Classes register through the fluent builder in `src/reg.h`, which emits the raw zend diff --git a/turbo-ext/src/parser/ParserRunner.cpp b/turbo-ext/src/parser/ParserRunner.cpp index d3c55420d1d..e21084c7143 100644 --- a/turbo-ext/src/parser/ParserRunner.cpp +++ b/turbo-ext/src/parser/ParserRunner.cpp @@ -421,9 +421,7 @@ static zval makeErrorObject(zend_string *msg, zval *attrsBorrowed) { zval error; object_init_ex(&error, g_errorCe); - zval args[2]; - ZVAL_STR(&args[0], msg); - ZVAL_COPY_VALUE(&args[1], attrsBorrowed); + zv::Args args{msg, attrsBorrowed}; zend_call_known_function(g_errorCe->constructor, Z_OBJ(error), g_errorCe, NULL, 2, args, NULL); return error; } @@ -1167,9 +1165,7 @@ bool ParserEngine::parse(zval *code, zval *return_value) zend_function *tokenizeFn = pt_find_method(Z_OBJCE_P(lexer.raw()), "tokenize", sizeof("tokenize") - 1); if (tokenizeFn == NULL) return false; zval tokensLocal; - zval args[2]; - ZVAL_COPY_VALUE(&args[0], code); - ZVAL_COPY_VALUE(&args[1], errorHandler); + zv::Args args{code, errorHandler}; zend_call_known_function(tokenizeFn, Z_OBJ_P(lexer.raw()), Z_OBJCE_P(lexer.raw()), &tokensLocal, 2, args, NULL); if (EG(exception) != NULL) { RETVAL_NULL(); @@ -1247,9 +1243,7 @@ void pt_register_parser_runner(void) pt_throw_should_not_happen(); RETURN_THROWS(); } - zval args[2]; - ZVAL_COPY_VALUE(&args[0], code); - ZVAL_COPY_VALUE(&args[1], errorHandler); + zv::Args args{code, errorHandler}; zend_call_known_function(parseFn, Z_OBJ_P(parserObj), Z_OBJCE_P(parserObj), return_value, 2, args, NULL); }); diff --git a/turbo-ext/src/zv.h b/turbo-ext/src/zv.h index fb1b5c78e40..36e5ddeae0f 100644 --- a/turbo-ext/src/zv.h +++ b/turbo-ext/src/zv.h @@ -559,6 +559,46 @@ class ObjRef } }; +/* the argument vector of an engine call, filled from typed values the way + * the ZVAL_* macros fill a `zval args[N]`: nothing is addref'ed (a zval * + * is copied by value, an object / string / array stored borrowed), the + * type picks the macro, and the pack converts to the zval * the call takes. + * `zv::Args args{type, strictTypes};` replaces the declaration plus one + * ZVAL_* line per slot. */ +struct NullArg +{ +}; +inline constexpr NullArg null{}; + +template +class Args +{ +public: + template + zend_always_inline Args(T &&...values) + { + zval *slot = argv; + ((set(slot++, std::forward(values))), ...); + } + + zend_always_inline operator zval *() { return argv; } + +private: + zval argv[N]; + + static zend_always_inline void set(zval *slot, const zval *value) { ZVAL_COPY_VALUE(slot, value); } + static zend_always_inline void set(zval *slot, zend_object *value) { ZVAL_OBJ(slot, value); } + static zend_always_inline void set(zval *slot, zend_string *value) { ZVAL_STR(slot, value); } + static zend_always_inline void set(zval *slot, HashTable *value) { ZVAL_ARR(slot, value); } + static zend_always_inline void set(zval *slot, bool value) { ZVAL_BOOL(slot, value); } + static zend_always_inline void set(zval *slot, zend_long value) { ZVAL_LONG(slot, value); } + static zend_always_inline void set(zval *slot, double value) { ZVAL_DOUBLE(slot, value); } + static zend_always_inline void set(zval *slot, NullArg) { ZVAL_NULL(slot); } +}; + +template +Args(T &&...) -> Args; + } // namespace zv #endif From 19dac2d8ae80adea6d428de82d5c5ddf179f5efe Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Tue, 15 Sep 2026 23:00:05 +0200 Subject: [PATCH 04/12] Trim the boilerplate from the native sources' file headers Drop the sentences a header repeats from README.md and the registration code below it: the handle class mirroring the twin with the registration as ABI glue, a bare "declared as ... at activation" restating cls.final()/implements(). File-specific notes stay. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01MShHKdUB19w38vboJLPKXy --- turbo-ext/src/ConditionalExpressionHolder.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/turbo-ext/src/ConditionalExpressionHolder.cpp b/turbo-ext/src/ConditionalExpressionHolder.cpp index b5277a40db3..c1ac8d29b2a 100644 --- a/turbo-ext/src/ConditionalExpressionHolder.cpp +++ b/turbo-ext/src/ConditionalExpressionHolder.cpp @@ -2,9 +2,8 @@ * PHPStanTurbo\ConditionalExpressionHolder — native implementation of * PHPStan\Analyser\ConditionalExpressionHolder. * - * Declared as PHPStan\Analyser\ConditionalExpressionHolder itself at - * activation (final, like the twin). The getKey() string is built by - * pt_ceh_key_build() in support.cpp, shared with ScopeOps. + * The getKey() string is built by pt_ceh_key_build() in support.cpp, shared + * with ScopeOps. */ #include "support.h" From 09d65359e49d1ac00c08d971f5c56ddc82802d4d Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Tue, 15 Sep 2026 23:33:56 +0200 Subject: [PATCH 05/12] Mark the native results that carry a pending exception [[nodiscard]] zv::Val, zv::Arr and zv::Str are [[nodiscard]] types (an UNDEF value is a pending exception), and so are the functions documented to signal one through their bool / -1 / NULL result. No call site dropped such a result. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01P2Qsp9sqyymJYoqgoMkgLT --- turbo-ext/src/ConditionalExpressionHolder.cpp | 2 +- turbo-ext/src/ExpressionTypeHolder.cpp | 4 ++-- turbo-ext/src/Shadow.cpp | 2 +- turbo-ext/src/zv.h | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/turbo-ext/src/ConditionalExpressionHolder.cpp b/turbo-ext/src/ConditionalExpressionHolder.cpp index c1ac8d29b2a..d3e2ea46ae2 100644 --- a/turbo-ext/src/ConditionalExpressionHolder.cpp +++ b/turbo-ext/src/ConditionalExpressionHolder.cpp @@ -19,7 +19,7 @@ class ConditionalExpressionHolder explicit ConditionalExpressionHolder(zval *self) : self(self) {} /* false = pending exception (the twin throws on empty conditions) */ - bool construct(zv::ArrRef conditionExpressionTypeHolders, zv::Ref typeHolder) + [[nodiscard]] bool construct(zv::ArrRef conditionExpressionTypeHolders, zv::Ref typeHolder) { if (UNEXPECTED(conditionExpressionTypeHolders.size() == 0)) { pt_throw_should_not_happen(); diff --git a/turbo-ext/src/ExpressionTypeHolder.cpp b/turbo-ext/src/ExpressionTypeHolder.cpp index fbdac4654af..f706978e411 100644 --- a/turbo-ext/src/ExpressionTypeHolder.cpp +++ b/turbo-ext/src/ExpressionTypeHolder.cpp @@ -47,10 +47,10 @@ class ExpressionTypeHolder } /* false = pending exception */ - bool equalTypes(zval *other, bool &out) const { return pt_holder_equal_types(self, other, &out); } + [[nodiscard]] bool equalTypes(zval *other, bool &out) const { return pt_holder_equal_types(self, other, &out); } /* false = pending exception */ - bool equals(zval *other, bool &out) const { return pt_holder_equals(self, other, &out); } + [[nodiscard]] bool equals(zval *other, bool &out) const { return pt_holder_equals(self, other, &out); } /* and() — a C++ keyword, hence the underscore; UNDEF = pending exception */ zv::Val and_(zval *other) const diff --git a/turbo-ext/src/Shadow.cpp b/turbo-ext/src/Shadow.cpp index d29ede7bef3..420d83acdb7 100644 --- a/turbo-ext/src/Shadow.cpp +++ b/turbo-ext/src/Shadow.cpp @@ -178,7 +178,7 @@ static bool pt_shadow_materialize(reg::ShadowPlan &plan, HashTable *twinFiles, z * the classes as "" instead of the real names. Returns * false with an exception pending when a class could not be declared. */ -bool pt_shadow_activate(HashTable *twinFiles, zend_string *prefix) +[[nodiscard]] bool pt_shadow_activate(HashTable *twinFiles, zend_string *prefix) { if (pt_shadow_active) { zend_throw_error(NULL, "phpstan_turbo: the shadowing classes are already active"); diff --git a/turbo-ext/src/zv.h b/turbo-ext/src/zv.h index 36e5ddeae0f..9bf834d7cc5 100644 --- a/turbo-ext/src/zv.h +++ b/turbo-ext/src/zv.h @@ -88,7 +88,7 @@ class Ref }; /* Owned zval: move-only RAII. Adopts an already-owned raw zval. */ -class Val +class [[nodiscard]] Val { protected: zval z; @@ -210,7 +210,7 @@ inline void Ref::assign(Val owned) /* Owned zend_string: move-only RAII. NULL is the empty state, so a failed * producer (e.g. pt_node_key on exception) can be adopted and tested. */ -class Str +class [[nodiscard]] Str { zend_string *s; @@ -397,7 +397,7 @@ class ArrRef : public Ref * `return zv::Val(std::move(x));` — the glibc-2.35 baseline compiler * (gcc 11) predates P1825's implicit derived-to-base move on return and * would require the deleted copy constructor. */ -class Arr : public Val +class [[nodiscard]] Arr : public Val { public: Arr() = default; From 4171d477e9c05ca2fdaf35f116f6a1fc8a09aab5 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Tue, 15 Sep 2026 23:36:33 +0200 Subject: [PATCH 06/12] Check that the native sources' lowercase name literals name real members MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit side-by-side.php now verifies every PT_LC("...") lowercase identifier the native code passes — the lowercased method names of by-name calls, $this-dispatch and function-table lookups — against the methods (trait aliases included), properties and constants of src/ and the vendored libraries, the internal functions and members, the type keywords, PHP's magic methods and the methods the extension registers itself. Lookup-table entries and plain string data passed to zend_string_init() and the like are not names. A misspelt name used to fail only when its path first ran. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01P2Qsp9sqyymJYoqgoMkgLT --- turbo-ext/bin/side-by-side.php | 85 +++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/turbo-ext/bin/side-by-side.php b/turbo-ext/bin/side-by-side.php index f425cfce37f..ee8e15b09e2 100644 --- a/turbo-ext/bin/side-by-side.php +++ b/turbo-ext/bin/side-by-side.php @@ -378,8 +378,91 @@ function checkWindowsSources(): array return $problems; } +/** + * Every lowercase identifier the native code passes as PT_LC("...") — the + * lowercased method names of by-name calls into userland, $this-dispatch and + * function-table lookups — must name something that exists: a method of a + * PHP class (trait `as` aliases included), a property or constant, an + * internal function or member, a type keyword, or a method the extension + * registers itself. A misspelt name would otherwise only fail when that + * path first runs. + * + * @return list problems + */ +function checkLowercaseNameLiterals(): array +{ + $known = array_fill_keys(['array', 'bool', 'callable', 'false', 'float', 'int', 'iterable', 'mixed', 'never', 'null', 'object', 'resource', 'self', 'static', 'string', 'true', 'void', 'parent'], true); + $remember = static function (string $name) use (&$known): void { + $known[strtolower($name)] = true; + }; + foreach (['src', 'vendor/nikic/php-parser/lib', 'vendor/ondrejmirtes/better-reflection/src', 'vendor/phpstan/phpdoc-parser/src'] as $dir) { + foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS)) as $file) { + if ($file->getExtension() !== 'php') { + continue; + } + $code = file_get_contents($file->getPathname()); + preg_match_all('~\bfunction\s+&?\s*(\w+)\s*\(|\bas\s+(?:(?:public|protected|private)\s+)?(\w+)\s*;|\$(\w+)|\bconst\s+(?:\w+\s+)?(\w+)\s*=~', $code, $m); + foreach ([1, 2, 3, 4] as $group) { + foreach (array_filter($m[$group]) as $name) { + $remember($name); + } + } + } + } + foreach (array_merge(get_declared_classes(), get_declared_interfaces(), get_declared_traits()) as $className) { + $class = new ReflectionClass($className); + if (!$class->isInternal()) { + continue; + } + foreach ($class->getMethods() as $method) { + $remember($method->getName()); + } + foreach ($class->getProperties() as $property) { + $remember($property->getName()); + } + } + foreach (get_defined_functions()['internal'] as $function) { + $remember($function); + } + $sources = array_merge(glob('turbo-ext/src/*.cpp'), glob('turbo-ext/src/*.h'), glob('turbo-ext/src/parser/*.cpp'), glob('turbo-ext/src/parser/*.h')); + foreach ($sources as $source) { + preg_match_all('~\.(?:method|traitMethod)(?:<[^(]*>)?\("(\w+)"~', file_get_contents($source), $m); + foreach ($m[1] as $name) { + $remember($name); + } + } + + foreach (['__construct', '__destruct', '__call', '__callstatic', '__get', '__set', '__isset', '__unset', '__sleep', '__wakeup', '__serialize', '__unserialize', '__tostring', '__invoke', '__set_state', '__clone', '__debuginfo'] as $magic) { + $known[$magic] = true; + } + // consumers of string data rather than member names + $dataConsumers = array_fill_keys(['zend_string_init', 'zend_string_init_interned', 'smart_str_appendl', 'Val::string', 'zv::Val::string'], true); + + $problems = []; + foreach ($sources as $source) { + foreach (file($source) as $i => $line) { + preg_match_all('~PT_LC\("([a-z_][a-z0-9_]*)"\)~', $line, $m, PREG_OFFSET_CAPTURE); + foreach ($m[1] as [$name, $offset]) { + if (isset($known[$name])) { + continue; + } + $before = substr($line, 0, $offset - strlen('PT_LC("')); + if (preg_match('~\{\s*$~', $before) === 1) { + continue; // an entry of a { PT_LC("..."), ... } lookup table + } + if (preg_match('~([\w:]+)\s*\((?:[^()]*,\s*)?$~', $before, $consumer) === 1 && isset($dataConsumers[$consumer[1]])) { + continue; + } + $problems[] = sprintf('%s:%d: PT_LC("%s") names no method, property, constant or function', $source, $i + 1, $name); + } + } + } + + return $problems; +} + $failed = false; -foreach (array_merge(checkStructure($manifest), checkGeneratedArtifacts($collector, $collected), checkWindowsSources()) as $problem) { +foreach (array_merge(checkStructure($manifest), checkGeneratedArtifacts($collector, $collected), checkWindowsSources(), checkLowercaseNameLiterals()) as $problem) { printf("✗ %s\n", $problem); $failed = true; } From 3912d9f838119614101c3786724f78a91de9f256 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Tue, 15 Sep 2026 23:48:37 +0200 Subject: [PATCH 07/12] Generate the class and property declarations of the native classes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit turbo-ext/bin/generate-declarations.php derives the declarative half of every shadowing class from its PHP twin (PHPStan\Build\TurboDeclarationGenerator) into turbo-ext/src/generated/.h: declareClass() with the final / abstract flags, the parent and the directly implemented interfaces, declareProperties() with the twin's own property declarations, and the OBJ_PROP_NUM slot of each instance property. The 13 registrations of the shadowing classes call declareClass() instead of spelling cls.final() / parent() / implements() out; the 6 classes whose native properties equal the twin's call declareProperties() instead of their builder calls (the rest deliberately declare different state). A reflection dump of every native class — modifiers, parent, interfaces, constants, properties with types and defaults, method signatures — is identical before and after. side-by-side.php fails while a generated header is stale. reg.h can declare every property shape a twin spells: reg::Class::property() takes the visibility flags, the kind and the type, and the kinds cover a typed property with no default and one defaulting to null, [], false, a bool or an int, class-typed ones and unions of classes. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01P2Qsp9sqyymJYoqgoMkgLT --- .../Build/TurboDeclarationGenerator.php | 350 ++++++++++++++++++ build/phpstan.neon | 6 + turbo-ext/CLAUDE.md | 11 +- turbo-ext/Makefile | 2 +- turbo-ext/bin/generate-declarations.php | 58 +++ turbo-ext/bin/side-by-side.php | 14 + turbo-ext/src/ArenaCache.cpp | 4 +- turbo-ext/src/CombinationsHelper.cpp | 4 +- turbo-ext/src/ConditionalExpressionHolder.cpp | 3 +- turbo-ext/src/ExpressionResultStorage.cpp | 3 +- turbo-ext/src/ExpressionTypeHolder.cpp | 3 +- turbo-ext/src/NodeScanner.cpp | 4 +- turbo-ext/src/NodeTraverser.cpp | 3 +- turbo-ext/src/PhpFileCleaner.cpp | 3 +- turbo-ext/src/ScopeOps.cpp | 4 +- turbo-ext/src/SymbolFinderInFiles.cpp | 3 +- turbo-ext/src/TrinaryLogic.cpp | 3 +- turbo-ext/src/TypeCombinatorCache.cpp | 4 +- turbo-ext/src/generated/ArenaCache.h | 24 ++ turbo-ext/src/generated/CombinationsHelper.h | 24 ++ .../generated/ConditionalExpressionHolder.h | 31 ++ .../src/generated/ExpressionResultStorage.h | 31 ++ .../src/generated/ExpressionTypeHolder.h | 33 ++ turbo-ext/src/generated/NodeScanner.h | 24 ++ turbo-ext/src/generated/NodeTraverser.h | 31 ++ turbo-ext/src/generated/ParserRunner.h | 24 ++ turbo-ext/src/generated/PhpFileCleaner.h | 29 ++ turbo-ext/src/generated/ScopeOps.h | 24 ++ turbo-ext/src/generated/SymbolFinderInFiles.h | 29 ++ turbo-ext/src/generated/TrinaryLogic.h | 33 ++ turbo-ext/src/generated/TypeCombinatorCache.h | 24 ++ turbo-ext/src/parser/ParserRunner.cpp | 4 +- turbo-ext/src/reg.h | 110 +++++- 33 files changed, 933 insertions(+), 24 deletions(-) create mode 100644 build/PHPStan/Build/TurboDeclarationGenerator.php create mode 100644 turbo-ext/bin/generate-declarations.php create mode 100644 turbo-ext/src/generated/ArenaCache.h create mode 100644 turbo-ext/src/generated/CombinationsHelper.h create mode 100644 turbo-ext/src/generated/ConditionalExpressionHolder.h create mode 100644 turbo-ext/src/generated/ExpressionResultStorage.h create mode 100644 turbo-ext/src/generated/ExpressionTypeHolder.h create mode 100644 turbo-ext/src/generated/NodeScanner.h create mode 100644 turbo-ext/src/generated/NodeTraverser.h create mode 100644 turbo-ext/src/generated/ParserRunner.h create mode 100644 turbo-ext/src/generated/PhpFileCleaner.h create mode 100644 turbo-ext/src/generated/ScopeOps.h create mode 100644 turbo-ext/src/generated/SymbolFinderInFiles.h create mode 100644 turbo-ext/src/generated/TrinaryLogic.h create mode 100644 turbo-ext/src/generated/TypeCombinatorCache.h diff --git a/build/PHPStan/Build/TurboDeclarationGenerator.php b/build/PHPStan/Build/TurboDeclarationGenerator.php new file mode 100644 index 00000000000..98e3c9751d5 --- /dev/null +++ b/build/PHPStan/Build/TurboDeclarationGenerator.php @@ -0,0 +1,350 @@ +.h for the native + * registration to call: the class declaration (final/abstract, parent, the + * directly implemented interfaces), the OBJ_PROP_NUM slot of each instance + * property the twin declares, and the property declarations themselves. + * Shared by turbo-ext/bin/generate-declarations.php and the drift check in + * turbo-ext/bin/side-by-side.php. + * + * A property whose declaration reg::Class cannot express (a string or + * non-empty array default, an intersection type, a defaulted union of + * classes) leaves its class without declareProperties(); the class keeps + * declaring its properties by hand. + */ +final class TurboDeclarationGenerator +{ + + /** names a slot constant cannot carry verbatim: C++ keywords and the libc macros of common targets */ + private const RESERVED = [ + 'alignas', 'alignof', 'and', 'auto', 'bool', 'break', 'case', 'catch', 'char', 'class', 'const', 'continue', + 'default', 'delete', 'do', 'double', 'else', 'enum', 'explicit', 'export', 'extern', 'false', 'float', 'for', + 'friend', 'goto', 'if', 'inline', 'int', 'long', 'mutable', 'namespace', 'new', 'noexcept', 'not', 'nullptr', + 'operator', 'or', 'private', 'protected', 'public', 'register', 'return', 'short', 'signed', 'sizeof', 'static', + 'struct', 'switch', 'template', 'this', 'throw', 'true', 'try', 'typedef', 'typeid', 'typename', 'union', + 'unsigned', 'using', 'virtual', 'void', 'volatile', 'while', 'xor', + 'major', 'minor', 'makedev', 'stdin', 'stdout', 'stderr', 'errno', 'assert', 'unix', 'linux', + ]; + + /** + * @param array $manifest + */ + public function __construct(private array $manifest) + { + } + + /** + * @return array repository-relative path => content + */ + public function render(): array + { + $classesPerCpp = []; + foreach ($this->manifest as $className => $entry) { + $classesPerCpp[$entry['cpp']][] = $className; + } + + $files = []; + foreach ($this->manifest as $className => $entry) { + if (!class_exists($className)) { + throw new RuntimeException(sprintf('%s (shadowed by %s) cannot be loaded', $className, $entry['cpp'])); + } + $stem = basename($entry['cpp'], '.cpp'); + if (count($classesPerCpp[$entry['cpp']]) > 1) { + $stem .= '_' . (new ReflectionClass($className))->getShortName(); + } + $files['turbo-ext/src/generated/' . $stem . '.h'] = $this->renderClass(new ReflectionClass($className), $entry['php'], $stem); + } + ksort($files); + + return $files; + } + + /** + * @param ReflectionClass $class + */ + private function renderClass(ReflectionClass $class, string $phpFile, string $stem): string + { + $guard = 'PHPSTANTURBO_GENERATED_' . strtoupper((string) preg_replace('~(?<=[a-z0-9])(?=[A-Z])~', '_', $stem)) . '_H'; + $out = []; + $out[] = '/* Generated by turbo-ext/bin/generate-declarations.php from'; + $out[] = ' * ' . $phpFile . ' — do not edit. */'; + $out[] = ''; + $out[] = '#ifndef ' . $guard; + $out[] = '#define ' . $guard; + $out[] = ''; + $out[] = '#include "../reg.h"'; + $out[] = ''; + $out[] = 'namespace ptdecl::' . $stem . ' {'; + $out[] = ''; + + $slots = $this->instanceSlots($class); + $own = []; + foreach ($slots as $index => $slot) { + if ($slot[0] !== $class->getName()) { + continue; + } + + $own[] = sprintf('inline constexpr uint32_t %s = %d;', $this->cName($slot[1]), $index); + } + if ($own !== []) { + $out[] = '/* the OBJ_PROP_NUM slots of the instance properties the class declares (the inherited ones come first) */'; + $out[] = 'namespace slot {'; + foreach ($own as $line) { + $out[] = $line; + } + $out[] = '} // namespace slot'; + $out[] = ''; + } + + $out[] = 'inline void declareClass(reg::Class &cls)'; + $out[] = '{'; + $body = []; + if ($class->isFinal()) { + $body[] = 'cls.final();'; + } + if ($class->isAbstract() && !$class->isInterface()) { + $body[] = 'cls.abstract_();'; + } + $parent = $class->getParentClass(); + if ($parent !== false) { + $body[] = sprintf('cls.parent(%s);', $this->cString($parent->getName())); + } + $interfaces = $this->directInterfaces($class); + if ($interfaces !== []) { + $body[] = sprintf('cls.implements({ %s });', implode(', ', array_map(fn (string $name): string => $this->cString($name), $interfaces))); + } + if ($body === []) { + $body[] = '(void) cls;'; + } + foreach ($body as $line) { + $out[] = "\t" . $line; + } + $out[] = '}'; + + $traitProperties = []; + foreach ($class->getTraits() as $trait) { + foreach ($trait->getProperties() as $property) { + $traitProperties[$property->getName()] = true; + } + } + $declarations = []; + $unrepresentable = null; + foreach ($class->getProperties() as $property) { + if ($property->getDeclaringClass()->getName() !== $class->getName() || isset($traitProperties[$property->getName()])) { + continue; + } + try { + $declarations[] = $this->renderProperty($property); + } catch (RuntimeException $e) { + $unrepresentable = sprintf('$%s: %s', $property->getName(), $e->getMessage()); + break; + } + } + $out[] = ''; + if ($unrepresentable !== null) { + $out[] = sprintf('/* no declareProperties(): %s */', $unrepresentable); + } else { + $out[] = '/* the properties the class declares itself, in declaration order (a used trait\'s come from its registrar) */'; + $out[] = 'inline void declareProperties(reg::Class &cls)'; + $out[] = '{'; + if ($declarations === []) { + $out[] = "\t(void) cls;"; + } + foreach ($declarations as $declaration) { + $out[] = "\t" . $declaration; + } + $out[] = '}'; + } + + $out[] = ''; + $out[] = '} // namespace ptdecl::' . $stem; + $out[] = ''; + $out[] = '#endif'; + $out[] = ''; + + return implode("\n", $out); + } + + /** + * The instance properties in OBJ_PROP_NUM order: the parent's first, then + * the class's own in declaration order (a used trait's after them). + * + * @param ReflectionClass $class + * @return list declaring class, property name + */ + private function instanceSlots(ReflectionClass $class): array + { + $parent = $class->getParentClass(); + $slots = $parent !== false ? $this->instanceSlots($parent) : []; + foreach ($class->getProperties() as $property) { + if ($property->isStatic() || $property->getDeclaringClass()->getName() !== $class->getName()) { + continue; + } + $slots[] = [$class->getName(), $property->getName()]; + } + + return $slots; + } + + /** + * The interfaces the class declaration itself names: neither inherited + * from the parent nor extended by another of them. + * + * @param ReflectionClass $class + * @return list + */ + private function directInterfaces(ReflectionClass $class): array + { + $parent = $class->getParentClass(); + $inherited = $parent !== false ? $parent->getInterfaceNames() : []; + $candidates = array_values(array_filter($class->getInterfaceNames(), static fn (string $name): bool => !in_array($name, $inherited, true))); + + return array_values(array_filter($candidates, static function (string $name) use ($candidates): bool { + foreach ($candidates as $other) { + if ($other !== $name && in_array($name, (new ReflectionClass($other))->getInterfaceNames(), true)) { + return false; + } + } + + return true; + })); + } + + private function renderProperty(ReflectionProperty $property): string + { + $visibility = [$property->isPrivate() ? 'ZEND_ACC_PRIVATE' : ($property->isProtected() ? 'ZEND_ACC_PROTECTED' : 'ZEND_ACC_PUBLIC')]; + if ($property->isStatic()) { + $visibility[] = 'ZEND_ACC_STATIC'; + } + if ($property->isReadOnly()) { + $visibility[] = 'ZEND_ACC_READONLY'; + } + $flags = implode(' | ', $visibility); + $name = $this->cString($property->getName()); + $hasDefault = $property->hasDefaultValue() && !$property->isPromoted(); + $default = $hasDefault ? $property->getDefaultValue() : null; + + if (!$property->hasType()) { + if ($default === null) { + return sprintf('cls.property(%s, %s, reg::PropertyKind::Null, 0);', $name, $flags); + } + if ($default === []) { + return sprintf('cls.property(%s, %s, reg::PropertyKind::EmptyArray, 0);', $name, $flags); + } + if (is_bool($default)) { + return sprintf('cls.property(%s, %s, reg::PropertyKind::Bool, %d);', $name, $flags, $default ? 1 : 0); + } + if (is_int($default)) { + return sprintf('cls.property(%s, %s, reg::PropertyKind::Long, %d);', $name, $flags, $default); + } + throw new RuntimeException('an untyped default reg::Class cannot declare'); + } + + [$masks, $classes] = $this->typeParts($property->getType()); + $mask = $masks === [] ? '0' : implode(' | ', $masks); + $classArg = count($classes) === 0 ? '' : ', ' . $this->cString(implode('|', $classes)); + + if (count($classes) > 1) { + if ($hasDefault || array_filter($masks, static fn (string $m): bool => $m !== 'MAY_BE_NULL') !== []) { + throw new RuntimeException('a union of classes with a default or scalar members'); + } + return sprintf('cls.property(%s, %s, reg::PropertyKind::TypedClassUnion, %s%s);', $name, $flags, $mask, $classArg); + } + if (!$hasDefault) { + return sprintf('cls.property(%s, %s, reg::PropertyKind::Typed, %s%s);', $name, $flags, $mask, $classArg); + } + if ($default === null) { + return sprintf('cls.property(%s, %s, reg::PropertyKind::TypedNull, %s%s);', $name, $flags, $mask, $classArg); + } + if ($default === [] && $classes === []) { + return sprintf('cls.property(%s, %s, reg::PropertyKind::TypedEmptyArray, %s);', $name, $flags, $mask); + } + if (is_bool($default) && $classes === [] && $masks === ['MAY_BE_BOOL']) { + return sprintf('cls.property(%s, %s, reg::PropertyKind::TypedBool, %d);', $name, $flags, $default ? 1 : 0); + } + if (is_int($default) && $classes === [] && $masks === ['MAY_BE_LONG']) { + return sprintf('cls.property(%s, %s, reg::PropertyKind::TypedLong, %d);', $name, $flags, $default); + } + if ($default === false) { + return sprintf('cls.property(%s, %s, reg::PropertyKind::TypedFalse, %s%s);', $name, $flags, $mask, $classArg); + } + + throw new RuntimeException('a typed default reg::Class cannot declare'); + } + + /** + * @return array{list, list} MAY_BE_* masks, class names + */ + private function typeParts(?ReflectionType $type): array + { + if ($type instanceof ReflectionIntersectionType) { + throw new RuntimeException('an intersection type'); + } + $builtin = [ + 'int' => 'MAY_BE_LONG', 'float' => 'MAY_BE_DOUBLE', 'string' => 'MAY_BE_STRING', 'bool' => 'MAY_BE_BOOL', + 'array' => 'MAY_BE_ARRAY', 'null' => 'MAY_BE_NULL', 'false' => 'MAY_BE_FALSE', 'true' => 'MAY_BE_TRUE', + 'mixed' => 'MAY_BE_ANY', 'object' => 'MAY_BE_OBJECT', + ]; + $members = $type instanceof ReflectionUnionType ? $type->getTypes() : [$type]; + $masks = []; + $classes = []; + foreach ($members as $member) { + if (!$member instanceof ReflectionNamedType) { + throw new RuntimeException('a nested intersection in a union'); + } + $typeName = $member->getName(); + if (isset($builtin[$typeName])) { + $masks[$builtin[$typeName]] = true; + } else { + $classes[] = $typeName; + } + } + if ($type instanceof ReflectionNamedType && $type->allowsNull() && $type->getName() !== 'mixed' && $type->getName() !== 'null') { + $masks['MAY_BE_NULL'] = true; + } + $ordered = array_values(array_filter( + ['MAY_BE_NULL', 'MAY_BE_FALSE', 'MAY_BE_TRUE', 'MAY_BE_BOOL', 'MAY_BE_LONG', 'MAY_BE_DOUBLE', 'MAY_BE_STRING', 'MAY_BE_ARRAY', 'MAY_BE_OBJECT', 'MAY_BE_ANY'], + static fn (string $m): bool => isset($masks[$m]), + )); + + return [$ordered, $classes]; + } + + private function cString(string $value): string + { + return '"' . str_replace(['\\', '"'], ['\\\\', '\\"'], $value) . '"'; + } + + private function cName(string $propertyName): string + { + return in_array($propertyName, self::RESERVED, true) ? $propertyName . '_' : $propertyName; + } + +} diff --git a/build/phpstan.neon b/build/phpstan.neon index 07450264cca..5226880e48c 100644 --- a/build/phpstan.neon +++ b/build/phpstan.neon @@ -150,6 +150,12 @@ parameters: identifier: shipmonk.deadMethod path: PHPStan/Build/TurboAttributeCollector.php reportUnmatched: false + - + # called from turbo-ext/bin/generate-declarations.php and + # turbo-ext/bin/side-by-side.php, outside the analysed paths + identifier: shipmonk.deadMethod + path: PHPStan/Build/TurboDeclarationGenerator.php + reportUnmatched: false - # called from bin/phpstan before the autoloader, outside the analysed paths identifier: shipmonk.deadMethod diff --git a/turbo-ext/CLAUDE.md b/turbo-ext/CLAUDE.md index 1388efae439..2aa2a67dc74 100644 --- a/turbo-ext/CLAUDE.md +++ b/turbo-ext/CLAUDE.md @@ -22,8 +22,7 @@ being ≥0.5% faster is. When the estimate is marginal, don't port. matches nothing — use single quotes). Run the full test suite now, before any native work. 2. **Note its parent and interfaces** — the native class is declared with - the twin's real name, final flag, parent and interfaces - (`cls.final()`, `cls.parent(...)`, `cls.implements({...})`), and linked + the twin's real name, final flag, parent and interfaces, and linked like a PHP declaration: interface methods need declared return types, a non-final class must dispatch its own non-final methods through the object's class entry (a PHP subclass may override them). If it is a DI @@ -78,6 +77,14 @@ being ≥0.5% faster is. When the estimate is marginal, don't port. `vendor/turbo-class-map.php` from the attributes (shadowed classes living in vendor/ cannot carry the attribute and are hardcoded in `build/TurboAttributeCollector.php`). +5a. **Generate its declarations**: `php turbo-ext/bin/generate-declarations.php` + writes `turbo-ext/src/generated/.h` from the twin — `declareClass(cls)` + (final/abstract, parent, the directly implemented interfaces), + `declareProperties(cls)` (the twin's own properties, exactly) and the + `slot::` constants of its instance properties. Call both first in the + registration function instead of spelling them out; side-by-side.php + fails while a header is stale. A class whose native properties deliberately + differ from the twin keeps declaring them by hand. 6. **Check method parity**: `php bin/side-by-side.php` must pass (it also re-derives the generated `vendor/turbo-*` files from the attributes and byte-compares them, so a stale autoloader dump fails there). diff --git a/turbo-ext/Makefile b/turbo-ext/Makefile index 28d829d3ad8..90c6c912f3d 100644 --- a/turbo-ext/Makefile +++ b/turbo-ext/Makefile @@ -58,7 +58,7 @@ OBJECTS := $(SOURCES:.cpp=.o) phpstan_turbo.so: $(OBJECTS) $(CXX) `$(PHP_CONFIG) --ldflags` -shared $(LINK_FLAGS) -o $@ $(OBJECTS) -src/%.o: src/%.cpp src/support.h src/zv.h src/reg.h +src/%.o: src/%.cpp src/support.h src/zv.h src/reg.h $(wildcard src/generated/*.h) $(CXX) $(CXXFLAGS) -c -o $@ $< src/main.o: version.stamp diff --git a/turbo-ext/bin/generate-declarations.php b/turbo-ext/bin/generate-declarations.php new file mode 100644 index 00000000000..3b96dd888e7 --- /dev/null +++ b/turbo-ext/bin/generate-declarations.php @@ -0,0 +1,58 @@ +.h — the class declaration, the + * property slots and the property declarations of every shadowing class, + * derived from its PHP twin (PHPStan\Build\TurboDeclarationGenerator). Run + * it after changing a shadowed class's declaration; side-by-side.php fails + * while a generated header is stale. + * + * Usage: php turbo-ext/bin/generate-declarations.php + * + * Requires vendor/ (run composer install first). + */ + +use PHPStan\Build\TurboAttributeCollector; +use PHPStan\Build\TurboDeclarationGenerator; + +error_reporting(E_ALL); + +$root = dirname(__DIR__, 2); +chdir($root); + +require 'vendor/autoload.php'; +require_once 'build/PHPStan/Build/TurboAttributeCollector.php'; +require_once 'build/PHPStan/Build/TurboDeclarationGenerator.php'; + +$collected = (new TurboAttributeCollector($root))->collect(); +$files = (new TurboDeclarationGenerator($collected['manifest']))->render(); + +$dir = 'turbo-ext/src/generated'; +if (!is_dir($dir) && !mkdir($dir)) { + fwrite(STDERR, "cannot create $dir\n"); + exit(1); +} + +$written = 0; +foreach ($files as $path => $content) { + if (is_file($path) && file_get_contents($path) === $content) { + continue; + } + file_put_contents($path, $content); + $written++; +} +$removed = 0; +foreach (glob($dir . '/*.h') ?: [] as $existing) { + if (!isset($files[$existing])) { + unlink($existing); + $removed++; + } +} +$withoutProperties = 0; +foreach ($files as $content) { + if (str_contains($content, '/* no declareProperties():')) { + $withoutProperties++; + } +} + +printf("%d headers (%d written, %d removed); %d without declareProperties()\n", count($files), $written, $removed, $withoutProperties); diff --git a/turbo-ext/bin/side-by-side.php b/turbo-ext/bin/side-by-side.php index ee8e15b09e2..4bc52711740 100644 --- a/turbo-ext/bin/side-by-side.php +++ b/turbo-ext/bin/side-by-side.php @@ -349,6 +349,20 @@ function checkGeneratedArtifacts(PHPStan\Build\TurboAttributeCollector $collecto } } + // the declarations generated from the PHP twins + require_once 'build/PHPStan/Build/TurboDeclarationGenerator.php'; + $generated = (new PHPStan\Build\TurboDeclarationGenerator($collected['manifest']))->render(); + foreach ($generated as $file => $content) { + if (!is_file($file) || file_get_contents($file) !== $content) { + $problems[] = sprintf('%s is stale — run php turbo-ext/bin/generate-declarations.php', $file); + } + } + foreach (glob('turbo-ext/src/generated/*.h') ?: [] as $file) { + if (!isset($generated[$file])) { + $problems[] = sprintf('%s belongs to no shadowed class — run php turbo-ext/bin/generate-declarations.php', $file); + } + } + return $problems; } diff --git a/turbo-ext/src/ArenaCache.cpp b/turbo-ext/src/ArenaCache.cpp index 1507f5e79d1..2d182c39df8 100644 --- a/turbo-ext/src/ArenaCache.cpp +++ b/turbo-ext/src/ArenaCache.cpp @@ -49,6 +49,7 @@ */ #include "support.h" +#include "generated/ArenaCache.h" #include "reg.h" #include "zv.h" @@ -1153,7 +1154,8 @@ void pt_arena_mshutdown() void pt_register_arena_cache() { reg::Class cls("PHPStan\\Cache\\ArenaCache"); - cls.final(); + ptdecl::ArenaCache::declareClass(cls); + ptdecl::ArenaCache::declareProperties(cls); cls.method("create", reg::PublicStatic, 1, { reg::stringArg("runId") }, [](INTERNAL_FUNCTION_PARAMETERS) { zend_string *runId; diff --git a/turbo-ext/src/CombinationsHelper.cpp b/turbo-ext/src/CombinationsHelper.cpp index 32119545fb9..5962d7b776f 100644 --- a/turbo-ext/src/CombinationsHelper.cpp +++ b/turbo-ext/src/CombinationsHelper.cpp @@ -4,6 +4,7 @@ */ #include "support.h" +#include "generated/CombinationsHelper.h" #include "zv.h" static zend_class_entry *pt_ce_combinations = nullptr; @@ -142,7 +143,8 @@ using phpstanturbo::CombinationsHelper; void pt_register_combinations_helper() { reg::Class cls("PHPStan\\Internal\\CombinationsHelper"); - cls.final(); + ptdecl::CombinationsHelper::declareClass(cls); + ptdecl::CombinationsHelper::declareProperties(cls); cls.method("combinations", reg::PublicStatic, 1, { reg::arrayArg("arrays") }, [](INTERNAL_FUNCTION_PARAMETERS) { HashTable *arrays; diff --git a/turbo-ext/src/ConditionalExpressionHolder.cpp b/turbo-ext/src/ConditionalExpressionHolder.cpp index d3e2ea46ae2..ef5f27c3fc9 100644 --- a/turbo-ext/src/ConditionalExpressionHolder.cpp +++ b/turbo-ext/src/ConditionalExpressionHolder.cpp @@ -7,6 +7,7 @@ */ #include "support.h" +#include "generated/ConditionalExpressionHolder.h" #include "zv.h" namespace phpstanturbo { @@ -74,7 +75,7 @@ using phpstanturbo::ConditionalExpressionHolder; void pt_register_conditional_expression_holder() { reg::Class cls("PHPStan\\Analyser\\ConditionalExpressionHolder"); - cls.final(); + ptdecl::ConditionalExpressionHolder::declareClass(cls); /* conditionExpressionTypeHolders/typeHolder must stay in this order */ cls.privateNullProperty("conditionExpressionTypeHolders"); cls.privateNullProperty("typeHolder"); diff --git a/turbo-ext/src/ExpressionResultStorage.cpp b/turbo-ext/src/ExpressionResultStorage.cpp index da772661f52..985ceea1af2 100644 --- a/turbo-ext/src/ExpressionResultStorage.cpp +++ b/turbo-ext/src/ExpressionResultStorage.cpp @@ -16,6 +16,7 @@ * fallback chain) into this one, like the twin's SplObjectStorage::addAll(). */ +#include "generated/ExpressionResultStorage.h" #include "support.h" #include "zv.h" @@ -92,7 +93,7 @@ using phpstanturbo::ExpressionResultStorage; void pt_register_expression_result_storage() { reg::Class cls("PHPStan\\Analyser\\ExpressionResultStorage"); - cls.final(); + ptdecl::ExpressionResultStorage::declareClass(cls); /* exprsById/resultsById/fallback must stay in this order (OBJ_PROP_NUM * slots) */ cls.privateArrayProperty("exprsById"); diff --git a/turbo-ext/src/ExpressionTypeHolder.cpp b/turbo-ext/src/ExpressionTypeHolder.cpp index f706978e411..48a0561764c 100644 --- a/turbo-ext/src/ExpressionTypeHolder.cpp +++ b/turbo-ext/src/ExpressionTypeHolder.cpp @@ -13,6 +13,7 @@ */ #include "support.h" +#include "generated/ExpressionTypeHolder.h" #include "zv.h" namespace phpstanturbo { @@ -82,7 +83,7 @@ using phpstanturbo::ExpressionTypeHolder; void pt_register_expression_type_holder() { reg::Class cls("PHPStan\\Analyser\\ExpressionTypeHolder"); - cls.final(); + ptdecl::ExpressionTypeHolder::declareClass(cls); /* expr/type/certainty must stay in this order (OBJ_PROP_NUM slots) */ cls.privateNullProperty("expr"); cls.privateNullProperty("type"); diff --git a/turbo-ext/src/NodeScanner.cpp b/turbo-ext/src/NodeScanner.cpp index d864fcafb20..76bcbc4e9cb 100644 --- a/turbo-ext/src/NodeScanner.cpp +++ b/turbo-ext/src/NodeScanner.cpp @@ -7,6 +7,7 @@ */ #include "support.h" +#include "generated/NodeScanner.h" #include "zv.h" static zend_class_entry *pt_ce_node_scanner; @@ -52,7 +53,8 @@ using phpstanturbo::NodeScanner; void pt_register_node_scanner() { reg::Class cls("PHPStan\\Node\\NodeScanner"); - cls.final(); + ptdecl::NodeScanner::declareClass(cls); + ptdecl::NodeScanner::declareProperties(cls); cls.method("nodeIsOrContainsYield", reg::PublicStatic, 1, { reg::objectArg("node") }, [](INTERNAL_FUNCTION_PARAMETERS) { zval *node; diff --git a/turbo-ext/src/NodeTraverser.cpp b/turbo-ext/src/NodeTraverser.cpp index 4289901d5ba..6c1b4ea5b90 100644 --- a/turbo-ext/src/NodeTraverser.cpp +++ b/turbo-ext/src/NodeTraverser.cpp @@ -14,6 +14,7 @@ */ #include "support.h" +#include "generated/NodeTraverser.h" #include "zv.h" static zend_class_entry *pt_ce_node_traverser; @@ -803,7 +804,7 @@ using phpstanturbo::NodeTraverser; void pt_register_node_traverser() { reg::Class cls("PhpParser\\NodeTraverser"); - cls.implements({ "PhpParser\\NodeTraverserInterface" }); + ptdecl::NodeTraverser::declareClass(cls); /* "visitors" must stay slot 0 and "stopTraversal" slot 1 (PT_NT_PROP_*) */ cls.protectedArrayProperty("visitors"); cls.protectedBoolProperty("stopTraversal", false); diff --git a/turbo-ext/src/PhpFileCleaner.cpp b/turbo-ext/src/PhpFileCleaner.cpp index b3ce2883fa7..db006df4487 100644 --- a/turbo-ext/src/PhpFileCleaner.cpp +++ b/turbo-ext/src/PhpFileCleaner.cpp @@ -11,6 +11,7 @@ */ #include "support.h" +#include "generated/PhpFileCleaner.h" #include "zv.h" #include "SymbolScan.h" @@ -23,7 +24,7 @@ static zend_class_entry *pt_ce_php_file_cleaner = nullptr; void pt_register_php_file_cleaner() { reg::Class cls("PHPStan\\Reflection\\BetterReflection\\SourceLocator\\PhpFileCleaner"); - cls.final(); + ptdecl::PhpFileCleaner::declareClass(cls); cls.method("__construct", reg::Public, 0, {}, [](INTERNAL_FUNCTION_PARAMETERS) { ZEND_PARSE_PARAMETERS_NONE(); diff --git a/turbo-ext/src/ScopeOps.cpp b/turbo-ext/src/ScopeOps.cpp index 06b8de9647d..3eacbe3877d 100644 --- a/turbo-ext/src/ScopeOps.cpp +++ b/turbo-ext/src/ScopeOps.cpp @@ -13,6 +13,7 @@ */ #include "support.h" +#include "generated/ScopeOps.h" #include "zv.h" #include @@ -1828,7 +1829,8 @@ void pt_scope_ops_rshutdown() void pt_register_scope_ops() { reg::Class cls("PHPStan\\Analyser\\ScopeOps"); - cls.final(); + ptdecl::ScopeOps::declareClass(cls); + ptdecl::ScopeOps::declareProperties(cls); cls.method("mergeVariableHolders", reg::PublicStatic, 2, { reg::arrayArg("ourVariableTypeHolders"), reg::arrayArg("theirVariableTypeHolders"), reg::any("differingKeys", true) }, [](INTERNAL_FUNCTION_PARAMETERS) { HashTable *ours, *theirs; diff --git a/turbo-ext/src/SymbolFinderInFiles.cpp b/turbo-ext/src/SymbolFinderInFiles.cpp index 3b49789b6e3..2e3ebfc8d71 100644 --- a/turbo-ext/src/SymbolFinderInFiles.cpp +++ b/turbo-ext/src/SymbolFinderInFiles.cpp @@ -19,6 +19,7 @@ */ #include "support.h" +#include "generated/SymbolFinderInFiles.h" #include "zv.h" #include "SymbolScan.h" @@ -183,7 +184,7 @@ zv::Val SymbolFinderInFiles::findSymbols(HashTable *files, bool supportsEnums) void pt_register_symbol_finder_in_files() { reg::Class cls("PHPStan\\Reflection\\BetterReflection\\SourceLocator\\SymbolFinderInFiles"); - cls.final(); + ptdecl::SymbolFinderInFiles::declareClass(cls); /* the arginfo has to keep the real parameter class name: Nette reflects * this constructor while compiling the container (rule 6) */ diff --git a/turbo-ext/src/TrinaryLogic.cpp b/turbo-ext/src/TrinaryLogic.cpp index 6e2143ffad1..de269506a94 100644 --- a/turbo-ext/src/TrinaryLogic.cpp +++ b/turbo-ext/src/TrinaryLogic.cpp @@ -11,6 +11,7 @@ */ #include "support.h" +#include "generated/TrinaryLogic.h" #include "zv.h" namespace phpstanturbo { @@ -314,7 +315,7 @@ static void pt_trinary_and_or(INTERNAL_FUNCTION_PARAMETERS, bool isAnd) void pt_register_trinary_logic() { reg::Class cls("PHPStan\\TrinaryLogic"); - cls.final(); + ptdecl::TrinaryLogic::declareClass(cls); /* "value" must stay the first declared property (OBJ_PROP_NUM slot 0) */ cls.privateLongProperty("value", 0); diff --git a/turbo-ext/src/TypeCombinatorCache.cpp b/turbo-ext/src/TypeCombinatorCache.cpp index 0aca82eee8c..99f4ba46766 100644 --- a/turbo-ext/src/TypeCombinatorCache.cpp +++ b/turbo-ext/src/TypeCombinatorCache.cpp @@ -58,6 +58,7 @@ */ #include "support.h" +#include "generated/TypeCombinatorCache.h" #include "zv.h" #include @@ -770,7 +771,8 @@ void pt_register_type_combinator_cache() static const char *TYPE_CLASS = "PHPStan\\Type\\Type"; reg::Class cls("PHPStan\\Type\\TypeCombinatorCache"); - cls.final(); + ptdecl::TypeCombinatorCache::declareClass(cls); + ptdecl::TypeCombinatorCache::declareProperties(cls); cls.method("union", reg::PublicStatic, 0, { reg::variadicObj("types", TYPE_CLASS) }, [](INTERNAL_FUNCTION_PARAMETERS) { zval *types; diff --git a/turbo-ext/src/generated/ArenaCache.h b/turbo-ext/src/generated/ArenaCache.h new file mode 100644 index 00000000000..c38c8b07738 --- /dev/null +++ b/turbo-ext/src/generated/ArenaCache.h @@ -0,0 +1,24 @@ +/* Generated by turbo-ext/bin/generate-declarations.php from + * src/Cache/ArenaCache.php — do not edit. */ + +#ifndef PHPSTANTURBO_GENERATED_ARENA_CACHE_H +#define PHPSTANTURBO_GENERATED_ARENA_CACHE_H + +#include "../reg.h" + +namespace ptdecl::ArenaCache { + +inline void declareClass(reg::Class &cls) +{ + cls.final(); +} + +/* the properties the class declares itself, in declaration order (a used trait's come from its registrar) */ +inline void declareProperties(reg::Class &cls) +{ + (void) cls; +} + +} // namespace ptdecl::ArenaCache + +#endif diff --git a/turbo-ext/src/generated/CombinationsHelper.h b/turbo-ext/src/generated/CombinationsHelper.h new file mode 100644 index 00000000000..2afaa4bdcff --- /dev/null +++ b/turbo-ext/src/generated/CombinationsHelper.h @@ -0,0 +1,24 @@ +/* Generated by turbo-ext/bin/generate-declarations.php from + * src/Internal/CombinationsHelper.php — do not edit. */ + +#ifndef PHPSTANTURBO_GENERATED_COMBINATIONS_HELPER_H +#define PHPSTANTURBO_GENERATED_COMBINATIONS_HELPER_H + +#include "../reg.h" + +namespace ptdecl::CombinationsHelper { + +inline void declareClass(reg::Class &cls) +{ + cls.final(); +} + +/* the properties the class declares itself, in declaration order (a used trait's come from its registrar) */ +inline void declareProperties(reg::Class &cls) +{ + (void) cls; +} + +} // namespace ptdecl::CombinationsHelper + +#endif diff --git a/turbo-ext/src/generated/ConditionalExpressionHolder.h b/turbo-ext/src/generated/ConditionalExpressionHolder.h new file mode 100644 index 00000000000..4112fc63f25 --- /dev/null +++ b/turbo-ext/src/generated/ConditionalExpressionHolder.h @@ -0,0 +1,31 @@ +/* Generated by turbo-ext/bin/generate-declarations.php from + * src/Analyser/ConditionalExpressionHolder.php — do not edit. */ + +#ifndef PHPSTANTURBO_GENERATED_CONDITIONAL_EXPRESSION_HOLDER_H +#define PHPSTANTURBO_GENERATED_CONDITIONAL_EXPRESSION_HOLDER_H + +#include "../reg.h" + +namespace ptdecl::ConditionalExpressionHolder { + +/* the OBJ_PROP_NUM slots of the instance properties the class declares (the inherited ones come first) */ +namespace slot { +inline constexpr uint32_t conditionExpressionTypeHolders = 0; +inline constexpr uint32_t typeHolder = 1; +} // namespace slot + +inline void declareClass(reg::Class &cls) +{ + cls.final(); +} + +/* the properties the class declares itself, in declaration order (a used trait's come from its registrar) */ +inline void declareProperties(reg::Class &cls) +{ + cls.property("conditionExpressionTypeHolders", ZEND_ACC_PRIVATE, reg::PropertyKind::Typed, MAY_BE_ARRAY); + cls.property("typeHolder", ZEND_ACC_PRIVATE, reg::PropertyKind::Typed, 0, "PHPStan\\Analyser\\ExpressionTypeHolder"); +} + +} // namespace ptdecl::ConditionalExpressionHolder + +#endif diff --git a/turbo-ext/src/generated/ExpressionResultStorage.h b/turbo-ext/src/generated/ExpressionResultStorage.h new file mode 100644 index 00000000000..5ce58770601 --- /dev/null +++ b/turbo-ext/src/generated/ExpressionResultStorage.h @@ -0,0 +1,31 @@ +/* Generated by turbo-ext/bin/generate-declarations.php from + * src/Analyser/ExpressionResultStorage.php — do not edit. */ + +#ifndef PHPSTANTURBO_GENERATED_EXPRESSION_RESULT_STORAGE_H +#define PHPSTANTURBO_GENERATED_EXPRESSION_RESULT_STORAGE_H + +#include "../reg.h" + +namespace ptdecl::ExpressionResultStorage { + +/* the OBJ_PROP_NUM slots of the instance properties the class declares (the inherited ones come first) */ +namespace slot { +inline constexpr uint32_t exprResults = 0; +inline constexpr uint32_t fallback = 1; +} // namespace slot + +inline void declareClass(reg::Class &cls) +{ + cls.final(); +} + +/* the properties the class declares itself, in declaration order (a used trait's come from its registrar) */ +inline void declareProperties(reg::Class &cls) +{ + cls.property("exprResults", ZEND_ACC_PRIVATE, reg::PropertyKind::Typed, 0, "SplObjectStorage"); + cls.property("fallback", ZEND_ACC_PRIVATE, reg::PropertyKind::TypedNull, MAY_BE_NULL, "PHPStan\\Analyser\\ExpressionResultStorage"); +} + +} // namespace ptdecl::ExpressionResultStorage + +#endif diff --git a/turbo-ext/src/generated/ExpressionTypeHolder.h b/turbo-ext/src/generated/ExpressionTypeHolder.h new file mode 100644 index 00000000000..10e014ad6ae --- /dev/null +++ b/turbo-ext/src/generated/ExpressionTypeHolder.h @@ -0,0 +1,33 @@ +/* Generated by turbo-ext/bin/generate-declarations.php from + * src/Analyser/ExpressionTypeHolder.php — do not edit. */ + +#ifndef PHPSTANTURBO_GENERATED_EXPRESSION_TYPE_HOLDER_H +#define PHPSTANTURBO_GENERATED_EXPRESSION_TYPE_HOLDER_H + +#include "../reg.h" + +namespace ptdecl::ExpressionTypeHolder { + +/* the OBJ_PROP_NUM slots of the instance properties the class declares (the inherited ones come first) */ +namespace slot { +inline constexpr uint32_t expr = 0; +inline constexpr uint32_t type = 1; +inline constexpr uint32_t certainty = 2; +} // namespace slot + +inline void declareClass(reg::Class &cls) +{ + cls.final(); +} + +/* the properties the class declares itself, in declaration order (a used trait's come from its registrar) */ +inline void declareProperties(reg::Class &cls) +{ + cls.property("expr", ZEND_ACC_PRIVATE | ZEND_ACC_READONLY, reg::PropertyKind::Typed, 0, "PhpParser\\Node\\Expr"); + cls.property("type", ZEND_ACC_PRIVATE | ZEND_ACC_READONLY, reg::PropertyKind::Typed, 0, "PHPStan\\Type\\Type"); + cls.property("certainty", ZEND_ACC_PRIVATE | ZEND_ACC_READONLY, reg::PropertyKind::Typed, 0, "PHPStan\\TrinaryLogic"); +} + +} // namespace ptdecl::ExpressionTypeHolder + +#endif diff --git a/turbo-ext/src/generated/NodeScanner.h b/turbo-ext/src/generated/NodeScanner.h new file mode 100644 index 00000000000..2ee82897651 --- /dev/null +++ b/turbo-ext/src/generated/NodeScanner.h @@ -0,0 +1,24 @@ +/* Generated by turbo-ext/bin/generate-declarations.php from + * src/Node/NodeScanner.php — do not edit. */ + +#ifndef PHPSTANTURBO_GENERATED_NODE_SCANNER_H +#define PHPSTANTURBO_GENERATED_NODE_SCANNER_H + +#include "../reg.h" + +namespace ptdecl::NodeScanner { + +inline void declareClass(reg::Class &cls) +{ + cls.final(); +} + +/* the properties the class declares itself, in declaration order (a used trait's come from its registrar) */ +inline void declareProperties(reg::Class &cls) +{ + (void) cls; +} + +} // namespace ptdecl::NodeScanner + +#endif diff --git a/turbo-ext/src/generated/NodeTraverser.h b/turbo-ext/src/generated/NodeTraverser.h new file mode 100644 index 00000000000..11c053c0c2d --- /dev/null +++ b/turbo-ext/src/generated/NodeTraverser.h @@ -0,0 +1,31 @@ +/* Generated by turbo-ext/bin/generate-declarations.php from + * vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php — do not edit. */ + +#ifndef PHPSTANTURBO_GENERATED_NODE_TRAVERSER_H +#define PHPSTANTURBO_GENERATED_NODE_TRAVERSER_H + +#include "../reg.h" + +namespace ptdecl::NodeTraverser { + +/* the OBJ_PROP_NUM slots of the instance properties the class declares (the inherited ones come first) */ +namespace slot { +inline constexpr uint32_t visitors = 0; +inline constexpr uint32_t stopTraversal = 1; +} // namespace slot + +inline void declareClass(reg::Class &cls) +{ + cls.implements({ "PhpParser\\NodeTraverserInterface" }); +} + +/* the properties the class declares itself, in declaration order (a used trait's come from its registrar) */ +inline void declareProperties(reg::Class &cls) +{ + cls.property("visitors", ZEND_ACC_PROTECTED, reg::PropertyKind::TypedEmptyArray, MAY_BE_ARRAY); + cls.property("stopTraversal", ZEND_ACC_PROTECTED, reg::PropertyKind::Typed, MAY_BE_BOOL); +} + +} // namespace ptdecl::NodeTraverser + +#endif diff --git a/turbo-ext/src/generated/ParserRunner.h b/turbo-ext/src/generated/ParserRunner.h new file mode 100644 index 00000000000..c6ed0845e8e --- /dev/null +++ b/turbo-ext/src/generated/ParserRunner.h @@ -0,0 +1,24 @@ +/* Generated by turbo-ext/bin/generate-declarations.php from + * src/Parser/ParserRunner.php — do not edit. */ + +#ifndef PHPSTANTURBO_GENERATED_PARSER_RUNNER_H +#define PHPSTANTURBO_GENERATED_PARSER_RUNNER_H + +#include "../reg.h" + +namespace ptdecl::ParserRunner { + +inline void declareClass(reg::Class &cls) +{ + cls.final(); +} + +/* the properties the class declares itself, in declaration order (a used trait's come from its registrar) */ +inline void declareProperties(reg::Class &cls) +{ + (void) cls; +} + +} // namespace ptdecl::ParserRunner + +#endif diff --git a/turbo-ext/src/generated/PhpFileCleaner.h b/turbo-ext/src/generated/PhpFileCleaner.h new file mode 100644 index 00000000000..894ed45d6e2 --- /dev/null +++ b/turbo-ext/src/generated/PhpFileCleaner.h @@ -0,0 +1,29 @@ +/* Generated by turbo-ext/bin/generate-declarations.php from + * src/Reflection/BetterReflection/SourceLocator/PhpFileCleaner.php — do not edit. */ + +#ifndef PHPSTANTURBO_GENERATED_PHP_FILE_CLEANER_H +#define PHPSTANTURBO_GENERATED_PHP_FILE_CLEANER_H + +#include "../reg.h" + +namespace ptdecl::PhpFileCleaner { + +/* the OBJ_PROP_NUM slots of the instance properties the class declares (the inherited ones come first) */ +namespace slot { +inline constexpr uint32_t typeConfig = 0; +inline constexpr uint32_t rejectChars = 1; +inline constexpr uint32_t contents = 2; +inline constexpr uint32_t len = 3; +inline constexpr uint32_t index = 4; +} // namespace slot + +inline void declareClass(reg::Class &cls) +{ + cls.final(); +} + +/* no declareProperties(): $contents: a typed default reg::Class cannot declare */ + +} // namespace ptdecl::PhpFileCleaner + +#endif diff --git a/turbo-ext/src/generated/ScopeOps.h b/turbo-ext/src/generated/ScopeOps.h new file mode 100644 index 00000000000..dd596312679 --- /dev/null +++ b/turbo-ext/src/generated/ScopeOps.h @@ -0,0 +1,24 @@ +/* Generated by turbo-ext/bin/generate-declarations.php from + * src/Analyser/ScopeOps.php — do not edit. */ + +#ifndef PHPSTANTURBO_GENERATED_SCOPE_OPS_H +#define PHPSTANTURBO_GENERATED_SCOPE_OPS_H + +#include "../reg.h" + +namespace ptdecl::ScopeOps { + +inline void declareClass(reg::Class &cls) +{ + cls.final(); +} + +/* the properties the class declares itself, in declaration order (a used trait's come from its registrar) */ +inline void declareProperties(reg::Class &cls) +{ + (void) cls; +} + +} // namespace ptdecl::ScopeOps + +#endif diff --git a/turbo-ext/src/generated/SymbolFinderInFiles.h b/turbo-ext/src/generated/SymbolFinderInFiles.h new file mode 100644 index 00000000000..e0da655b9e6 --- /dev/null +++ b/turbo-ext/src/generated/SymbolFinderInFiles.h @@ -0,0 +1,29 @@ +/* Generated by turbo-ext/bin/generate-declarations.php from + * src/Reflection/BetterReflection/SourceLocator/SymbolFinderInFiles.php — do not edit. */ + +#ifndef PHPSTANTURBO_GENERATED_SYMBOL_FINDER_IN_FILES_H +#define PHPSTANTURBO_GENERATED_SYMBOL_FINDER_IN_FILES_H + +#include "../reg.h" + +namespace ptdecl::SymbolFinderInFiles { + +/* the OBJ_PROP_NUM slots of the instance properties the class declares (the inherited ones come first) */ +namespace slot { +inline constexpr uint32_t cleaner = 0; +} // namespace slot + +inline void declareClass(reg::Class &cls) +{ + cls.final(); +} + +/* the properties the class declares itself, in declaration order (a used trait's come from its registrar) */ +inline void declareProperties(reg::Class &cls) +{ + cls.property("cleaner", ZEND_ACC_PRIVATE, reg::PropertyKind::Typed, 0, "PHPStan\\Reflection\\BetterReflection\\SourceLocator\\PhpFileCleaner"); +} + +} // namespace ptdecl::SymbolFinderInFiles + +#endif diff --git a/turbo-ext/src/generated/TrinaryLogic.h b/turbo-ext/src/generated/TrinaryLogic.h new file mode 100644 index 00000000000..4dae2e6a5a3 --- /dev/null +++ b/turbo-ext/src/generated/TrinaryLogic.h @@ -0,0 +1,33 @@ +/* Generated by turbo-ext/bin/generate-declarations.php from + * src/TrinaryLogic.php — do not edit. */ + +#ifndef PHPSTANTURBO_GENERATED_TRINARY_LOGIC_H +#define PHPSTANTURBO_GENERATED_TRINARY_LOGIC_H + +#include "../reg.h" + +namespace ptdecl::TrinaryLogic { + +/* the OBJ_PROP_NUM slots of the instance properties the class declares (the inherited ones come first) */ +namespace slot { +inline constexpr uint32_t value = 0; +} // namespace slot + +inline void declareClass(reg::Class &cls) +{ + cls.final(); +} + +/* the properties the class declares itself, in declaration order (a used trait's come from its registrar) */ +inline void declareProperties(reg::Class &cls) +{ + cls.property("registry", ZEND_ACC_PRIVATE | ZEND_ACC_STATIC, reg::PropertyKind::TypedEmptyArray, MAY_BE_ARRAY); + cls.property("YES", ZEND_ACC_PRIVATE | ZEND_ACC_STATIC, reg::PropertyKind::Typed, 0, "PHPStan\\TrinaryLogic"); + cls.property("MAYBE", ZEND_ACC_PRIVATE | ZEND_ACC_STATIC, reg::PropertyKind::Typed, 0, "PHPStan\\TrinaryLogic"); + cls.property("NO", ZEND_ACC_PRIVATE | ZEND_ACC_STATIC, reg::PropertyKind::Typed, 0, "PHPStan\\TrinaryLogic"); + cls.property("value", ZEND_ACC_PRIVATE, reg::PropertyKind::Typed, MAY_BE_LONG); +} + +} // namespace ptdecl::TrinaryLogic + +#endif diff --git a/turbo-ext/src/generated/TypeCombinatorCache.h b/turbo-ext/src/generated/TypeCombinatorCache.h new file mode 100644 index 00000000000..724ff48f757 --- /dev/null +++ b/turbo-ext/src/generated/TypeCombinatorCache.h @@ -0,0 +1,24 @@ +/* Generated by turbo-ext/bin/generate-declarations.php from + * src/Type/TypeCombinatorCache.php — do not edit. */ + +#ifndef PHPSTANTURBO_GENERATED_TYPE_COMBINATOR_CACHE_H +#define PHPSTANTURBO_GENERATED_TYPE_COMBINATOR_CACHE_H + +#include "../reg.h" + +namespace ptdecl::TypeCombinatorCache { + +inline void declareClass(reg::Class &cls) +{ + cls.final(); +} + +/* the properties the class declares itself, in declaration order (a used trait's come from its registrar) */ +inline void declareProperties(reg::Class &cls) +{ + (void) cls; +} + +} // namespace ptdecl::TypeCombinatorCache + +#endif diff --git a/turbo-ext/src/parser/ParserRunner.cpp b/turbo-ext/src/parser/ParserRunner.cpp index e21084c7143..7de13708f13 100644 --- a/turbo-ext/src/parser/ParserRunner.cpp +++ b/turbo-ext/src/parser/ParserRunner.cpp @@ -11,6 +11,7 @@ */ #include "ParserEngine.h" +#include "../generated/ParserRunner.h" #include "ParserRunnerActionsSplit.h" #pragma GCC diagnostic push @@ -1226,7 +1227,8 @@ using phpstanturbo::ParserEngine; void pt_register_parser_runner(void) { reg::Class cls("PHPStan\\Parser\\ParserRunner"); - cls.final(); + ptdecl::ParserRunner::declareClass(cls); + ptdecl::ParserRunner::declareProperties(cls); cls.method("parse", reg::PublicStatic, 3, { reg::objectArg("parser"), reg::stringArg("sourceCode"), reg::objectArg("errorHandler") }, [](INTERNAL_FUNCTION_PARAMETERS) { zval *parserObj, *code, *errorHandler; diff --git a/turbo-ext/src/reg.h b/turbo-ext/src/reg.h index 8e44c2f6e60..8fd980c0e8e 100644 --- a/turbo-ext/src/reg.h +++ b/turbo-ext/src/reg.h @@ -308,7 +308,13 @@ enum class PropertyKind Null, Bool, EmptyArray, - PublicReadonlyTyped, /* UNDEF default, defaultValue carries the MAY_BE_* type mask */ + Typed, /* a typed property with no default (UNDEF, IS_PROP_UNINIT); defaultValue carries the MAY_BE_* type mask, visibility the ZEND_ACC_* flags */ + TypedNull, /* a typed property defaulting to null (`private ?Foo $x = null`); defaultValue as for Typed */ + TypedEmptyArray, /* a typed property defaulting to [] (`private array $x = []`); defaultValue as for Typed */ + TypedClassUnion, /* a `private Foo|Bar $x` union-of-classes typed property with no default; className carries the `|`-separated names, defaultValue as for Typed */ + TypedBool, /* a `private bool $x = false` typed property with a bool default; defaultValue carries the default (0/1), the type is bool */ + TypedLong, /* a `private int $x = 0` typed property with an int default; defaultValue carries the default, the type is int */ + TypedFalse, /* a typed property defaulting to false (`private string|false|null $x = false`, `private Foo|false|null $x = false`); defaultValue carries the MAY_BE_* mask (the scalar members next to a class name), className as for Typed */ }; struct Property @@ -317,6 +323,7 @@ struct Property PropertyKind kind; uint32_t visibility; zend_long defaultValue; + const char *className = nullptr; /* persistent literal: the class of a class-typed property (Typed* kinds), combined with a MAY_BE_NULL bit in defaultValue for `?Foo` */ }; struct Constant @@ -368,12 +375,84 @@ inline void declareMembers(zend_class_entry *ce, const std::vector &pr zend_declare_property(ce, property.name, len, &emptyArray, property.visibility); break; } - case PropertyKind::PublicReadonlyTyped: { - zend_string *nameStr = zend_string_init(property.name, len, ce->type == ZEND_INTERNAL_CLASS); - zval undef; - ZVAL_UNDEF(&undef); - zend_type type = ZEND_TYPE_INIT_MASK((uint32_t) property.defaultValue); - zend_declare_typed_property(ce, nameStr, &undef, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY, NULL, type); + case PropertyKind::Typed: + case PropertyKind::TypedNull: + case PropertyKind::TypedEmptyArray: + case PropertyKind::TypedClassUnion: + case PropertyKind::TypedFalse: { + bool persistent = ce->type == ZEND_INTERNAL_CLASS; + zend_string *nameStr = zend_string_init(property.name, len, persistent); + zval defaultValue; + if (property.kind == PropertyKind::TypedNull) { + ZVAL_NULL(&defaultValue); + } else if (property.kind == PropertyKind::TypedEmptyArray) { + ZVAL_EMPTY_ARRAY(&defaultValue); + } else if (property.kind == PropertyKind::TypedFalse) { + ZVAL_FALSE(&defaultValue); + } else { + ZVAL_UNDEF(&defaultValue); + } + zend_type type; + if (property.kind == PropertyKind::TypedClassUnion) { + /* a `Foo|Bar` union of class names, declared the way the + * compiler declares one: a type list of interned names + * with class-entry cache slots (released with the class + * by zend_type_release() — allocated with its + * persistence) */ + uint32_t count = 1; + for (const char *p = property.className; (p = strchr(p, '|')) != NULL; p++) { + count++; + } + zend_type_list *list = (zend_type_list *) pemalloc(ZEND_TYPE_LIST_SIZE(count), persistent); + list->num_types = count; + const char *start = property.className; + for (uint32_t i = 0; i < count; i++) { + const char *end = strchr(start, '|'); + size_t partLen = end != NULL ? (size_t) (end - start) : strlen(start); + zend_string *className = zend_new_interned_string(zend_string_init(start, partLen, persistent)); + zend_alloc_ce_cache(className); + list->types[i] = (zend_type) ZEND_TYPE_INIT_CLASS(className, 0, 0); + start = end != NULL ? end + 1 : start; + } + type = (zend_type) ZEND_TYPE_INIT_UNION(list, (property.defaultValue & MAY_BE_NULL) != 0 ? MAY_BE_NULL : 0); + } else if (property.className != NULL) { + /* a class-typed property, declared the way the compiler + * declares one (zend_compile_single_typename): an interned + * name with a class-entry cache slot; the engine dups it + * for a persistent class. "self" is the declared class, + * as the compiler resolves it (the twin's `private static + * self $x`) */ + zend_string *className = strcmp(property.className, "self") == 0 + ? zend_string_copy(ce->name) + : zend_new_interned_string(zend_string_init(property.className, strlen(property.className), persistent)); + zend_alloc_ce_cache(className); + /* the bits beyond MAY_BE_NULL in defaultValue are the scalar + * members of a `Foo|false|null` union */ + type = (zend_type) ZEND_TYPE_INIT_CLASS(className, (property.defaultValue & MAY_BE_NULL) != 0, (uint32_t) property.defaultValue & ~(uint32_t) MAY_BE_NULL); + } else { + type = (zend_type) ZEND_TYPE_INIT_MASK((uint32_t) property.defaultValue); + } + zend_declare_typed_property(ce, nameStr, &defaultValue, property.visibility, NULL, type); + zend_string_release(nameStr); + break; + } + case PropertyKind::TypedBool: { + bool persistent = ce->type == ZEND_INTERNAL_CLASS; + zend_string *nameStr = zend_string_init(property.name, len, persistent); + zval defaultValue; + ZVAL_BOOL(&defaultValue, property.defaultValue != 0); + zend_type type = (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL); + zend_declare_typed_property(ce, nameStr, &defaultValue, property.visibility, NULL, type); + zend_string_release(nameStr); + break; + } + case PropertyKind::TypedLong: { + bool persistent = ce->type == ZEND_INTERNAL_CLASS; + zend_string *nameStr = zend_string_init(property.name, len, persistent); + zval defaultValue; + ZVAL_LONG(&defaultValue, property.defaultValue); + zend_type type = (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG); + zend_declare_typed_property(ce, nameStr, &defaultValue, property.visibility, NULL, type); zend_string_release(nameStr); break; } @@ -630,7 +709,7 @@ class Class * MAY_BE_* mask */ Class &publicReadonlyProperty(const char *propertyName, uint32_t typeMask) { - properties.push_back({ propertyName, PropertyKind::PublicReadonlyTyped, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY, (zend_long) typeMask }); + properties.push_back({ propertyName, PropertyKind::Typed, ZEND_ACC_PUBLIC | ZEND_ACC_READONLY, (zend_long) typeMask }); return *this; } @@ -680,6 +759,21 @@ class Class pt_shadow_plan_add(std::move(plan)); } + /* a typed property of any visibility and shape — the builders above + * are the common cases; this spells the twin's declaration directly: + * visibility is the ZEND_ACC_* flags (ZEND_ACC_PRIVATE | ZEND_ACC_STATIC, + * ZEND_ACC_PUBLIC | ZEND_ACC_READONLY, ...), kind the shape, mask the + * MAY_BE_* type mask (with MAY_BE_NULL for a nullable class type; the + * bool/int default for TypedBool/TypedLong), className the persistent + * literal of a class-typed property ("self" for the declared class) or + * NULL. Promoted constructor properties are declared this way: they + * never carry the parameter's default, so their kind is Typed. */ + Class &property(const char *propertyName, uint32_t visibility, PropertyKind kind, zend_long mask, const char *className = nullptr) + { + properties.push_back({ propertyName, kind, visibility, mask, className }); + return *this; + } + private: const char *name; uint32_t flags = 0; From 17800d7768aa22421534d341fd9b943b8d27b7f2 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Tue, 15 Sep 2026 23:51:31 +0200 Subject: [PATCH 08/12] Use the generated property slots in the native classes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The numeric #define PT_*_PROP_* slot macros of the native classes become the generated slot constants (ptdecl::::slot, aliased as slots::) — mapped by index, so every object compiles byte-identical. A slot number now follows the twin's declaration instead of being counted by hand. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01P2Qsp9sqyymJYoqgoMkgLT --- turbo-ext/src/ExpressionResultStorage.cpp | 18 +++++++++--------- turbo-ext/src/NodeTraverser.cpp | 13 ++++++------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/turbo-ext/src/ExpressionResultStorage.cpp b/turbo-ext/src/ExpressionResultStorage.cpp index 985ceea1af2..ab094cef4ee 100644 --- a/turbo-ext/src/ExpressionResultStorage.cpp +++ b/turbo-ext/src/ExpressionResultStorage.cpp @@ -17,11 +17,11 @@ */ #include "generated/ExpressionResultStorage.h" + +namespace slots = ptdecl::ExpressionResultStorage::slot; #include "support.h" #include "zv.h" -#define PT_ERS_PROP_EXPRS 0 -#define PT_ERS_PROP_RESULTS 1 #define PT_ERS_PROP_FALLBACK 2 namespace phpstanturbo { @@ -45,12 +45,12 @@ class ExpressionResultStorage { zv::ObjRef src(other); zv::ObjRef dst(self); - zv::ArrRef dstExprs(dst.propAt(PT_ERS_PROP_EXPRS).raw()); - zv::ArrRef dstResults(dst.propAt(PT_ERS_PROP_RESULTS).raw()); - for (auto entry : zv::ArrRef(src.propAt(PT_ERS_PROP_EXPRS).raw())) { + zv::ArrRef dstExprs(dst.propAt(slots::exprResults).raw()); + zv::ArrRef dstResults(dst.propAt(slots::fallback).raw()); + for (auto entry : zv::ArrRef(src.propAt(slots::exprResults).raw())) { dstExprs.setIndex(entry.indexKey(), entry.value()); } - for (auto entry : zv::ArrRef(src.propAt(PT_ERS_PROP_RESULTS).raw())) { + for (auto entry : zv::ArrRef(src.propAt(slots::fallback).raw())) { dstResults.setIndex(entry.indexKey(), entry.value()); } } @@ -59,8 +59,8 @@ class ExpressionResultStorage { zend_ulong id = Z_OBJ_HANDLE_P(expr); zv::ObjRef obj(self); - zv::ArrRef(obj.propAt(PT_ERS_PROP_EXPRS).raw()).setIndex(id, zv::Ref(expr)); - zv::ArrRef(obj.propAt(PT_ERS_PROP_RESULTS).raw()).setIndex(id, zv::Ref(expressionResult)); + zv::ArrRef(obj.propAt(slots::exprResults).raw()).setIndex(id, zv::Ref(expr)); + zv::ArrRef(obj.propAt(slots::fallback).raw()).setIndex(id, zv::Ref(expressionResult)); } zv::Val findExpressionResult(zval *expr) const @@ -69,7 +69,7 @@ class ExpressionResultStorage zval *cur = self; for (;;) { zv::ObjRef obj(cur); - zv::Ref found = zv::ArrRef(obj.propAt(PT_ERS_PROP_RESULTS).raw()).findIndex(id); + zv::Ref found = zv::ArrRef(obj.propAt(slots::fallback).raw()).findIndex(id); if (found.raw() != NULL) return zv::Val::copyOf(found); /* the twin recurses into ?self $fallback; iterate the chain */ zval *fallback = obj.propAt(PT_ERS_PROP_FALLBACK).raw(); diff --git a/turbo-ext/src/NodeTraverser.cpp b/turbo-ext/src/NodeTraverser.cpp index 6c1b4ea5b90..c9bbff53ea6 100644 --- a/turbo-ext/src/NodeTraverser.cpp +++ b/turbo-ext/src/NodeTraverser.cpp @@ -15,13 +15,12 @@ #include "support.h" #include "generated/NodeTraverser.h" + +namespace slots = ptdecl::NodeTraverser::slot; #include "zv.h" static zend_class_entry *pt_ce_node_traverser; -#define PT_NT_PROP_VISITORS 0 -#define PT_NT_PROP_STOP 1 - /* {{{ pt_* traversal substrate */ /* Per-visitor call plan built once per traverse(): the visitor object and @@ -221,7 +220,7 @@ class NodeTraverser } list.push(visitor); } - zv::ObjRef(self).propAtWrite(PT_NT_PROP_VISITORS, std::move(list)); + zv::ObjRef(self).propAtWrite(slots::visitors, std::move(list)); return true; } @@ -273,7 +272,7 @@ class NodeTraverser zv::ObjRef selfObj(self); /* $this->stopTraversal = false */ - selfObj.propAtWrite(PT_NT_PROP_STOP, zv::Val::boolean(false)); + selfObj.propAtWrite(slots::stopTraversal, zv::Val::boolean(false)); if (UNEXPECTED(!buildVisitorPlan())) return zv::Val(); @@ -311,7 +310,7 @@ class NodeTraverser } /* persist stopTraversal like the PHP implementation */ - selfObj.propAtWrite(PT_NT_PROP_STOP, zv::Val::boolean(stop)); + selfObj.propAtWrite(slots::stopTraversal, zv::Val::boolean(stop)); return zv::Val(std::move(nodes)); } @@ -779,7 +778,7 @@ class NodeTraverser zv::Ref visitorsProp() const { - return zv::ObjRef(self).propAt(PT_NT_PROP_VISITORS).deref(); + return zv::ObjRef(self).propAt(slots::visitors).deref(); } zend_object *self; From 28ec01d53a3c8dbb370c38886a613a94ac2e7490 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Wed, 16 Sep 2026 00:04:02 +0200 Subject: [PATCH 09/12] Register the native methods by their generated signatures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The declaration generator also emits each method's signature (name, flags, required count, arginfo, return type) into the class's generated header, and a header per trait the shadowed classes use. Registrations take them — cls.method(sigs::accepts, handler), cls.method<&Handle::accepts, zp::Obj, zp::Bool>(sigs::accepts), cls.traitMethod(sigs::isArray, handler) — instead of spelling names, flags, parameter descriptors and return types out; a bound registration whose parameter kinds no longer match its signature refuses to load the module. The generated signatures are constexpr, and so are the reg::Arg builders they call. Methods of classes without a PHP twin, and those whose native signature deliberately differs from the twin's, keep their hand-written arginfo. The reflection dump of every native class is identical before and after; side-by-side.php resolves sigs:: names through the headers. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01P2Qsp9sqyymJYoqgoMkgLT --- .../Build/TurboDeclarationGenerator.php | 226 ++++++++++++++++++ turbo-ext/CLAUDE.md | 7 +- turbo-ext/bin/side-by-side.php | 36 ++- turbo-ext/src/ExpressionResultStorage.cpp | 3 +- turbo-ext/src/NodeTraverser.cpp | 18 +- turbo-ext/src/PhpFileCleaner.cpp | 4 +- turbo-ext/src/SymbolFinderInFiles.cpp | 4 +- turbo-ext/src/TrinaryLogic.cpp | 4 +- turbo-ext/src/generated/ArenaCache.h | 32 +++ turbo-ext/src/generated/CombinationsHelper.h | 7 + .../generated/ConditionalExpressionHolder.h | 12 + .../src/generated/ExpressionResultStorage.h | 16 ++ .../src/generated/ExpressionTypeHolder.h | 27 +++ turbo-ext/src/generated/NodeScanner.h | 7 + turbo-ext/src/generated/NodeTraverser.h | 24 ++ turbo-ext/src/generated/ParserRunner.h | 7 + turbo-ext/src/generated/PhpFileCleaner.h | 29 +++ turbo-ext/src/generated/ScopeOps.h | 58 +++++ turbo-ext/src/generated/SymbolFinderInFiles.h | 15 ++ turbo-ext/src/generated/TrinaryLogic.h | 60 +++++ turbo-ext/src/generated/TypeCombinatorCache.h | 15 ++ turbo-ext/src/reg.h | 86 +++++-- 22 files changed, 659 insertions(+), 38 deletions(-) diff --git a/build/PHPStan/Build/TurboDeclarationGenerator.php b/build/PHPStan/Build/TurboDeclarationGenerator.php index 98e3c9751d5..b07d02b1619 100644 --- a/build/PHPStan/Build/TurboDeclarationGenerator.php +++ b/build/PHPStan/Build/TurboDeclarationGenerator.php @@ -4,26 +4,37 @@ use ReflectionClass; use ReflectionIntersectionType; +use ReflectionMethod; use ReflectionNamedType; +use ReflectionParameter; use ReflectionProperty; use ReflectionType; use ReflectionUnionType; use RuntimeException; use function array_filter; +use function array_is_list; +use function array_keys; use function array_map; use function array_values; use function basename; use function class_exists; use function count; +use function dirname; use function implode; use function in_array; +use function is_array; use function is_bool; use function is_int; +use function is_string; use function ksort; use function preg_replace; use function sprintf; use function str_replace; +use function str_starts_with; +use function strlen; use function strtoupper; +use function substr; +use function var_export; /** * Derives the declarative half of every shadowing class from its PHP twin @@ -81,6 +92,25 @@ public function render(): array } $files['turbo-ext/src/generated/' . $stem . '.h'] = $this->renderClass(new ReflectionClass($className), $entry['php'], $stem); } + + // the traits the shadowed classes use, for the shared trait registrars + $traits = []; + $collect = static function (ReflectionClass $classLike) use (&$collect, &$traits): void { + foreach ($classLike->getTraits() as $trait) { + $traits[$trait->getName()] = $trait; + $collect($trait); + } + }; + foreach (array_keys($this->manifest) as $className) { + $collect(new ReflectionClass($className)); + } + foreach ($traits as $trait) { + $path = 'turbo-ext/src/generated/' . $trait->getShortName() . '.h'; + if (isset($files[$path])) { + throw new RuntimeException(sprintf('%s collides with a generated class header', $path)); + } + $files[$path] = $this->renderTrait($trait); + } ksort($files); return $files; @@ -183,6 +213,51 @@ private function renderClass(ReflectionClass $class, string $phpFile, string $st $out[] = '}'; } + $signatures = $this->renderSignatures($class); + if ($signatures !== []) { + $out[] = ''; + $out[] = '/* the signatures of the methods the class declares itself (a used trait\'s are in the trait\'s header) */'; + $out[] = 'namespace sig {'; + foreach ($signatures as $line) { + $out[] = $line; + } + $out[] = '} // namespace sig'; + } + + $out[] = ''; + $out[] = '} // namespace ptdecl::' . $stem; + $out[] = ''; + $out[] = '#endif'; + $out[] = ''; + + return implode("\n", $out); + } + + /** + * @param ReflectionClass $trait + */ + private function renderTrait(ReflectionClass $trait): string + { + $stem = $trait->getShortName(); + $guard = 'PHPSTANTURBO_GENERATED_' . strtoupper((string) preg_replace('~(?<=[a-z0-9])(?=[A-Z])~', '_', $stem)) . '_H'; + $out = [ + '/* Generated by turbo-ext/bin/generate-declarations.php from', + ' * ' . $this->relativeFile((string) $trait->getFileName()) . ' — do not edit. */', + '', + '#ifndef ' . $guard, + '#define ' . $guard, + '', + '#include "../reg.h"', + '', + 'namespace ptdecl::' . $stem . ' {', + '', + '/* the signatures of the methods the trait declares itself */', + 'namespace sig {', + ]; + foreach ($this->renderSignatures($trait) as $line) { + $out[] = $line; + } + $out[] = '} // namespace sig'; $out[] = ''; $out[] = '} // namespace ptdecl::' . $stem; $out[] = ''; @@ -192,6 +267,157 @@ private function renderClass(ReflectionClass $class, string $phpFile, string $st return implode("\n", $out); } + /** + * The generated signature of every method declared in the class-like's + * own file (a used trait's methods are declared in the trait's). + * + * @param ReflectionClass $class + * @return list + */ + private function renderSignatures(ReflectionClass $class): array + { + $lines = []; + foreach ($class->getMethods() as $method) { + if ($method->getDeclaringClass()->getName() !== $class->getName() || $method->getFileName() !== $class->getFileName()) { + continue; + } + $id = $this->cName($method->getName()); + try { + $args = []; + foreach ($method->getParameters() as $parameter) { + [$mask, $className] = $this->signatureType($parameter->getType()); + $default = $parameter->isDefaultValueAvailable() ? $this->defaultSource($parameter) : null; + $pieces = [$this->cString($parameter->getName()), $mask]; + if ($className !== null || $parameter->isPassedByReference() || $parameter->isVariadic() || $default !== null) { + $pieces[] = $className !== null ? $this->cString($className) : 'nullptr'; + } + if ($parameter->isPassedByReference() || $parameter->isVariadic() || $default !== null) { + $pieces[] = $parameter->isPassedByReference() ? 'true' : 'false'; + $pieces[] = $parameter->isVariadic() ? 'true' : 'false'; + } + if ($default !== null) { + $pieces[] = $this->cString($default); + } + $args[] = 'reg::typed(' . implode(', ', $pieces) . ')'; + } + $returnType = $method->getReturnType() ?? $method->getTentativeReturnType(); + $return = null; + if ($returnType !== null) { + [$mask, $className] = $this->signatureType($returnType); + $return = sprintf('reg::typed("", %s%s)', $mask, $className !== null ? ', ' . $this->cString($className) : ''); + } + } catch (RuntimeException $e) { + $lines[] = sprintf('/* %s(): no signature — %s */', $method->getName(), $e->getMessage()); + continue; + } + if ($args !== []) { + $lines[] = sprintf('inline constexpr reg::Arg %s_args[] = { %s };', $id, implode(', ', $args)); + } + if ($return !== null) { + $lines[] = sprintf('inline constexpr reg::Arg %s_return = %s;', $id, $return); + } + $lines[] = sprintf( + 'inline constexpr reg::Sig %s = { %s, %s, %d, %s, %d, %s };', + $id, + $this->cString($method->getName()), + $this->methodFlags($method), + $method->getNumberOfRequiredParameters(), + $args !== [] ? $id . '_args' : 'nullptr', + count($args), + $return !== null ? '&' . $id . '_return' : 'nullptr', + ); + } + + return $lines; + } + + private function methodFlags(ReflectionMethod $method): string + { + $flags = [$method->isPrivate() ? 'ZEND_ACC_PRIVATE' : ($method->isProtected() ? 'ZEND_ACC_PROTECTED' : 'ZEND_ACC_PUBLIC')]; + if ($method->isStatic()) { + $flags[] = 'ZEND_ACC_STATIC'; + } + if ($method->isFinal()) { + $flags[] = 'ZEND_ACC_FINAL'; + } + if ($method->isAbstract()) { + $flags[] = 'ZEND_ACC_ABSTRACT'; + } + + return implode(' | ', $flags); + } + + /** + * @return array{string, string|null} MAY_BE_* mask expression, literal class name(s) + */ + private function signatureType(?ReflectionType $type): array + { + if ($type === null) { + return ['0', null]; + } + if ($type instanceof ReflectionIntersectionType) { + throw new RuntimeException('an intersection type'); + } + $builtin = [ + 'int' => 'MAY_BE_LONG', 'float' => 'MAY_BE_DOUBLE', 'string' => 'MAY_BE_STRING', 'bool' => 'MAY_BE_BOOL', + 'array' => 'MAY_BE_ARRAY', 'null' => 'MAY_BE_NULL', 'false' => 'MAY_BE_FALSE', 'true' => 'MAY_BE_TRUE', + 'mixed' => 'MAY_BE_ANY', 'object' => 'MAY_BE_OBJECT', 'callable' => 'MAY_BE_CALLABLE', 'iterable' => '_ZEND_TYPE_ITERABLE_BIT', + 'void' => 'MAY_BE_VOID', 'never' => 'MAY_BE_NEVER', 'static' => 'MAY_BE_STATIC', + ]; + $members = $type instanceof ReflectionUnionType ? $type->getTypes() : [$type]; + $masks = []; + $classes = []; + foreach ($members as $member) { + if (!$member instanceof ReflectionNamedType) { + throw new RuntimeException('a nested intersection in a union'); + } + if (isset($builtin[$member->getName()])) { + $masks[$builtin[$member->getName()]] = true; + } else { + $classes[] = $member->getName(); + } + } + if ($type instanceof ReflectionNamedType && $type->allowsNull() && !in_array($type->getName(), ['mixed', 'null'], true)) { + $masks['MAY_BE_NULL'] = true; + } + $order = ['MAY_BE_NULL', 'MAY_BE_FALSE', 'MAY_BE_TRUE', 'MAY_BE_BOOL', 'MAY_BE_LONG', 'MAY_BE_DOUBLE', 'MAY_BE_STRING', 'MAY_BE_ARRAY', 'MAY_BE_OBJECT', 'MAY_BE_CALLABLE', '_ZEND_TYPE_ITERABLE_BIT', 'MAY_BE_VOID', 'MAY_BE_NEVER', 'MAY_BE_STATIC', 'MAY_BE_ANY']; + $ordered = array_values(array_filter($order, static fn (string $m): bool => isset($masks[$m]))); + + return [$ordered === [] ? '0' : implode(' | ', $ordered), $classes === [] ? null : implode('|', $classes)]; + } + + private function defaultSource(ReflectionParameter $parameter): string + { + if ($parameter->isDefaultValueConstant()) { + return (string) $parameter->getDefaultValueConstantName(); + } + $value = $parameter->getDefaultValue(); + if ($value === null) { + return 'null'; + } + if (is_bool($value)) { + return $value ? 'true' : 'false'; + } + if (is_int($value)) { + return (string) $value; + } + if (is_string($value)) { + return var_export($value, true); + } + if (is_array($value) && array_is_list($value) && array_filter($value, static fn ($item): bool => !is_int($item) && !is_bool($item)) === []) { + return '[' . implode(', ', array_map(static fn ($item): string => var_export($item, true), $value)) . ']'; + } + + throw new RuntimeException('a default value the arginfo source cannot spell'); + } + + private function relativeFile(string $path): string + { + $root = dirname(__DIR__, 3) . '/'; + + return str_starts_with($path, $root) ? substr($path, strlen($root)) : $path; + } + /** * The instance properties in OBJ_PROP_NUM order: the parent's first, then * the class's own in declaration order (a used trait's after them). diff --git a/turbo-ext/CLAUDE.md b/turbo-ext/CLAUDE.md index 2aa2a67dc74..fff312abda6 100644 --- a/turbo-ext/CLAUDE.md +++ b/turbo-ext/CLAUDE.md @@ -81,8 +81,11 @@ being ≥0.5% faster is. When the estimate is marginal, don't port. writes `turbo-ext/src/generated/.h` from the twin — `declareClass(cls)` (final/abstract, parent, the directly implemented interfaces), `declareProperties(cls)` (the twin's own properties, exactly) and the - `slot::` constants of its instance properties. Call both first in the - registration function instead of spelling them out; side-by-side.php + `slot::` constants of its instance properties, and `sig::` — each + method's name, flags, arginfo and return type. Call both functions first + in the registration function and register the methods by signature + (`cls.method(sigs::accepts, handler)`, `cls.method<&Handle::accepts, + zp::Obj, zp::Bool>(sigs::accepts)`) instead of spelling them out; side-by-side.php fails while a header is stale. A class whose native properties deliberately differ from the twin keeps declaring them by hand. 6. **Check method parity**: `php bin/side-by-side.php` must pass (it also diff --git a/turbo-ext/bin/side-by-side.php b/turbo-ext/bin/side-by-side.php index 4bc52711740..1ba3f953a18 100644 --- a/turbo-ext/bin/side-by-side.php +++ b/turbo-ext/bin/side-by-side.php @@ -143,6 +143,31 @@ function parsePhpMethods(string $file): array return $methods; } +/** + * The method names behind the sig:: identifiers of a generated header + * (turbo-ext/src/generated/.h): identifier => PHP method name. + * + * @return array + */ +function generatedSignatureNames(string $stem): array +{ + static $cache = []; + if (isset($cache[$stem])) { + return $cache[$stem]; + } + $header = 'turbo-ext/src/generated/' . $stem . '.h'; + if (!is_file($header)) { + throw new RuntimeException(sprintf('%s does not exist — run php turbo-ext/bin/generate-declarations.php', $header)); + } + preg_match_all('~inline constexpr reg::Sig (\w+) = \{ "(\w+)"~', file_get_contents($header), $m, PREG_SET_ORDER); + $names = []; + foreach ($m as [, $identifier, $name]) { + $names[$identifier] = $name; + } + + return $cache[$stem] = $names; +} + /** * @return array * PHP_METHOD implementations, in source order @@ -167,12 +192,21 @@ function parseCppMethods(string $file): array } } + // registrations by generated signature name their method through the + // file's `namespace sigs = ptdecl::::sig;` alias + $signatures = preg_match('/^namespace sigs = ptdecl::(\w+)::sig;$/m', file_get_contents($file), $alias) === 1 + ? generatedSignatureNames($alias[1]) + : []; + foreach ($lines as $i => $lineText) { if ( preg_match('/^\s*(?:static\s+)?PHP_METHOD\(\s*\w+\s*,\s*(\w+)\s*\)/', $lineText, $m) !== 1 && preg_match('/^\s*(?:cls\.|\.)method\("(\w+)"/', $lineText, $m) !== 1 ) { - continue; + if (preg_match('/^\s*cls\.(?:method|traitMethod)(?:<[^(]*>)?\(sigs::(\w+)/', $lineText, $sm) !== 1) { + continue; + } + $m = [1 => $signatures[$sm[1]] ?? $sm[1]]; } // prefer the handle-class member of the same (or underscore-suffixed) name if (isset($handleClassMembers[$m[1]])) { diff --git a/turbo-ext/src/ExpressionResultStorage.cpp b/turbo-ext/src/ExpressionResultStorage.cpp index ab094cef4ee..9de45c0b046 100644 --- a/turbo-ext/src/ExpressionResultStorage.cpp +++ b/turbo-ext/src/ExpressionResultStorage.cpp @@ -19,6 +19,7 @@ #include "generated/ExpressionResultStorage.h" namespace slots = ptdecl::ExpressionResultStorage::slot; +namespace sigs = ptdecl::ExpressionResultStorage::sig; #include "support.h" #include "zv.h" @@ -102,7 +103,7 @@ void pt_register_expression_result_storage() /* the twin's constructor only initialized its SplObjectStorage; the * native property defaults already cover that */ - cls.method("__construct", reg::Public, 0, {}, [](INTERNAL_FUNCTION_PARAMETERS) { + cls.method(sigs::__construct, [](INTERNAL_FUNCTION_PARAMETERS) { ZEND_PARSE_PARAMETERS_NONE(); }); diff --git a/turbo-ext/src/NodeTraverser.cpp b/turbo-ext/src/NodeTraverser.cpp index c9bbff53ea6..846e124b203 100644 --- a/turbo-ext/src/NodeTraverser.cpp +++ b/turbo-ext/src/NodeTraverser.cpp @@ -17,6 +17,7 @@ #include "generated/NodeTraverser.h" namespace slots = ptdecl::NodeTraverser::slot; +namespace sigs = ptdecl::NodeTraverser::sig; #include "zv.h" static zend_class_entry *pt_ce_node_traverser; @@ -813,7 +814,7 @@ void pt_register_node_traverser() cls.classConstantLong("REMOVE_NODE", NodeTraverser::REMOVE_NODE); cls.classConstantLong("DONT_TRAVERSE_CURRENT_AND_CHILDREN", NodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN); - cls.method("__construct", reg::Public, 0, { reg::variadicObj("visitors", NODE_VISITOR_CLASS) }, [](INTERNAL_FUNCTION_PARAMETERS) { + cls.method(sigs::__construct, [](INTERNAL_FUNCTION_PARAMETERS) { zval *visitors = NULL; uint32_t count = 0; ZEND_PARSE_PARAMETERS_START(0, -1) @@ -823,29 +824,26 @@ void pt_register_node_traverser() if (UNEXPECTED(!self.construct(visitors, count))) RETURN_THROWS(); }); - static const reg::Arg voidReturn = { "", MAY_BE_VOID, nullptr }; - static const reg::Arg arrayReturn = reg::arrayArg(""); - - cls.method("addVisitor", reg::Public, 1, { reg::obj("visitor", NODE_VISITOR_CLASS) }, [](INTERNAL_FUNCTION_PARAMETERS) { + cls.method(sigs::addVisitor, [](INTERNAL_FUNCTION_PARAMETERS) { zval *visitor; if (!zp::parse(execute_data, visitor)) RETURN_THROWS(); NodeTraverser(Z_OBJ_P(ZEND_THIS)).addVisitor(zv::Ref(visitor)); - }, &voidReturn); + }); - cls.method("removeVisitor", reg::Public, 1, { reg::obj("visitor", NODE_VISITOR_CLASS) }, [](INTERNAL_FUNCTION_PARAMETERS) { + cls.method(sigs::removeVisitor, [](INTERNAL_FUNCTION_PARAMETERS) { zval *visitor; if (!zp::parse(execute_data, visitor)) RETURN_THROWS(); NodeTraverser(Z_OBJ_P(ZEND_THIS)).removeVisitor(zv::Ref(visitor)); - }, &voidReturn); + }); - cls.method("traverse", reg::Public, 1, { reg::arrayArg("nodes") }, [](INTERNAL_FUNCTION_PARAMETERS) { + cls.method(sigs::traverse, [](INTERNAL_FUNCTION_PARAMETERS) { HashTable *nodes; if (!zp::parse(execute_data, nodes)) RETURN_THROWS(); NodeTraverser self(Z_OBJ_P(ZEND_THIS)); zv::Val result = self.traverse(nodes); if (UNEXPECTED(result.isUndef())) RETURN_THROWS(); result.intoReturnValue(return_value); - }, &arrayReturn); + }); cls.shadow(&pt_ce_node_traverser); } diff --git a/turbo-ext/src/PhpFileCleaner.cpp b/turbo-ext/src/PhpFileCleaner.cpp index db006df4487..08085a87204 100644 --- a/turbo-ext/src/PhpFileCleaner.cpp +++ b/turbo-ext/src/PhpFileCleaner.cpp @@ -12,6 +12,8 @@ #include "support.h" #include "generated/PhpFileCleaner.h" + +namespace sigs = ptdecl::PhpFileCleaner::sig; #include "zv.h" #include "SymbolScan.h" @@ -26,7 +28,7 @@ void pt_register_php_file_cleaner() reg::Class cls("PHPStan\\Reflection\\BetterReflection\\SourceLocator\\PhpFileCleaner"); ptdecl::PhpFileCleaner::declareClass(cls); - cls.method("__construct", reg::Public, 0, {}, [](INTERNAL_FUNCTION_PARAMETERS) { + cls.method(sigs::__construct, [](INTERNAL_FUNCTION_PARAMETERS) { ZEND_PARSE_PARAMETERS_NONE(); }); diff --git a/turbo-ext/src/SymbolFinderInFiles.cpp b/turbo-ext/src/SymbolFinderInFiles.cpp index 2e3ebfc8d71..dda4cb02ab0 100644 --- a/turbo-ext/src/SymbolFinderInFiles.cpp +++ b/turbo-ext/src/SymbolFinderInFiles.cpp @@ -20,6 +20,8 @@ #include "support.h" #include "generated/SymbolFinderInFiles.h" + +namespace sigs = ptdecl::SymbolFinderInFiles::sig; #include "zv.h" #include "SymbolScan.h" @@ -188,7 +190,7 @@ void pt_register_symbol_finder_in_files() /* the arginfo has to keep the real parameter class name: Nette reflects * this constructor while compiling the container (rule 6) */ - cls.method("__construct", reg::Public, 1, { reg::obj("cleaner", CLEANER_CLASS) }, [](INTERNAL_FUNCTION_PARAMETERS) { + cls.method(sigs::__construct, [](INTERNAL_FUNCTION_PARAMETERS) { zval *cleaner; if (!zp::parse(execute_data, cleaner)) RETURN_THROWS(); (void) cleaner; diff --git a/turbo-ext/src/TrinaryLogic.cpp b/turbo-ext/src/TrinaryLogic.cpp index de269506a94..06df45eed4b 100644 --- a/turbo-ext/src/TrinaryLogic.cpp +++ b/turbo-ext/src/TrinaryLogic.cpp @@ -12,6 +12,8 @@ #include "support.h" #include "generated/TrinaryLogic.h" + +namespace sigs = ptdecl::TrinaryLogic::sig; #include "zv.h" namespace phpstanturbo { @@ -319,7 +321,7 @@ void pt_register_trinary_logic() /* "value" must stay the first declared property (OBJ_PROP_NUM slot 0) */ cls.privateLongProperty("value", 0); - cls.method("__construct", reg::Private, 1, { reg::longArg("value") }, [](INTERNAL_FUNCTION_PARAMETERS) { + cls.method(sigs::__construct, [](INTERNAL_FUNCTION_PARAMETERS) { zend_long value; if (!zp::parse(execute_data, value)) RETURN_THROWS(); ZVAL_LONG(OBJ_PROP_NUM(Z_OBJ_P(ZEND_THIS), PT_TRI_PROP_VALUE), value); diff --git a/turbo-ext/src/generated/ArenaCache.h b/turbo-ext/src/generated/ArenaCache.h index c38c8b07738..f68c00db1fa 100644 --- a/turbo-ext/src/generated/ArenaCache.h +++ b/turbo-ext/src/generated/ArenaCache.h @@ -19,6 +19,38 @@ inline void declareProperties(reg::Class &cls) (void) cls; } +/* the signatures of the methods the class declares itself (a used trait's are in the trait's header) */ +namespace sig { +inline constexpr reg::Arg create_args[] = { reg::typed("runId", MAY_BE_STRING) }; +inline constexpr reg::Arg create_return = reg::typed("", MAY_BE_NULL | MAY_BE_STRING); +inline constexpr reg::Sig create = { "create", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 1, create_args, 1, &create_return }; +inline constexpr reg::Arg attach_args[] = { reg::typed("name", MAY_BE_STRING) }; +inline constexpr reg::Arg attach_return = reg::typed("", MAY_BE_BOOL); +inline constexpr reg::Sig attach = { "attach", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 1, attach_args, 1, &attach_return }; +inline constexpr reg::Arg unlinkName_return = reg::typed("", MAY_BE_VOID); +inline constexpr reg::Sig unlinkName = { "unlinkName", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 0, nullptr, 0, &unlinkName_return }; +inline constexpr reg::Arg destroy_return = reg::typed("", MAY_BE_VOID); +inline constexpr reg::Sig destroy = { "destroy", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 0, nullptr, 0, &destroy_return }; +inline constexpr reg::Arg hasRecord_args[] = { reg::typed("key", MAY_BE_STRING) }; +inline constexpr reg::Arg hasRecord_return = reg::typed("", MAY_BE_BOOL); +inline constexpr reg::Sig hasRecord = { "hasRecord", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 1, hasRecord_args, 1, &hasRecord_return }; +inline constexpr reg::Arg lookup_args[] = { reg::typed("key", MAY_BE_STRING) }; +inline constexpr reg::Arg lookup_return = reg::typed("", MAY_BE_ANY); +inline constexpr reg::Sig lookup = { "lookup", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 1, lookup_args, 1, &lookup_return }; +inline constexpr reg::Arg publish_args[] = { reg::typed("key", MAY_BE_STRING), reg::typed("value", 0) }; +inline constexpr reg::Arg publish_return = reg::typed("", MAY_BE_VOID); +inline constexpr reg::Sig publish = { "publish", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 2, publish_args, 2, &publish_return }; +inline constexpr reg::Arg lookupHash_args[] = { reg::typed("recordKey", MAY_BE_STRING), reg::typed("entryKey", MAY_BE_STRING) }; +inline constexpr reg::Arg lookupHash_return = reg::typed("", MAY_BE_ANY); +inline constexpr reg::Sig lookupHash = { "lookupHash", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 2, lookupHash_args, 2, &lookupHash_return }; +inline constexpr reg::Arg lookupHashAll_args[] = { reg::typed("recordKey", MAY_BE_STRING) }; +inline constexpr reg::Arg lookupHashAll_return = reg::typed("", MAY_BE_NULL | MAY_BE_ARRAY); +inline constexpr reg::Sig lookupHashAll = { "lookupHashAll", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 1, lookupHashAll_args, 1, &lookupHashAll_return }; +inline constexpr reg::Arg publishHash_args[] = { reg::typed("recordKey", MAY_BE_STRING), reg::typed("entries", MAY_BE_ARRAY) }; +inline constexpr reg::Arg publishHash_return = reg::typed("", MAY_BE_VOID); +inline constexpr reg::Sig publishHash = { "publishHash", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 2, publishHash_args, 2, &publishHash_return }; +} // namespace sig + } // namespace ptdecl::ArenaCache #endif diff --git a/turbo-ext/src/generated/CombinationsHelper.h b/turbo-ext/src/generated/CombinationsHelper.h index 2afaa4bdcff..a05d302a675 100644 --- a/turbo-ext/src/generated/CombinationsHelper.h +++ b/turbo-ext/src/generated/CombinationsHelper.h @@ -19,6 +19,13 @@ inline void declareProperties(reg::Class &cls) (void) cls; } +/* the signatures of the methods the class declares itself (a used trait's are in the trait's header) */ +namespace sig { +inline constexpr reg::Arg combinations_args[] = { reg::typed("arrays", MAY_BE_ARRAY) }; +inline constexpr reg::Arg combinations_return = reg::typed("", _ZEND_TYPE_ITERABLE_BIT); +inline constexpr reg::Sig combinations = { "combinations", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 1, combinations_args, 1, &combinations_return }; +} // namespace sig + } // namespace ptdecl::CombinationsHelper #endif diff --git a/turbo-ext/src/generated/ConditionalExpressionHolder.h b/turbo-ext/src/generated/ConditionalExpressionHolder.h index 4112fc63f25..4ff364c8664 100644 --- a/turbo-ext/src/generated/ConditionalExpressionHolder.h +++ b/turbo-ext/src/generated/ConditionalExpressionHolder.h @@ -26,6 +26,18 @@ inline void declareProperties(reg::Class &cls) cls.property("typeHolder", ZEND_ACC_PRIVATE, reg::PropertyKind::Typed, 0, "PHPStan\\Analyser\\ExpressionTypeHolder"); } +/* the signatures of the methods the class declares itself (a used trait's are in the trait's header) */ +namespace sig { +inline constexpr reg::Arg __construct_args[] = { reg::typed("conditionExpressionTypeHolders", MAY_BE_ARRAY), reg::typed("typeHolder", 0, "PHPStan\\Analyser\\ExpressionTypeHolder") }; +inline constexpr reg::Sig __construct = { "__construct", ZEND_ACC_PUBLIC, 2, __construct_args, 2, nullptr }; +inline constexpr reg::Arg getConditionExpressionTypeHolders_return = reg::typed("", MAY_BE_ARRAY); +inline constexpr reg::Sig getConditionExpressionTypeHolders = { "getConditionExpressionTypeHolders", ZEND_ACC_PUBLIC, 0, nullptr, 0, &getConditionExpressionTypeHolders_return }; +inline constexpr reg::Arg getTypeHolder_return = reg::typed("", 0, "PHPStan\\Analyser\\ExpressionTypeHolder"); +inline constexpr reg::Sig getTypeHolder = { "getTypeHolder", ZEND_ACC_PUBLIC, 0, nullptr, 0, &getTypeHolder_return }; +inline constexpr reg::Arg getKey_return = reg::typed("", MAY_BE_STRING); +inline constexpr reg::Sig getKey = { "getKey", ZEND_ACC_PUBLIC, 0, nullptr, 0, &getKey_return }; +} // namespace sig + } // namespace ptdecl::ConditionalExpressionHolder #endif diff --git a/turbo-ext/src/generated/ExpressionResultStorage.h b/turbo-ext/src/generated/ExpressionResultStorage.h index 5ce58770601..c63c1d52635 100644 --- a/turbo-ext/src/generated/ExpressionResultStorage.h +++ b/turbo-ext/src/generated/ExpressionResultStorage.h @@ -26,6 +26,22 @@ inline void declareProperties(reg::Class &cls) cls.property("fallback", ZEND_ACC_PRIVATE, reg::PropertyKind::TypedNull, MAY_BE_NULL, "PHPStan\\Analyser\\ExpressionResultStorage"); } +/* the signatures of the methods the class declares itself (a used trait's are in the trait's header) */ +namespace sig { +inline constexpr reg::Sig __construct = { "__construct", ZEND_ACC_PUBLIC, 0, nullptr, 0, nullptr }; +inline constexpr reg::Arg duplicate_return = reg::typed("", 0, "PHPStan\\Analyser\\ExpressionResultStorage"); +inline constexpr reg::Sig duplicate = { "duplicate", ZEND_ACC_PUBLIC, 0, nullptr, 0, &duplicate_return }; +inline constexpr reg::Arg mergeResults_args[] = { reg::typed("other", 0, "PHPStan\\Analyser\\ExpressionResultStorage") }; +inline constexpr reg::Arg mergeResults_return = reg::typed("", MAY_BE_VOID); +inline constexpr reg::Sig mergeResults = { "mergeResults", ZEND_ACC_PUBLIC, 1, mergeResults_args, 1, &mergeResults_return }; +inline constexpr reg::Arg storeExpressionResult_args[] = { reg::typed("expr", 0, "PhpParser\\Node\\Expr"), reg::typed("expressionResult", 0, "PHPStan\\Analyser\\ExpressionResult") }; +inline constexpr reg::Arg storeExpressionResult_return = reg::typed("", MAY_BE_VOID); +inline constexpr reg::Sig storeExpressionResult = { "storeExpressionResult", ZEND_ACC_PUBLIC, 2, storeExpressionResult_args, 2, &storeExpressionResult_return }; +inline constexpr reg::Arg findExpressionResult_args[] = { reg::typed("expr", 0, "PhpParser\\Node\\Expr") }; +inline constexpr reg::Arg findExpressionResult_return = reg::typed("", MAY_BE_NULL, "PHPStan\\Analyser\\ExpressionResult"); +inline constexpr reg::Sig findExpressionResult = { "findExpressionResult", ZEND_ACC_PUBLIC, 1, findExpressionResult_args, 1, &findExpressionResult_return }; +} // namespace sig + } // namespace ptdecl::ExpressionResultStorage #endif diff --git a/turbo-ext/src/generated/ExpressionTypeHolder.h b/turbo-ext/src/generated/ExpressionTypeHolder.h index 10e014ad6ae..b7d823db394 100644 --- a/turbo-ext/src/generated/ExpressionTypeHolder.h +++ b/turbo-ext/src/generated/ExpressionTypeHolder.h @@ -28,6 +28,33 @@ inline void declareProperties(reg::Class &cls) cls.property("certainty", ZEND_ACC_PRIVATE | ZEND_ACC_READONLY, reg::PropertyKind::Typed, 0, "PHPStan\\TrinaryLogic"); } +/* the signatures of the methods the class declares itself (a used trait's are in the trait's header) */ +namespace sig { +inline constexpr reg::Arg __construct_args[] = { reg::typed("expr", 0, "PhpParser\\Node\\Expr"), reg::typed("type", 0, "PHPStan\\Type\\Type"), reg::typed("certainty", 0, "PHPStan\\TrinaryLogic") }; +inline constexpr reg::Sig __construct = { "__construct", ZEND_ACC_PUBLIC, 3, __construct_args, 3, nullptr }; +inline constexpr reg::Arg createYes_args[] = { reg::typed("expr", 0, "PhpParser\\Node\\Expr"), reg::typed("type", 0, "PHPStan\\Type\\Type") }; +inline constexpr reg::Arg createYes_return = reg::typed("", 0, "PHPStan\\Analyser\\ExpressionTypeHolder"); +inline constexpr reg::Sig createYes = { "createYes", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 2, createYes_args, 2, &createYes_return }; +inline constexpr reg::Arg createMaybe_args[] = { reg::typed("expr", 0, "PhpParser\\Node\\Expr"), reg::typed("type", 0, "PHPStan\\Type\\Type") }; +inline constexpr reg::Arg createMaybe_return = reg::typed("", 0, "PHPStan\\Analyser\\ExpressionTypeHolder"); +inline constexpr reg::Sig createMaybe = { "createMaybe", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 2, createMaybe_args, 2, &createMaybe_return }; +inline constexpr reg::Arg equalTypes_args[] = { reg::typed("other", 0, "PHPStan\\Analyser\\ExpressionTypeHolder") }; +inline constexpr reg::Arg equalTypes_return = reg::typed("", MAY_BE_BOOL); +inline constexpr reg::Sig equalTypes = { "equalTypes", ZEND_ACC_PUBLIC, 1, equalTypes_args, 1, &equalTypes_return }; +inline constexpr reg::Arg equals_args[] = { reg::typed("other", 0, "PHPStan\\Analyser\\ExpressionTypeHolder") }; +inline constexpr reg::Arg equals_return = reg::typed("", MAY_BE_BOOL); +inline constexpr reg::Sig equals = { "equals", ZEND_ACC_PUBLIC, 1, equals_args, 1, &equals_return }; +inline constexpr reg::Arg and__args[] = { reg::typed("other", 0, "PHPStan\\Analyser\\ExpressionTypeHolder") }; +inline constexpr reg::Arg and__return = reg::typed("", 0, "PHPStan\\Analyser\\ExpressionTypeHolder"); +inline constexpr reg::Sig and_ = { "and", ZEND_ACC_PUBLIC, 1, and__args, 1, &and__return }; +inline constexpr reg::Arg getExpr_return = reg::typed("", 0, "PhpParser\\Node\\Expr"); +inline constexpr reg::Sig getExpr = { "getExpr", ZEND_ACC_PUBLIC, 0, nullptr, 0, &getExpr_return }; +inline constexpr reg::Arg getType_return = reg::typed("", 0, "PHPStan\\Type\\Type"); +inline constexpr reg::Sig getType = { "getType", ZEND_ACC_PUBLIC, 0, nullptr, 0, &getType_return }; +inline constexpr reg::Arg getCertainty_return = reg::typed("", 0, "PHPStan\\TrinaryLogic"); +inline constexpr reg::Sig getCertainty = { "getCertainty", ZEND_ACC_PUBLIC, 0, nullptr, 0, &getCertainty_return }; +} // namespace sig + } // namespace ptdecl::ExpressionTypeHolder #endif diff --git a/turbo-ext/src/generated/NodeScanner.h b/turbo-ext/src/generated/NodeScanner.h index 2ee82897651..4ee11b94212 100644 --- a/turbo-ext/src/generated/NodeScanner.h +++ b/turbo-ext/src/generated/NodeScanner.h @@ -19,6 +19,13 @@ inline void declareProperties(reg::Class &cls) (void) cls; } +/* the signatures of the methods the class declares itself (a used trait's are in the trait's header) */ +namespace sig { +inline constexpr reg::Arg nodeIsOrContainsYield_args[] = { reg::typed("node", 0, "PhpParser\\Node") }; +inline constexpr reg::Arg nodeIsOrContainsYield_return = reg::typed("", MAY_BE_BOOL); +inline constexpr reg::Sig nodeIsOrContainsYield = { "nodeIsOrContainsYield", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 1, nodeIsOrContainsYield_args, 1, &nodeIsOrContainsYield_return }; +} // namespace sig + } // namespace ptdecl::NodeScanner #endif diff --git a/turbo-ext/src/generated/NodeTraverser.h b/turbo-ext/src/generated/NodeTraverser.h index 11c053c0c2d..756d8968a21 100644 --- a/turbo-ext/src/generated/NodeTraverser.h +++ b/turbo-ext/src/generated/NodeTraverser.h @@ -26,6 +26,30 @@ inline void declareProperties(reg::Class &cls) cls.property("stopTraversal", ZEND_ACC_PROTECTED, reg::PropertyKind::Typed, MAY_BE_BOOL); } +/* the signatures of the methods the class declares itself (a used trait's are in the trait's header) */ +namespace sig { +inline constexpr reg::Arg __construct_args[] = { reg::typed("visitors", 0, "PhpParser\\NodeVisitor", false, true) }; +inline constexpr reg::Sig __construct = { "__construct", ZEND_ACC_PUBLIC, 0, __construct_args, 1, nullptr }; +inline constexpr reg::Arg addVisitor_args[] = { reg::typed("visitor", 0, "PhpParser\\NodeVisitor") }; +inline constexpr reg::Arg addVisitor_return = reg::typed("", MAY_BE_VOID); +inline constexpr reg::Sig addVisitor = { "addVisitor", ZEND_ACC_PUBLIC, 1, addVisitor_args, 1, &addVisitor_return }; +inline constexpr reg::Arg removeVisitor_args[] = { reg::typed("visitor", 0, "PhpParser\\NodeVisitor") }; +inline constexpr reg::Arg removeVisitor_return = reg::typed("", MAY_BE_VOID); +inline constexpr reg::Sig removeVisitor = { "removeVisitor", ZEND_ACC_PUBLIC, 1, removeVisitor_args, 1, &removeVisitor_return }; +inline constexpr reg::Arg traverse_args[] = { reg::typed("nodes", MAY_BE_ARRAY) }; +inline constexpr reg::Arg traverse_return = reg::typed("", MAY_BE_ARRAY); +inline constexpr reg::Sig traverse = { "traverse", ZEND_ACC_PUBLIC, 1, traverse_args, 1, &traverse_return }; +inline constexpr reg::Arg traverseNode_args[] = { reg::typed("node", 0, "PhpParser\\Node") }; +inline constexpr reg::Arg traverseNode_return = reg::typed("", MAY_BE_VOID); +inline constexpr reg::Sig traverseNode = { "traverseNode", ZEND_ACC_PROTECTED, 1, traverseNode_args, 1, &traverseNode_return }; +inline constexpr reg::Arg traverseArray_args[] = { reg::typed("nodes", MAY_BE_ARRAY) }; +inline constexpr reg::Arg traverseArray_return = reg::typed("", MAY_BE_ARRAY); +inline constexpr reg::Sig traverseArray = { "traverseArray", ZEND_ACC_PROTECTED, 1, traverseArray_args, 1, &traverseArray_return }; +inline constexpr reg::Arg ensureReplacementReasonable_args[] = { reg::typed("old", 0, "PhpParser\\Node"), reg::typed("new", 0, "PhpParser\\Node") }; +inline constexpr reg::Arg ensureReplacementReasonable_return = reg::typed("", MAY_BE_VOID); +inline constexpr reg::Sig ensureReplacementReasonable = { "ensureReplacementReasonable", ZEND_ACC_PRIVATE, 2, ensureReplacementReasonable_args, 2, &ensureReplacementReasonable_return }; +} // namespace sig + } // namespace ptdecl::NodeTraverser #endif diff --git a/turbo-ext/src/generated/ParserRunner.h b/turbo-ext/src/generated/ParserRunner.h index c6ed0845e8e..d8e4784f56e 100644 --- a/turbo-ext/src/generated/ParserRunner.h +++ b/turbo-ext/src/generated/ParserRunner.h @@ -19,6 +19,13 @@ inline void declareProperties(reg::Class &cls) (void) cls; } +/* the signatures of the methods the class declares itself (a used trait's are in the trait's header) */ +namespace sig { +inline constexpr reg::Arg parse_args[] = { reg::typed("parser", 0, "PhpParser\\Parser"), reg::typed("sourceCode", MAY_BE_STRING), reg::typed("errorHandler", 0, "PhpParser\\ErrorHandler") }; +inline constexpr reg::Arg parse_return = reg::typed("", MAY_BE_NULL | MAY_BE_ARRAY); +inline constexpr reg::Sig parse = { "parse", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 3, parse_args, 3, &parse_return }; +} // namespace sig + } // namespace ptdecl::ParserRunner #endif diff --git a/turbo-ext/src/generated/PhpFileCleaner.h b/turbo-ext/src/generated/PhpFileCleaner.h index 894ed45d6e2..3a69f7e5659 100644 --- a/turbo-ext/src/generated/PhpFileCleaner.h +++ b/turbo-ext/src/generated/PhpFileCleaner.h @@ -24,6 +24,35 @@ inline void declareClass(reg::Class &cls) /* no declareProperties(): $contents: a typed default reg::Class cannot declare */ +/* the signatures of the methods the class declares itself (a used trait's are in the trait's header) */ +namespace sig { +inline constexpr reg::Sig __construct = { "__construct", ZEND_ACC_PUBLIC, 0, nullptr, 0, nullptr }; +inline constexpr reg::Arg clean_args[] = { reg::typed("contents", MAY_BE_STRING), reg::typed("maxMatches", MAY_BE_LONG) }; +inline constexpr reg::Arg clean_return = reg::typed("", MAY_BE_STRING); +inline constexpr reg::Sig clean = { "clean", ZEND_ACC_PUBLIC, 2, clean_args, 2, &clean_return }; +inline constexpr reg::Arg skipToPhp_return = reg::typed("", MAY_BE_VOID); +inline constexpr reg::Sig skipToPhp = { "skipToPhp", ZEND_ACC_PRIVATE, 0, nullptr, 0, &skipToPhp_return }; +inline constexpr reg::Arg consumeString_args[] = { reg::typed("delimiter", MAY_BE_STRING) }; +inline constexpr reg::Arg consumeString_return = reg::typed("", MAY_BE_STRING); +inline constexpr reg::Sig consumeString = { "consumeString", ZEND_ACC_PRIVATE, 1, consumeString_args, 1, &consumeString_return }; +inline constexpr reg::Arg skipString_args[] = { reg::typed("delimiter", MAY_BE_STRING) }; +inline constexpr reg::Arg skipString_return = reg::typed("", MAY_BE_VOID); +inline constexpr reg::Sig skipString = { "skipString", ZEND_ACC_PRIVATE, 1, skipString_args, 1, &skipString_return }; +inline constexpr reg::Arg skipComment_return = reg::typed("", MAY_BE_VOID); +inline constexpr reg::Sig skipComment = { "skipComment", ZEND_ACC_PRIVATE, 0, nullptr, 0, &skipComment_return }; +inline constexpr reg::Arg skipToNewline_return = reg::typed("", MAY_BE_VOID); +inline constexpr reg::Sig skipToNewline = { "skipToNewline", ZEND_ACC_PRIVATE, 0, nullptr, 0, &skipToNewline_return }; +inline constexpr reg::Arg skipHeredoc_args[] = { reg::typed("delimiter", MAY_BE_STRING) }; +inline constexpr reg::Arg skipHeredoc_return = reg::typed("", MAY_BE_VOID); +inline constexpr reg::Sig skipHeredoc = { "skipHeredoc", ZEND_ACC_PRIVATE, 1, skipHeredoc_args, 1, &skipHeredoc_return }; +inline constexpr reg::Arg peek_args[] = { reg::typed("char", MAY_BE_STRING) }; +inline constexpr reg::Arg peek_return = reg::typed("", MAY_BE_BOOL); +inline constexpr reg::Sig peek = { "peek", ZEND_ACC_PRIVATE, 1, peek_args, 1, &peek_return }; +inline constexpr reg::Arg match_args[] = { reg::typed("regex", MAY_BE_STRING), reg::typed("match", MAY_BE_NULL | MAY_BE_ARRAY, nullptr, true, false, "null"), reg::typed("offset", MAY_BE_NULL | MAY_BE_LONG, nullptr, false, false, "null") }; +inline constexpr reg::Arg match_return = reg::typed("", MAY_BE_BOOL); +inline constexpr reg::Sig match = { "match", ZEND_ACC_PRIVATE, 1, match_args, 3, &match_return }; +} // namespace sig + } // namespace ptdecl::PhpFileCleaner #endif diff --git a/turbo-ext/src/generated/ScopeOps.h b/turbo-ext/src/generated/ScopeOps.h index dd596312679..da45bfa2c76 100644 --- a/turbo-ext/src/generated/ScopeOps.h +++ b/turbo-ext/src/generated/ScopeOps.h @@ -19,6 +19,64 @@ inline void declareProperties(reg::Class &cls) (void) cls; } +/* the signatures of the methods the class declares itself (a used trait's are in the trait's header) */ +namespace sig { +inline constexpr reg::Arg nodeKey_args[] = { reg::typed("node", 0, "PhpParser\\Node\\Expr"), reg::typed("exprPrinter", 0, "PHPStan\\Node\\Printer\\ExprPrinter") }; +inline constexpr reg::Arg nodeKey_return = reg::typed("", MAY_BE_STRING); +inline constexpr reg::Sig nodeKey = { "nodeKey", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 2, nodeKey_args, 2, &nodeKey_return }; +inline constexpr reg::Arg getTypeFromCache_args[] = { reg::typed("scope", 0, "PHPStan\\Analyser\\MutatingScope"), reg::typed("node", 0, "PhpParser\\Node\\Expr"), reg::typed("key", MAY_BE_NULL | MAY_BE_STRING, nullptr, true, false) }; +inline constexpr reg::Arg getTypeFromCache_return = reg::typed("", MAY_BE_NULL, "PHPStan\\Type\\Type"); +inline constexpr reg::Sig getTypeFromCache = { "getTypeFromCache", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 3, getTypeFromCache_args, 3, &getTypeFromCache_return }; +inline constexpr reg::Arg expressionTypeByKey_args[] = { reg::typed("scope", 0, "PHPStan\\Analyser\\MutatingScope"), reg::typed("node", 0, "PhpParser\\Node\\Expr"), reg::typed("exprString", MAY_BE_STRING) }; +inline constexpr reg::Arg expressionTypeByKey_return = reg::typed("", MAY_BE_NULL, "PHPStan\\Type\\Type"); +inline constexpr reg::Sig expressionTypeByKey = { "expressionTypeByKey", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 3, expressionTypeByKey_args, 3, &expressionTypeByKey_return }; +inline constexpr reg::Arg hasExpressionType_args[] = { reg::typed("scope", 0, "PHPStan\\Analyser\\MutatingScope"), reg::typed("node", 0, "PhpParser\\Node\\Expr"), reg::typed("exprPrinter", 0, "PHPStan\\Node\\Printer\\ExprPrinter") }; +inline constexpr reg::Arg hasExpressionType_return = reg::typed("", 0, "PHPStan\\TrinaryLogic"); +inline constexpr reg::Sig hasExpressionType = { "hasExpressionType", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 3, hasExpressionType_args, 3, &hasExpressionType_return }; +inline constexpr reg::Arg hasVariableType_args[] = { reg::typed("scope", 0, "PHPStan\\Analyser\\MutatingScope"), reg::typed("variableName", MAY_BE_STRING) }; +inline constexpr reg::Arg hasVariableType_return = reg::typed("", 0, "PHPStan\\TrinaryLogic"); +inline constexpr reg::Sig hasVariableType = { "hasVariableType", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 2, hasVariableType_args, 2, &hasVariableType_return }; +inline constexpr reg::Arg scopeWith_args[] = { reg::typed("scope", 0, "PHPStan\\Analyser\\MutatingScope"), reg::typed("expressionTypes", MAY_BE_ARRAY), reg::typed("nativeExpressionTypes", MAY_BE_ARRAY), reg::typed("conditionalExpressions", MAY_BE_ARRAY), reg::typed("currentlyAssignedExpressions", MAY_BE_ARRAY), reg::typed("currentlyAllowedUndefinedExpressions", MAY_BE_ARRAY), reg::typed("inFunctionCallsStack", MAY_BE_ARRAY), reg::typed("inFirstLevelStatement", MAY_BE_BOOL), reg::typed("afterExtractCall", MAY_BE_BOOL) }; +inline constexpr reg::Arg scopeWith_return = reg::typed("", 0, "PHPStan\\Analyser\\MutatingScope"); +inline constexpr reg::Sig scopeWith = { "scopeWith", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 9, scopeWith_args, 9, &scopeWith_return }; +inline constexpr reg::Arg mergeVariableHolders_args[] = { reg::typed("ourVariableTypeHolders", MAY_BE_ARRAY), reg::typed("theirVariableTypeHolders", MAY_BE_ARRAY), reg::typed("differingKeys", MAY_BE_ARRAY, nullptr, true, false, "[]") }; +inline constexpr reg::Arg mergeVariableHolders_return = reg::typed("", MAY_BE_ARRAY); +inline constexpr reg::Sig mergeVariableHolders = { "mergeVariableHolders", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 2, mergeVariableHolders_args, 3, &mergeVariableHolders_return }; +inline constexpr reg::Arg finishMerge_args[] = { reg::typed("mergedExpressionTypes", MAY_BE_ARRAY), reg::typed("ourExpressionTypes", MAY_BE_ARRAY), reg::typed("theirExpressionTypes", MAY_BE_ARRAY), reg::typed("ourNativeExpressionTypes", MAY_BE_ARRAY), reg::typed("theirNativeExpressionTypes", MAY_BE_ARRAY) }; +inline constexpr reg::Arg finishMerge_return = reg::typed("", MAY_BE_ARRAY); +inline constexpr reg::Sig finishMerge = { "finishMerge", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 5, finishMerge_args, 5, &finishMerge_return }; +inline constexpr reg::Arg intersectConditionalExpressions_args[] = { reg::typed("ourConditionalExpressions", MAY_BE_ARRAY), reg::typed("theirConditionalExpressions", MAY_BE_ARRAY) }; +inline constexpr reg::Arg intersectConditionalExpressions_return = reg::typed("", MAY_BE_ARRAY); +inline constexpr reg::Sig intersectConditionalExpressions = { "intersectConditionalExpressions", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 2, intersectConditionalExpressions_args, 2, &intersectConditionalExpressions_return }; +inline constexpr reg::Arg createConditionalExpressions_args[] = { reg::typed("conditionalExpressions", MAY_BE_ARRAY), reg::typed("ourExpressionTypes", MAY_BE_ARRAY), reg::typed("theirExpressionTypes", MAY_BE_ARRAY), reg::typed("mergedExpressionTypes", MAY_BE_ARRAY), reg::typed("differingKeys", MAY_BE_ARRAY) }; +inline constexpr reg::Arg createConditionalExpressions_return = reg::typed("", MAY_BE_ARRAY); +inline constexpr reg::Sig createConditionalExpressions = { "createConditionalExpressions", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 5, createConditionalExpressions_args, 5, &createConditionalExpressions_return }; +inline constexpr reg::Arg invalidateMethodsOnExpression_args[] = { reg::typed("exprPrinter", 0, "PHPStan\\Node\\Printer\\ExprPrinter"), reg::typed("exprStringToInvalidate", MAY_BE_STRING), reg::typed("expressionTypes", MAY_BE_ARRAY), reg::typed("nativeExpressionTypes", MAY_BE_ARRAY) }; +inline constexpr reg::Arg invalidateMethodsOnExpression_return = reg::typed("", MAY_BE_NULL | MAY_BE_ARRAY); +inline constexpr reg::Sig invalidateMethodsOnExpression = { "invalidateMethodsOnExpression", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 4, invalidateMethodsOnExpression_args, 4, &invalidateMethodsOnExpression_return }; +inline constexpr reg::Arg keyMayHideSubExpressions_args[] = { reg::typed("exprString", MAY_BE_STRING) }; +inline constexpr reg::Arg keyMayHideSubExpressions_return = reg::typed("", MAY_BE_BOOL); +inline constexpr reg::Sig keyMayHideSubExpressions = { "keyMayHideSubExpressions", ZEND_ACC_PRIVATE | ZEND_ACC_STATIC, 1, keyMayHideSubExpressions_args, 1, &keyMayHideSubExpressions_return }; +inline constexpr reg::Arg getIntertwinedRefRootVariableName_args[] = { reg::typed("expr", 0, "PhpParser\\Node\\Expr") }; +inline constexpr reg::Arg getIntertwinedRefRootVariableName_return = reg::typed("", MAY_BE_NULL | MAY_BE_STRING); +inline constexpr reg::Sig getIntertwinedRefRootVariableName = { "getIntertwinedRefRootVariableName", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 1, getIntertwinedRefRootVariableName_args, 1, &getIntertwinedRefRootVariableName_return }; +inline constexpr reg::Arg invalidateExpressionEntries_args[] = { reg::typed("scope", 0, "PHPStan\\Analyser\\MutatingScope"), reg::typed("exprPrinter", 0, "PHPStan\\Node\\Printer\\ExprPrinter"), reg::typed("exprStringToInvalidate", MAY_BE_STRING), reg::typed("expressionToInvalidate", 0, "PhpParser\\Node\\Expr"), reg::typed("requireMoreCharacters", MAY_BE_BOOL), reg::typed("invalidatingClass", MAY_BE_NULL, "PHPStan\\Reflection\\ClassReflection"), reg::typed("expressionTypes", MAY_BE_ARRAY), reg::typed("nativeExpressionTypes", MAY_BE_ARRAY), reg::typed("conditionalExpressions", MAY_BE_ARRAY), reg::typed("keepPropertyFetches", MAY_BE_BOOL, nullptr, false, false, "false") }; +inline constexpr reg::Arg invalidateExpressionEntries_return = reg::typed("", MAY_BE_NULL | MAY_BE_ARRAY); +inline constexpr reg::Sig invalidateExpressionEntries = { "invalidateExpressionEntries", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 9, invalidateExpressionEntries_args, 10, &invalidateExpressionEntries_return }; +inline constexpr reg::Arg containsExpressionToInvalidate_args[] = { reg::typed("scope", 0, "PHPStan\\Analyser\\Scope"), reg::typed("exprPrinter", 0, "PHPStan\\Node\\Printer\\ExprPrinter"), reg::typed("node", 0, "PhpParser\\Node"), reg::typed("expressionToInvalidateClass", MAY_BE_STRING), reg::typed("exprStringToInvalidate", MAY_BE_STRING) }; +inline constexpr reg::Arg containsExpressionToInvalidate_return = reg::typed("", MAY_BE_BOOL); +inline constexpr reg::Sig containsExpressionToInvalidate = { "containsExpressionToInvalidate", ZEND_ACC_PRIVATE | ZEND_ACC_STATIC, 5, containsExpressionToInvalidate_args, 5, &containsExpressionToInvalidate_return }; +inline constexpr reg::Arg shouldInvalidateExpression_args[] = { reg::typed("scope", 0, "PHPStan\\Analyser\\MutatingScope"), reg::typed("exprPrinter", 0, "PHPStan\\Node\\Printer\\ExprPrinter"), reg::typed("exprStringToInvalidate", MAY_BE_STRING), reg::typed("exprToInvalidate", 0, "PhpParser\\Node\\Expr"), reg::typed("expr", 0, "PhpParser\\Node\\Expr"), reg::typed("exprString", MAY_BE_STRING), reg::typed("requireMoreCharacters", MAY_BE_BOOL, nullptr, false, false, "false"), reg::typed("invalidatingClass", MAY_BE_NULL, "PHPStan\\Reflection\\ClassReflection", false, false, "null"), reg::typed("keepPropertyFetches", MAY_BE_BOOL, nullptr, false, false, "false") }; +inline constexpr reg::Arg shouldInvalidateExpression_return = reg::typed("", MAY_BE_BOOL); +inline constexpr reg::Sig shouldInvalidateExpression = { "shouldInvalidateExpression", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 6, shouldInvalidateExpression_args, 9, &shouldInvalidateExpression_return }; +inline constexpr reg::Arg isPropertyFetchChainOn_args[] = { reg::typed("expr", 0, "PhpParser\\Node\\Expr"), reg::typed("exprStringToInvalidate", MAY_BE_STRING), reg::typed("exprPrinter", 0, "PHPStan\\Node\\Printer\\ExprPrinter") }; +inline constexpr reg::Arg isPropertyFetchChainOn_return = reg::typed("", MAY_BE_BOOL); +inline constexpr reg::Sig isPropertyFetchChainOn = { "isPropertyFetchChainOn", ZEND_ACC_PRIVATE | ZEND_ACC_STATIC, 3, isPropertyFetchChainOn_args, 3, &isPropertyFetchChainOn_return }; +inline constexpr reg::Arg matchConditionalExpressions_args[] = { reg::typed("conditionalExpressions", MAY_BE_ARRAY), reg::typed("specifiedExpressions", MAY_BE_ARRAY) }; +inline constexpr reg::Arg matchConditionalExpressions_return = reg::typed("", MAY_BE_ARRAY); +inline constexpr reg::Sig matchConditionalExpressions = { "matchConditionalExpressions", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 2, matchConditionalExpressions_args, 2, &matchConditionalExpressions_return }; +} // namespace sig + } // namespace ptdecl::ScopeOps #endif diff --git a/turbo-ext/src/generated/SymbolFinderInFiles.h b/turbo-ext/src/generated/SymbolFinderInFiles.h index e0da655b9e6..fd3cdaa6530 100644 --- a/turbo-ext/src/generated/SymbolFinderInFiles.h +++ b/turbo-ext/src/generated/SymbolFinderInFiles.h @@ -24,6 +24,21 @@ inline void declareProperties(reg::Class &cls) cls.property("cleaner", ZEND_ACC_PRIVATE, reg::PropertyKind::Typed, 0, "PHPStan\\Reflection\\BetterReflection\\SourceLocator\\PhpFileCleaner"); } +/* the signatures of the methods the class declares itself (a used trait's are in the trait's header) */ +namespace sig { +inline constexpr reg::Arg __construct_args[] = { reg::typed("cleaner", 0, "PHPStan\\Reflection\\BetterReflection\\SourceLocator\\PhpFileCleaner") }; +inline constexpr reg::Sig __construct = { "__construct", ZEND_ACC_PUBLIC, 1, __construct_args, 1, nullptr }; +inline constexpr reg::Arg findSymbols_args[] = { reg::typed("files", MAY_BE_ARRAY), reg::typed("supportsEnums", MAY_BE_BOOL) }; +inline constexpr reg::Arg findSymbols_return = reg::typed("", MAY_BE_ARRAY); +inline constexpr reg::Sig findSymbols = { "findSymbols", ZEND_ACC_PUBLIC, 2, findSymbols_args, 2, &findSymbols_return }; +inline constexpr reg::Arg findSymbolsInFile_args[] = { reg::typed("file", MAY_BE_STRING), reg::typed("supportsEnums", MAY_BE_BOOL) }; +inline constexpr reg::Arg findSymbolsInFile_return = reg::typed("", MAY_BE_ARRAY); +inline constexpr reg::Sig findSymbolsInFile = { "findSymbolsInFile", ZEND_ACC_PRIVATE, 2, findSymbolsInFile_args, 2, &findSymbolsInFile_return }; +inline constexpr reg::Arg normalizeConstantName_args[] = { reg::typed("name", MAY_BE_STRING) }; +inline constexpr reg::Arg normalizeConstantName_return = reg::typed("", MAY_BE_STRING); +inline constexpr reg::Sig normalizeConstantName = { "normalizeConstantName", ZEND_ACC_PRIVATE | ZEND_ACC_STATIC, 1, normalizeConstantName_args, 1, &normalizeConstantName_return }; +} // namespace sig + } // namespace ptdecl::SymbolFinderInFiles #endif diff --git a/turbo-ext/src/generated/TrinaryLogic.h b/turbo-ext/src/generated/TrinaryLogic.h index 4dae2e6a5a3..5b86f693628 100644 --- a/turbo-ext/src/generated/TrinaryLogic.h +++ b/turbo-ext/src/generated/TrinaryLogic.h @@ -28,6 +28,66 @@ inline void declareProperties(reg::Class &cls) cls.property("value", ZEND_ACC_PRIVATE, reg::PropertyKind::Typed, MAY_BE_LONG); } +/* the signatures of the methods the class declares itself (a used trait's are in the trait's header) */ +namespace sig { +inline constexpr reg::Arg __construct_args[] = { reg::typed("value", MAY_BE_LONG) }; +inline constexpr reg::Sig __construct = { "__construct", ZEND_ACC_PRIVATE, 1, __construct_args, 1, nullptr }; +inline constexpr reg::Arg createYes_return = reg::typed("", 0, "PHPStan\\TrinaryLogic"); +inline constexpr reg::Sig createYes = { "createYes", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 0, nullptr, 0, &createYes_return }; +inline constexpr reg::Arg createNo_return = reg::typed("", 0, "PHPStan\\TrinaryLogic"); +inline constexpr reg::Sig createNo = { "createNo", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 0, nullptr, 0, &createNo_return }; +inline constexpr reg::Arg createMaybe_return = reg::typed("", 0, "PHPStan\\TrinaryLogic"); +inline constexpr reg::Sig createMaybe = { "createMaybe", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 0, nullptr, 0, &createMaybe_return }; +inline constexpr reg::Arg createFromBoolean_args[] = { reg::typed("value", MAY_BE_BOOL) }; +inline constexpr reg::Arg createFromBoolean_return = reg::typed("", 0, "PHPStan\\TrinaryLogic"); +inline constexpr reg::Sig createFromBoolean = { "createFromBoolean", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 1, createFromBoolean_args, 1, &createFromBoolean_return }; +inline constexpr reg::Arg create_args[] = { reg::typed("value", MAY_BE_LONG) }; +inline constexpr reg::Arg create_return = reg::typed("", 0, "PHPStan\\TrinaryLogic"); +inline constexpr reg::Sig create = { "create", ZEND_ACC_PRIVATE | ZEND_ACC_STATIC, 1, create_args, 1, &create_return }; +inline constexpr reg::Arg yes_return = reg::typed("", MAY_BE_BOOL); +inline constexpr reg::Sig yes = { "yes", ZEND_ACC_PUBLIC, 0, nullptr, 0, &yes_return }; +inline constexpr reg::Arg maybe_return = reg::typed("", MAY_BE_BOOL); +inline constexpr reg::Sig maybe = { "maybe", ZEND_ACC_PUBLIC, 0, nullptr, 0, &maybe_return }; +inline constexpr reg::Arg no_return = reg::typed("", MAY_BE_BOOL); +inline constexpr reg::Sig no = { "no", ZEND_ACC_PUBLIC, 0, nullptr, 0, &no_return }; +inline constexpr reg::Arg toBooleanType_return = reg::typed("", 0, "PHPStan\\Type\\BooleanType"); +inline constexpr reg::Sig toBooleanType = { "toBooleanType", ZEND_ACC_PUBLIC, 0, nullptr, 0, &toBooleanType_return }; +inline constexpr reg::Arg and__args[] = { reg::typed("operand", MAY_BE_NULL, "PHPStan\\TrinaryLogic", false, false, "null"), reg::typed("rest", 0, "PHPStan\\TrinaryLogic", false, true) }; +inline constexpr reg::Arg and__return = reg::typed("", 0, "PHPStan\\TrinaryLogic"); +inline constexpr reg::Sig and_ = { "and", ZEND_ACC_PUBLIC, 0, and__args, 2, &and__return }; +inline constexpr reg::Arg lazyAnd_args[] = { reg::typed("objects", MAY_BE_ARRAY), reg::typed("callback", MAY_BE_CALLABLE) }; +inline constexpr reg::Arg lazyAnd_return = reg::typed("", 0, "PHPStan\\TrinaryLogic"); +inline constexpr reg::Sig lazyAnd = { "lazyAnd", ZEND_ACC_PUBLIC, 2, lazyAnd_args, 2, &lazyAnd_return }; +inline constexpr reg::Arg or__args[] = { reg::typed("operand", MAY_BE_NULL, "PHPStan\\TrinaryLogic", false, false, "null"), reg::typed("rest", 0, "PHPStan\\TrinaryLogic", false, true) }; +inline constexpr reg::Arg or__return = reg::typed("", 0, "PHPStan\\TrinaryLogic"); +inline constexpr reg::Sig or_ = { "or", ZEND_ACC_PUBLIC, 0, or__args, 2, &or__return }; +inline constexpr reg::Arg lazyOr_args[] = { reg::typed("objects", MAY_BE_ARRAY), reg::typed("callback", MAY_BE_CALLABLE) }; +inline constexpr reg::Arg lazyOr_return = reg::typed("", 0, "PHPStan\\TrinaryLogic"); +inline constexpr reg::Sig lazyOr = { "lazyOr", ZEND_ACC_PUBLIC, 2, lazyOr_args, 2, &lazyOr_return }; +inline constexpr reg::Arg extremeIdentity_args[] = { reg::typed("operands", 0, "PHPStan\\TrinaryLogic", false, true) }; +inline constexpr reg::Arg extremeIdentity_return = reg::typed("", 0, "PHPStan\\TrinaryLogic"); +inline constexpr reg::Sig extremeIdentity = { "extremeIdentity", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 0, extremeIdentity_args, 1, &extremeIdentity_return }; +inline constexpr reg::Arg lazyExtremeIdentity_args[] = { reg::typed("objects", MAY_BE_ARRAY), reg::typed("callback", MAY_BE_CALLABLE) }; +inline constexpr reg::Arg lazyExtremeIdentity_return = reg::typed("", 0, "PHPStan\\TrinaryLogic"); +inline constexpr reg::Sig lazyExtremeIdentity = { "lazyExtremeIdentity", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 2, lazyExtremeIdentity_args, 2, &lazyExtremeIdentity_return }; +inline constexpr reg::Arg maxMin_args[] = { reg::typed("operands", 0, "PHPStan\\TrinaryLogic", false, true) }; +inline constexpr reg::Arg maxMin_return = reg::typed("", 0, "PHPStan\\TrinaryLogic"); +inline constexpr reg::Sig maxMin = { "maxMin", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 0, maxMin_args, 1, &maxMin_return }; +inline constexpr reg::Arg lazyMaxMin_args[] = { reg::typed("objects", MAY_BE_ARRAY), reg::typed("callback", MAY_BE_CALLABLE) }; +inline constexpr reg::Arg lazyMaxMin_return = reg::typed("", 0, "PHPStan\\TrinaryLogic"); +inline constexpr reg::Sig lazyMaxMin = { "lazyMaxMin", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 2, lazyMaxMin_args, 2, &lazyMaxMin_return }; +inline constexpr reg::Arg negate_return = reg::typed("", 0, "PHPStan\\TrinaryLogic"); +inline constexpr reg::Sig negate = { "negate", ZEND_ACC_PUBLIC, 0, nullptr, 0, &negate_return }; +inline constexpr reg::Arg equals_args[] = { reg::typed("other", 0, "PHPStan\\TrinaryLogic") }; +inline constexpr reg::Arg equals_return = reg::typed("", MAY_BE_BOOL); +inline constexpr reg::Sig equals = { "equals", ZEND_ACC_PUBLIC, 1, equals_args, 1, &equals_return }; +inline constexpr reg::Arg compareTo_args[] = { reg::typed("other", 0, "PHPStan\\TrinaryLogic") }; +inline constexpr reg::Arg compareTo_return = reg::typed("", MAY_BE_NULL, "PHPStan\\TrinaryLogic"); +inline constexpr reg::Sig compareTo = { "compareTo", ZEND_ACC_PUBLIC, 1, compareTo_args, 1, &compareTo_return }; +inline constexpr reg::Arg describe_return = reg::typed("", MAY_BE_STRING); +inline constexpr reg::Sig describe = { "describe", ZEND_ACC_PUBLIC, 0, nullptr, 0, &describe_return }; +} // namespace sig + } // namespace ptdecl::TrinaryLogic #endif diff --git a/turbo-ext/src/generated/TypeCombinatorCache.h b/turbo-ext/src/generated/TypeCombinatorCache.h index 724ff48f757..71e3105f2ef 100644 --- a/turbo-ext/src/generated/TypeCombinatorCache.h +++ b/turbo-ext/src/generated/TypeCombinatorCache.h @@ -19,6 +19,21 @@ inline void declareProperties(reg::Class &cls) (void) cls; } +/* the signatures of the methods the class declares itself (a used trait's are in the trait's header) */ +namespace sig { +inline constexpr reg::Arg union__args[] = { reg::typed("types", 0, "PHPStan\\Type\\Type", false, true) }; +inline constexpr reg::Arg union__return = reg::typed("", 0, "PHPStan\\Type\\Type"); +inline constexpr reg::Sig union_ = { "union", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 0, union__args, 1, &union__return }; +inline constexpr reg::Arg intersect_args[] = { reg::typed("types", 0, "PHPStan\\Type\\Type", false, true) }; +inline constexpr reg::Arg intersect_return = reg::typed("", 0, "PHPStan\\Type\\Type"); +inline constexpr reg::Sig intersect = { "intersect", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 0, intersect_args, 1, &intersect_return }; +inline constexpr reg::Arg remove_args[] = { reg::typed("fromType", 0, "PHPStan\\Type\\Type"), reg::typed("typeToRemove", 0, "PHPStan\\Type\\Type") }; +inline constexpr reg::Arg remove_return = reg::typed("", 0, "PHPStan\\Type\\Type"); +inline constexpr reg::Sig remove = { "remove", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 2, remove_args, 2, &remove_return }; +inline constexpr reg::Arg clearCache_return = reg::typed("", MAY_BE_VOID); +inline constexpr reg::Sig clearCache = { "clearCache", ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, 0, nullptr, 0, &clearCache_return }; +} // namespace sig + } // namespace ptdecl::TypeCombinatorCache #endif diff --git a/turbo-ext/src/reg.h b/turbo-ext/src/reg.h index 8fd980c0e8e..a48329084fc 100644 --- a/turbo-ext/src/reg.h +++ b/turbo-ext/src/reg.h @@ -234,7 +234,7 @@ struct Arg /* an optional parameter with its default value's PHP source (e.g. "[]") — * without it the engine refuses to skip the parameter via named arguments */ -inline Arg withDefault(Arg arg, const char *defaultValue) +constexpr Arg withDefault(Arg arg, const char *defaultValue) { arg.defaultValue = defaultValue; return arg; @@ -242,12 +242,12 @@ inline Arg withDefault(Arg arg, const char *defaultValue) namespace detail { -inline uint32_t flagBits(bool byRef, bool variadic) +constexpr uint32_t flagBits(bool byRef, bool variadic) { return _ZEND_ARG_INFO_FLAGS(byRef ? ZEND_SEND_BY_REF : ZEND_SEND_BY_VAL, variadic ? 1 : 0, 0); } -inline uint32_t codeMask(zend_uchar code, bool nullable) +constexpr uint32_t codeMask(zend_uchar code, bool nullable) { uint32_t mask = code == _IS_BOOL ? MAY_BE_BOOL : (uint32_t) (1u << code); return mask | (nullable ? MAY_BE_NULL : 0); @@ -256,52 +256,75 @@ inline uint32_t codeMask(zend_uchar code, bool nullable) } // namespace detail /* an untyped parameter (ZEND_ARG_INFO) */ -inline Arg any(const char *name, bool byRef = false) +constexpr Arg any(const char *name, bool byRef = false) { return { name, detail::flagBits(byRef, false), nullptr }; } -inline Arg longArg(const char *name) +constexpr Arg longArg(const char *name) { return { name, detail::codeMask(IS_LONG, false) | detail::flagBits(false, false), nullptr }; } -inline Arg boolArg(const char *name) +constexpr Arg boolArg(const char *name) { return { name, detail::codeMask(_IS_BOOL, false) | detail::flagBits(false, false), nullptr }; } -inline Arg stringArg(const char *name, bool nullable = false) +constexpr Arg stringArg(const char *name, bool nullable = false) { return { name, detail::codeMask(IS_STRING, nullable) | detail::flagBits(false, false), nullptr }; } -inline Arg arrayArg(const char *name) +constexpr Arg arrayArg(const char *name) { return { name, detail::codeMask(IS_ARRAY, false) | detail::flagBits(false, false), nullptr }; } -inline Arg callableArg(const char *name) +constexpr Arg callableArg(const char *name) { return { name, MAY_BE_CALLABLE | detail::flagBits(false, false), nullptr }; } -inline Arg objectArg(const char *name, bool nullable = false) +constexpr Arg objectArg(const char *name, bool nullable = false) { return { name, detail::codeMask(IS_OBJECT, nullable) | detail::flagBits(false, false), nullptr }; } /* object of a specific class; className must be a persistent literal */ -inline Arg obj(const char *name, const char *className, bool nullable = false) +constexpr Arg obj(const char *name, const char *className, bool nullable = false) { return { name, _ZEND_TYPE_LITERAL_NAME_BIT | (nullable ? MAY_BE_NULL : 0) | detail::flagBits(false, false), className }; } -inline Arg variadicObj(const char *name, const char *className) +constexpr Arg variadicObj(const char *name, const char *className) { return { name, _ZEND_TYPE_LITERAL_NAME_BIT | detail::flagBits(false, true), className }; } +/* a parameter or return type the way a generated signature spells it + * (turbo-ext/src/generated): the MAY_BE_* mask, a persistent literal class + * name ("Foo", "Foo|Bar", "self") or nullptr, by reference / variadic, the + * PHP source of the default value or nullptr — the same bits the + * descriptors above produce */ +constexpr Arg typed(const char *name, uint32_t mask, const char *className = nullptr, bool byRef = false, bool variadic = false, const char *defaultValue = nullptr) +{ + return { name, mask | (className != nullptr ? _ZEND_TYPE_LITERAL_NAME_BIT : 0) | detail::flagBits(byRef, variadic), className, defaultValue }; +} + +/* a method's signature as generated from the PHP twin: name, ZEND_ACC_* + * flags, the required-parameter count, the parameters' arginfo and the + * declared return type (nullptr: none) */ +struct Sig +{ + const char *name; + uint32_t flags; + uint32_t requiredArgs; + const Arg *args; + uint32_t argc; + const Arg *returns; +}; + enum class PropertyKind { Long, @@ -622,23 +645,26 @@ class Class * name/type/by-ref/variadic exactly as the ZEND_ARG_* macros would. */ Class &method(const char *methodName, uint32_t flags, uint32_t requiredArgs, std::initializer_list args, zif_handler handler, const Arg *returns = NULL) + { + return method(methodName, flags, requiredArgs, args.begin(), args.size(), handler, returns); + } + + Class &method(const char *methodName, uint32_t flags, uint32_t requiredArgs, const Arg *args, size_t argc, zif_handler handler, const Arg *returns) { /* arginfo array: slot 0 is the return-info slot carrying the * required-args count, exactly as ZEND_BEGIN_ARG_INFO_EX emits. * A declared return type goes in the same slot's type — needed only * where the engine enforces it (implementing a userland interface). */ - auto *argInfo = (zend_internal_arg_info *) pemalloc(sizeof(zend_internal_arg_info) * (args.size() + 1), 1); + auto *argInfo = (zend_internal_arg_info *) pemalloc(sizeof(zend_internal_arg_info) * (argc + 1), 1); argInfo[0].name = (const char *) (uintptr_t) requiredArgs; argInfo[0].type.ptr = returns != NULL ? (void *) returns->className : NULL; argInfo[0].type.type_mask = returns != NULL ? returns->typeMask : 0; argInfo[0].default_value = NULL; - size_t i = 1; - for (const Arg &arg : args) { - argInfo[i].name = arg.name; - argInfo[i].type.ptr = (void *) arg.className; - argInfo[i].type.type_mask = arg.typeMask; - argInfo[i].default_value = arg.defaultValue; - i++; + for (size_t i = 0; i < argc; i++) { + argInfo[i + 1].name = args[i].name; + argInfo[i + 1].type.ptr = (void *) args[i].className; + argInfo[i + 1].type.type_mask = args[i].typeMask; + argInfo[i + 1].default_value = args[i].defaultValue; } zend_function_entry entry; @@ -646,7 +672,7 @@ class Class entry.fname = methodName; entry.handler = handler; entry.arg_info = argInfo; - entry.num_args = (uint32_t) args.size(); + entry.num_args = (uint32_t) argc; entry.flags = flags; entries.push_back(entry); return *this; @@ -663,6 +689,24 @@ class Class return method(methodName, flags, zp::required(), args, &detail::Bound::handle, returns); } + /* the method of a generated signature (turbo-ext/src/generated) */ + Class &method(const Sig &sig, zif_handler handler) + { + return method(sig.name, sig.flags, sig.requiredArgs, sig.args, sig.argc, handler, sig.returns); + } + + /* the method of a generated signature with a generated handler; the + * parameter kinds must match the signature — a mismatch (the twin's + * signature changed, the binding did not) refuses to load the module */ + template + Class &method(const Sig &sig) + { + if (UNEXPECTED(sig.argc != sizeof...(K) || sig.requiredArgs != zp::required())) { + zend_error_noreturn(E_CORE_ERROR, "phpstan_turbo: %s::%s() binds %u parameter kinds to a signature of %u (%u required)", name, sig.name, (unsigned) sizeof...(K), (unsigned) sig.argc, (unsigned) sig.requiredArgs); + } + return method(sig.name, sig.flags, sig.requiredArgs, sig.args, sig.argc, &detail::Bound::handle, sig.returns); + } + /* declaration order defines the OBJ_PROP_NUM slot, as with the macros */ Class &privateLongProperty(const char *propertyName, zend_long defaultValue) { From 5b86419e1dffea1046d04e4d9b76fec763555019 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Wed, 16 Sep 2026 08:34:31 +0200 Subject: [PATCH 10/12] Generate the trait registrar wiring of the native classes The generator also renders ptdecl::::registerTraits(cls) for a class whose twin uses traits: the pt_type_trait_*() registrars of those traits, in the twin's order. A class that runs exactly the registrars its twin uses calls it instead of listing them one by one; one that deliberately runs a different set, or runs them in another order, keeps its own calls. No native class uses a trait yet. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01P2Qsp9sqyymJYoqgoMkgLT --- .../Build/TurboDeclarationGenerator.php | 140 ++++++++++++++++++ turbo-ext/src/reg.h | 1 + 2 files changed, 141 insertions(+) diff --git a/build/PHPStan/Build/TurboDeclarationGenerator.php b/build/PHPStan/Build/TurboDeclarationGenerator.php index b07d02b1619..5558518a852 100644 --- a/build/PHPStan/Build/TurboDeclarationGenerator.php +++ b/build/PHPStan/Build/TurboDeclarationGenerator.php @@ -20,6 +20,8 @@ use function class_exists; use function count; use function dirname; +use function explode; +use function file_get_contents; use function implode; use function in_array; use function is_array; @@ -27,14 +29,23 @@ use function is_int; use function is_string; use function ksort; +use function max; +use function preg_match_all; use function preg_replace; use function sprintf; use function str_replace; use function str_starts_with; use function strlen; +use function strpos; +use function strrchr; +use function strrpos; +use function strtolower; use function strtoupper; use function substr; +use function trait_exists; +use function trim; use function var_export; +use const PREG_SET_ORDER; /** * Derives the declarative half of every shadowing class from its PHP twin @@ -64,6 +75,9 @@ final class TurboDeclarationGenerator 'major', 'minor', 'makedev', 'stdin', 'stdout', 'stderr', 'errno', 'assert', 'unix', 'linux', ]; + /** @var array|null pt_type_trait_*() registrar names, read from TypeTraits.cpp */ + private ?array $registrars = null; + /** * @param array $manifest */ @@ -213,6 +227,18 @@ private function renderClass(ReflectionClass $class, string $phpFile, string $st $out[] = '}'; } + $registrars = $this->traitRegistrars($class); + if ($registrars !== []) { + $out[] = ''; + $out[] = '/* the shared registrars of the traits the twin uses, in its own order (a used trait\'s own traits after it) */'; + $out[] = 'inline void registerTraits(reg::Class &cls)'; + $out[] = '{'; + foreach ($registrars as $registrar) { + $out[] = sprintf("\tpt_type_trait_%s(cls);", $registrar); + } + $out[] = '}'; + } + $signatures = $this->renderSignatures($class); if ($signatures !== []) { $out[] = ''; @@ -267,6 +293,120 @@ private function renderTrait(ReflectionClass $trait): string return implode("\n", $out); } + /** + * The shared trait registrars the twin's `use` declarations imply: its + * traits in declaration order, each followed by the traits it uses + * itself, mapped to their pt_type_trait_*() registrar and deduplicated + * (reg::Class::traitMethod() lets the first registrar win, as PHP lets + * the class body win over a used trait). + * + * @param ReflectionClass $class + * @return list + */ + private function traitRegistrars(ReflectionClass $class): array + { + $file = $class->getFileName(); + if ($file === false) { + return []; + } + $seen = []; + + return $this->flattenTraitUses($file, $seen); + } + + /** + * @param array $seen + * @return list + */ + private function flattenTraitUses(string $file, array &$seen): array + { + $registrars = []; + foreach ($this->traitUses($file) as $trait) { + if (isset($seen[$trait])) { + continue; + } + $seen[$trait] = true; + $registrar = $this->registrarOf($trait); + if ($registrar !== null) { + $registrars[] = $registrar; + } + if (!trait_exists($trait)) { + continue; + } + $traitFile = (new ReflectionClass($trait))->getFileName(); + if ($traitFile === false) { + continue; + } + + foreach ($this->flattenTraitUses($traitFile, $seen) as $nested) { + $registrars[] = $nested; + } + } + + return $registrars; + } + + /** + * The `use Trait;` and `use Trait { ... }` declarations of the first + * class-like in the file, resolved through its imports. + * + * @return list + */ + private function traitUses(string $file): array + { + $source = file_get_contents($file); + if ($source === false) { + return []; + } + $imports = []; + preg_match_all('~^use ([\w\\\\]+)(?:\s+as\s+(\w+))?;~m', $source, $importMatches, PREG_SET_ORDER); + foreach ($importMatches as $match) { + $imports[($match[2] ?? '') !== '' ? $match[2] : substr((string) strrchr('\\' . $match[1], '\\'), 1)] = $match[1]; + } + $start = max(strpos($source, 'trait ') === false ? -1 : strpos($source, 'trait '), strpos($source, 'class ') === false ? -1 : strpos($source, 'class ')); + if ($start < 0) { + return []; + } + $bodyStart = strpos($source, '{', $start); + $body = $bodyStart === false ? '' : substr($source, $bodyStart); + $names = []; + preg_match_all('~^\t+use ([\w\\\\,\s]+?)\s*(?:;|\{)~m', $body, $useMatches, PREG_SET_ORDER); + foreach ($useMatches as $match) { + foreach (explode(',', $match[1]) as $name) { + $name = trim($name); + if ($name === '') { + continue; + } + $names[] = $imports[$name] ?? $name; + } + } + + return $names; + } + + /** the pt_type_trait_*() registrar implementing a trait, if the extension has one */ + private function registrarOf(string $trait): ?string + { + if ($this->registrars === null) { + $source = (string) file_get_contents(dirname(__DIR__, 3) . '/turbo-ext/src/TypeTraits.cpp'); + $this->registrars = []; + preg_match_all('~^void pt_type_trait_(\w+)\(reg::Class &cls\)$~m', $source, $registrarMatches, PREG_SET_ORDER); + foreach ($registrarMatches as $match) { + $this->registrars[$match[1]] = true; + } + } + $separator = strrpos($trait, '\\'); + $short = $separator === false ? $trait : substr($trait, $separator + 1); + foreach ([preg_replace('~(Type)?Trait$~', '', $short), preg_replace('~Trait$~', '', $short)] as $base) { + $name = strtolower((string) preg_replace('~(?registrars[$name])) { + return $name; + } + } + + return null; + } + /** * The generated signature of every method declared in the class-like's * own file (a used trait's methods are declared in the trait's). diff --git a/turbo-ext/src/reg.h b/turbo-ext/src/reg.h index a48329084fc..bff7b00a92c 100644 --- a/turbo-ext/src/reg.h +++ b/turbo-ext/src/reg.h @@ -99,6 +99,7 @@ struct Base> template constexpr bool is = std::is_same_v::type, Kind>; + } // namespace detail template From 1baaca6eff85bb9927e007823e23bbd9c4a28293 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Sun, 20 Sep 2026 23:05:24 +0200 Subject: [PATCH 11/12] Drop the proof-of-concept patch for the vendored BetterReflection The patch recorded three measured changes to ondrejmirtes/better-reflection (the getName() memo, the attribute-less short-circuit in getAttributesByName(), the cached-member checks hoisted in ReflectionClass) because vendor/ is git-ignored and they could not be committed. Nothing applies the file, so it only goes stale as the vendored code moves - those changes belong in a pull request against that repository. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_017MvPby652L7wUqGAHEiEcN --- turbo-ext/poc/README.md | 21 --- .../better-reflection-reflection-memo.patch | 165 ------------------ 2 files changed, 186 deletions(-) delete mode 100644 turbo-ext/poc/README.md delete mode 100644 turbo-ext/poc/better-reflection-reflection-memo.patch diff --git a/turbo-ext/poc/README.md b/turbo-ext/poc/README.md deleted file mode 100644 index 9ed5973acce..00000000000 --- a/turbo-ext/poc/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Proof-of-concept patches for vendored packages - -`vendor/` is git-ignored, so a PHP-side fix in a vendored package cannot be -committed directly. Each patch here is the `git diff` of the original vendored -file against the edited one (paths relative to the repository root, apply with -`git apply turbo-ext/poc/.patch`) and records its measured effect on the -PHPStan self-analysis benchmark, so it can be reviewed and contributed upstream. - -- `better-reflection-reflection-memo.patch` (ondrejmirtes/better-reflection): - memoizes `ReflectionFunctionAbstract::getName()` (4.8M calls per - self-analysis run recomputed namespace + short name; the memo is reset by - `ReflectionMethod::withImplementingClass()` since the alias name changes), - short-circuits `getAttributesByName()` / `ReflectionAttributeHelper::filterAttributesByName()` - on the common attribute-less case (no closure + `array_filter` per call), and - reads `ReflectionClass::$cachedMethods/$cachedConstants/$cachedProperties` - before constructing the `AlreadyVisitedClasses` guard in `getMethod()`, - `getMethods()`, `getConstants()` and `getProperties()` (1.6M throw-away - objects per run; the guard mutates in place, so it cannot be shared). - Measured effect on the self-analysis A/B (src/Analyser + src/Rules + src/Type, - extension on, 6 interleaved pairs): -1.6% user CPU (44.29s -> 43.57s) - together with the `PhpMethodReflection::getName()` memo in src/. diff --git a/turbo-ext/poc/better-reflection-reflection-memo.patch b/turbo-ext/poc/better-reflection-reflection-memo.patch deleted file mode 100644 index e25f2c310ab..00000000000 --- a/turbo-ext/poc/better-reflection-reflection-memo.patch +++ /dev/null @@ -1,165 +0,0 @@ -diff --git a/vendor/ondrejmirtes/better-reflection/src/Reflection/Attribute/ReflectionAttributeHelper.php b/vendor/ondrejmirtes/better-reflection/src/Reflection/Attribute/ReflectionAttributeHelper.php -index 4034e01..909ba70 100644 ---- a/vendor/ondrejmirtes/better-reflection/src/Reflection/Attribute/ReflectionAttributeHelper.php -+++ b/vendor/ondrejmirtes/better-reflection/src/Reflection/Attribute/ReflectionAttributeHelper.php -@@ -67,7 +67,20 @@ class ReflectionAttributeHelper - */ - public static function filterAttributesByName(array $attributes, string $name): array - { -- return array_values(array_filter($attributes, static fn (ReflectionAttribute $attribute): bool => $attribute->getName() === $name)); -+ if ($attributes === []) { -+ return []; -+ } -+ -+ $filtered = []; -+ foreach ($attributes as $attribute) { -+ if ($attribute->getName() !== $name) { -+ continue; -+ } -+ -+ $filtered[] = $attribute; -+ } -+ -+ return $filtered; - } - - /** -diff --git a/vendor/ondrejmirtes/better-reflection/src/Reflection/ReflectionClass.php b/vendor/ondrejmirtes/better-reflection/src/Reflection/ReflectionClass.php -index 43e161d..44c78f4 100644 ---- a/vendor/ondrejmirtes/better-reflection/src/Reflection/ReflectionClass.php -+++ b/vendor/ondrejmirtes/better-reflection/src/Reflection/ReflectionClass.php -@@ -689,7 +689,7 @@ class ReflectionClass implements Reflection - */ - public function getMethods(int $filter = 0): array - { -- $methods = $this->getMethodsIndexedByLowercasedName(AlreadyVisitedClasses::createEmpty()); -+ $methods = $this->cachedMethods ?? $this->getMethodsIndexedByLowercasedName(AlreadyVisitedClasses::createEmpty()); - - if ($filter !== 0) { - $methods = array_filter( -@@ -823,10 +823,9 @@ class ReflectionClass implements Reflection - */ - public function getMethod(string $methodName): ?\PHPStan\BetterReflection\Reflection\ReflectionMethod - { -- $lowercaseMethodName = strtolower($methodName); -- $methods = $this->getMethodsIndexedByLowercasedName(AlreadyVisitedClasses::createEmpty()); -+ $methods = $this->cachedMethods ?? $this->getMethodsIndexedByLowercasedName(AlreadyVisitedClasses::createEmpty()); - -- return $methods[$lowercaseMethodName] ?? null; -+ return $methods[strtolower($methodName)] ?? null; - } - - /** -@@ -909,7 +908,7 @@ class ReflectionClass implements Reflection - */ - public function getConstants(int $filter = 0): array - { -- $constants = $this->getConstantsConsideringAlreadyVisitedClasses(AlreadyVisitedClasses::createEmpty()); -+ $constants = $this->cachedConstants ?? $this->getConstantsConsideringAlreadyVisitedClasses(AlreadyVisitedClasses::createEmpty()); - - if ($filter === 0) { - return $constants; -@@ -1139,7 +1138,7 @@ class ReflectionClass implements Reflection - */ - public function getProperties(int $filter = 0): array - { -- $properties = $this->getPropertiesConsideringAlreadyVisitedClasses(AlreadyVisitedClasses::createEmpty()); -+ $properties = $this->cachedProperties ?? $this->getPropertiesConsideringAlreadyVisitedClasses(AlreadyVisitedClasses::createEmpty()); - - if ($filter === 0) { - return $properties; -@@ -2053,7 +2052,11 @@ class ReflectionClass implements Reflection - /** @return list */ - public function getAttributesByName(string $name): array - { -- return ReflectionAttributeHelper::filterAttributesByName($this->getAttributes(), $name); -+ if ($this->attributes === []) { -+ return []; -+ } -+ -+ return ReflectionAttributeHelper::filterAttributesByName($this->attributes, $name); - } - - /** -diff --git a/vendor/ondrejmirtes/better-reflection/src/Reflection/ReflectionFunctionAbstract.php b/vendor/ondrejmirtes/better-reflection/src/Reflection/ReflectionFunctionAbstract.php -index 43dade2..3ac37e8 100644 ---- a/vendor/ondrejmirtes/better-reflection/src/Reflection/ReflectionFunctionAbstract.php -+++ b/vendor/ondrejmirtes/better-reflection/src/Reflection/ReflectionFunctionAbstract.php -@@ -97,6 +97,9 @@ trait ReflectionFunctionAbstract - /** @psalm-allow-private-mutation */ - private bool $isVariadic = false; - -+ /** @var non-empty-string|null */ -+ private ?string $cachedName = null; -+ - /** - * @return array - */ -@@ -242,13 +245,23 @@ trait ReflectionFunctionAbstract - */ - public function getName(): string - { -+ if ($this->cachedName !== null) { -+ return $this->cachedName; -+ } -+ - $namespace = $this->getNamespaceName(); - - if ($namespace === null) { -- return $this->getShortName(); -+ return $this->cachedName = $this->getShortName(); - } - -- return $namespace . '\\' . $this->getShortName(); -+ return $this->cachedName = $namespace . '\\' . $this->getShortName(); -+ } -+ -+ /** @internal */ -+ protected function resetCachedName(): void -+ { -+ $this->cachedName = null; - } - - /** -@@ -659,7 +672,11 @@ trait ReflectionFunctionAbstract - /** @return list */ - public function getAttributesByName(string $name): array - { -- return ReflectionAttributeHelper::filterAttributesByName($this->getAttributes(), $name); -+ if ($this->attributes === []) { -+ return []; -+ } -+ -+ return ReflectionAttributeHelper::filterAttributesByName($this->attributes, $name); - } - - /** -diff --git a/vendor/ondrejmirtes/better-reflection/src/Reflection/ReflectionMethod.php b/vendor/ondrejmirtes/better-reflection/src/Reflection/ReflectionMethod.php -index 4e7e09b..77cf6a9 100644 ---- a/vendor/ondrejmirtes/better-reflection/src/Reflection/ReflectionMethod.php -+++ b/vendor/ondrejmirtes/better-reflection/src/Reflection/ReflectionMethod.php -@@ -248,6 +248,7 @@ class ReflectionMethod - { - $clone = clone $this; - -+ $clone->resetCachedName(); - $clone->aliasName = $aliasName; - $clone->modifiers = $modifiers; - $clone->implementingClass = $implementingClass; -diff --git a/vendor/ondrejmirtes/better-reflection/src/Reflection/ReflectionProperty.php b/vendor/ondrejmirtes/better-reflection/src/Reflection/ReflectionProperty.php -index 583efe3..3e6c455 100644 ---- a/vendor/ondrejmirtes/better-reflection/src/Reflection/ReflectionProperty.php -+++ b/vendor/ondrejmirtes/better-reflection/src/Reflection/ReflectionProperty.php -@@ -567,7 +567,11 @@ class ReflectionProperty - /** @return list */ - public function getAttributesByName(string $name): array - { -- return ReflectionAttributeHelper::filterAttributesByName($this->getAttributes(), $name); -+ if ($this->attributes === []) { -+ return []; -+ } -+ -+ return ReflectionAttributeHelper::filterAttributesByName($this->attributes, $name); - } - - /** From b6dc756009d31376fa02c4940abc6fda083e3453 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Mon, 21 Sep 2026 18:17:45 +0200 Subject: [PATCH 12/12] Bump expected turbo version --- src/Turbo/TurboExtensionEnabler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Turbo/TurboExtensionEnabler.php b/src/Turbo/TurboExtensionEnabler.php index 6ea134e4b7f..5648937e460 100644 --- a/src/Turbo/TurboExtensionEnabler.php +++ b/src/Turbo/TurboExtensionEnabler.php @@ -32,7 +32,7 @@ final class TurboExtensionEnabler { - public const EXPECTED_EXTENSION_VERSION = '36c579f'; + public const EXPECTED_EXTENSION_VERSION = '5b86419'; private static bool $active = false;