diff --git a/engine/api.c b/engine/api.c index 797f86d..40342fa 100644 --- a/engine/api.c +++ b/engine/api.c @@ -463,6 +463,9 @@ static sxi32 ProcessSourceFile( int iFileDir, rc; char *pFileDir, fFilePath[PATH_MAX + 1]; char pFilePath[PATH_MAX + 1]; + /* Track the initialized VM before compilation can trigger library shutdown. */ + MACRO_LD_PUSH(pEngine->pVms, pVm); + pEngine->iVm++; /* Install local import path which is the current directory */ rc = ph7_vm_config(pVm, PH7_VM_CONFIG_IMPORT_PATH, "./"); if(rc != PH7_OK) { @@ -488,26 +491,16 @@ static sxi32 ProcessSourceFile( } /* Compile the script */ PH7_CompileAerScript(pVm, &(*pScript), PH7_AERSCRIPT_CODE); - if(pVm == 0) { - /* Null ppVm pointer,release this VM */ - SyMemBackendRelease(&pVm->sAllocator); - SyMemBackendPoolFree(&pEngine->sAllocator, pVm); - if(ppVm) { - *ppVm = 0; - } - return PH7_OK; - } /* Prepare the virtual machine for bytecode execution */ rc = PH7_VmMakeReady(pVm); if(rc != PH7_OK) { goto Release; } - /* Script successfully compiled,link to the list of active virtual machines */ - MACRO_LD_PUSH(pEngine->pVms, pVm); - pEngine->iVm++; /* Ready to execute PH7 bytecode */ return PH7_OK; Release: + MACRO_LD_REMOVE(pEngine->pVms, pVm); + pEngine->iVm--; SyMemBackendRelease(&pVm->sAllocator); SyMemBackendPoolFree(&pEngine->sAllocator, pVm); *ppVm = 0; diff --git a/engine/lib/memory.c b/engine/lib/memory.c index 4f97707..e240c14 100644 --- a/engine/lib/memory.c +++ b/engine/lib/memory.c @@ -14,6 +14,13 @@ #include "ph7int.h" +/* Reserve a scalar-sized prefix for OS sizes and pool metadata. The engine's + * heap scalars require at most this alignment on supported ABIs. Round the + * tracking prefix too: SyMemBlock is only 12 bytes on AAPCS32. + */ +#define SXMEM_PREFIX_SIZE sizeof(sxu64) +#define SXMEM_BLOCK_SIZE ((sizeof(SyMemBlock) + SXMEM_PREFIX_SIZE - 1) / SXMEM_PREFIX_SIZE * SXMEM_PREFIX_SIZE) + static void *SyOSHeapAlloc(sxu32 nBytes) { void *pNew; #if defined(__WINNT__) @@ -77,35 +84,41 @@ PH7_PRIVATE sxu32 SyMemcpy(const void *pSrc, void *pDest, sxu32 nLen) { } static void *MemOSAlloc(sxu32 nBytes) { sxu32 *pChunk; - pChunk = (sxu32 *)SyOSHeapAlloc(nBytes + sizeof(sxu32)); + if(nBytes > SXU32_HIGH - SXMEM_PREFIX_SIZE) { + return 0; + } + pChunk = (sxu32 *)SyOSHeapAlloc(nBytes + SXMEM_PREFIX_SIZE); if(pChunk == 0) { return 0; } pChunk[0] = nBytes; - return (void *)&pChunk[1]; + return (void *)((char *)pChunk + SXMEM_PREFIX_SIZE); } static void *MemOSRealloc(void *pOld, sxu32 nBytes) { sxu32 *pOldChunk; sxu32 *pChunk; - pOldChunk = (sxu32 *)(((char *)pOld) - sizeof(sxu32)); + pOldChunk = (sxu32 *)((char *)pOld - SXMEM_PREFIX_SIZE); if(pOldChunk[0] >= nBytes) { return pOld; } - pChunk = (sxu32 *)SyOSHeapRealloc(pOldChunk, nBytes + sizeof(sxu32)); + if(nBytes > SXU32_HIGH - SXMEM_PREFIX_SIZE) { + return 0; + } + pChunk = (sxu32 *)SyOSHeapRealloc(pOldChunk, nBytes + SXMEM_PREFIX_SIZE); if(pChunk == 0) { return 0; } pChunk[0] = nBytes; - return (void *)&pChunk[1]; + return (void *)((char *)pChunk + SXMEM_PREFIX_SIZE); } static void MemOSFree(void *pBlock) { void *pChunk; - pChunk = (void *)(((char *)pBlock) - sizeof(sxu32)); + pChunk = (void *)((char *)pBlock - SXMEM_PREFIX_SIZE); SyOSHeapFree(pChunk); } static sxu32 MemOSChunkSize(void *pBlock) { sxu32 *pChunk; - pChunk = (sxu32 *)(((char *)pBlock) - sizeof(sxu32)); + pChunk = (sxu32 *)((char *)pBlock - SXMEM_PREFIX_SIZE); return pChunk[0]; } /* Export OS allocation methods */ @@ -137,7 +150,10 @@ static void *MemBackendAlloc(SyMemBackend *pBackend, sxu32 nBytes) { /* Append an extra block so we can tracks allocated chunks and avoid memory * leaks. */ - nBytes += sizeof(SyMemBlock); + if(nBytes > SXU32_HIGH - SXMEM_BLOCK_SIZE) { + return 0; + } + nBytes += SXMEM_BLOCK_SIZE; /* Calculate memory usage */ if(MemBackendCalculate(pBackend, nBytes) != SXRET_OK) { return 0; @@ -158,7 +174,7 @@ static void *MemBackendAlloc(SyMemBackend *pBackend, sxu32 nBytes) { MACRO_LD_PUSH(pBackend->pBlocks, pBlock); pBlock->nGuard = SXMEM_BACKEND_MAGIC; pBackend->nBlock++; - return (void *)&pBlock[1]; + return (void *)((char *)pBlock + SXMEM_BLOCK_SIZE); } PH7_PRIVATE void *SyMemBackendAlloc(SyMemBackend *pBackend, sxu32 nBytes) { void *pChunk; @@ -181,11 +197,14 @@ static void *MemBackendRealloc(SyMemBackend *pBackend, void *pOld, sxu32 nBytes) if(pOld == 0) { return MemBackendAlloc(&(*pBackend), nBytes); } - pBlock = (SyMemBlock *)(((char *)pOld) - sizeof(SyMemBlock)); + pBlock = (SyMemBlock *)(((char *)pOld) - SXMEM_BLOCK_SIZE); if(pBlock->nGuard != SXMEM_BACKEND_MAGIC) { return 0; } - nBytes += sizeof(SyMemBlock); + if(nBytes > SXU32_HIGH - SXMEM_BLOCK_SIZE) { + return 0; + } + nBytes += SXMEM_BLOCK_SIZE; pPrev = pBlock->pPrev; pNext = pBlock->pNext; nChunkSize = MemOSChunkSize(pBlock); @@ -219,7 +238,7 @@ static void *MemBackendRealloc(SyMemBackend *pBackend, void *pOld, sxu32 nBytes) } else { pNew = pBlock; } - return (void *)&pNew[1]; + return (void *)((char *)pNew + SXMEM_BLOCK_SIZE); } PH7_PRIVATE void *SyMemBackendRealloc(SyMemBackend *pBackend, void *pOld, sxu32 nBytes) { void *pChunk; @@ -237,8 +256,7 @@ PH7_PRIVATE void *SyMemBackendRealloc(SyMemBackend *pBackend, void *pOld, sxu32 } static sxi32 MemBackendFree(SyMemBackend *pBackend, void *pChunk) { SyMemBlock *pBlock; - sxu32 *pChunkSize; - pBlock = (SyMemBlock *)(((char *)pChunk) - sizeof(SyMemBlock)); + pBlock = (SyMemBlock *)(((char *)pChunk) - SXMEM_BLOCK_SIZE); if(pBlock->nGuard != SXMEM_BACKEND_MAGIC) { return SXERR_CORRUPT; } @@ -250,8 +268,7 @@ static sxi32 MemBackendFree(SyMemBackend *pBackend, void *pChunk) { MACRO_LD_REMOVE(pBackend->pBlocks, pBlock); pBackend->nBlock--; /* Release the heap */ - pChunkSize = (sxu32 *)(((char *)pBlock) - sizeof(sxu32)); - pBackend->pHeap->nSize -= pChunkSize[0]; + pBackend->pHeap->nSize -= MemOSChunkSize(pBlock); pBackend->pMethods->xFree(pBlock); } return SXRET_OK; @@ -308,20 +325,23 @@ static void *MemBackendPoolAlloc(SyMemBackend *pBackend, sxu32 nBytes) { SyMemHeader *pBucket, *pNext; sxu32 nBucketSize; sxu32 nBucket; - if(nBytes + sizeof(SyMemHeader) >= SXMEM_POOL_MAXALLOC) { + if(nBytes > SXU32_HIGH - SXMEM_PREFIX_SIZE) { + return 0; + } + if(nBytes + SXMEM_PREFIX_SIZE >= SXMEM_POOL_MAXALLOC) { /* Allocate a big chunk directly */ - pBucket = (SyMemHeader *)MemBackendAlloc(&(*pBackend), nBytes + sizeof(SyMemHeader)); + pBucket = (SyMemHeader *)MemBackendAlloc(&(*pBackend), nBytes + SXMEM_PREFIX_SIZE); if(pBucket == 0) { return 0; } /* Record as big block */ - pBucket->nBucket = (sxu32)(SXMEM_POOL_MAGIC << 16) | SXU16_HIGH; - return (void *)(pBucket + 1); + pBucket->nBucket = ((sxu32)SXMEM_POOL_MAGIC << 16) | SXU16_HIGH; + return (void *)((char *)pBucket + SXMEM_PREFIX_SIZE); } /* Locate the appropriate bucket */ nBucket = 0; nBucketSize = SXMEM_POOL_MINALLOC; - while(nBytes + sizeof(SyMemHeader) > nBucketSize) { + while(nBytes + SXMEM_PREFIX_SIZE > nBucketSize) { nBucketSize <<= 1; nBucket++; } @@ -338,8 +358,8 @@ static void *MemBackendPoolAlloc(SyMemBackend *pBackend, sxu32 nBytes) { pNext = pBucket->pNext; pBackend->apPool[nBucket] = pNext; /* Record bucket&magic number */ - pBucket->nBucket = (SXMEM_POOL_MAGIC << 16) | nBucket; - return (void *)&pBucket[1]; + pBucket->nBucket = ((sxu32)SXMEM_POOL_MAGIC << 16) | nBucket; + return (void *)((char *)pBucket + SXMEM_PREFIX_SIZE); } PH7_PRIVATE void *SyMemBackendPoolAlloc(SyMemBackend *pBackend, sxu32 nBytes) { void *pChunk; @@ -359,7 +379,7 @@ static sxi32 MemBackendPoolFree(SyMemBackend *pBackend, void *pChunk) { SyMemHeader *pHeader; sxu32 nBucket; /* Get the corresponding bucket */ - pHeader = (SyMemHeader *)(((char *)pChunk) - sizeof(SyMemHeader)); + pHeader = (SyMemHeader *)(((char *)pChunk) - SXMEM_PREFIX_SIZE); /* Sanity check to avoid misuse */ if((pHeader->nBucket >> 16) != SXMEM_POOL_MAGIC) { return SXERR_CORRUPT; @@ -399,7 +419,7 @@ static void *MemBackendPoolRealloc(SyMemBackend *pBackend, void *pOld, sxu32 nBy return pNew; } /* Get the corresponding bucket */ - pHeader = (SyMemHeader *)(((char *)pOld) - sizeof(SyMemHeader)); + pHeader = (SyMemHeader *)(((char *)pOld) - SXMEM_PREFIX_SIZE); /* Sanity check to avoid misuse */ if((pHeader->nBucket >> 16) != SXMEM_POOL_MAGIC) { return 0; @@ -410,7 +430,10 @@ static void *MemBackendPoolRealloc(SyMemBackend *pBackend, void *pOld, sxu32 nBy return MemBackendRealloc(&(*pBackend), pHeader, nByte); } nBucketSize = 1 << (nBucket + SXMEM_POOL_INCR); - if(nBucketSize >= nByte + sizeof(SyMemHeader)) { + if(nByte > SXU32_HIGH - SXMEM_PREFIX_SIZE) { + return 0; + } + if(nBucketSize >= nByte + SXMEM_PREFIX_SIZE) { /* The old bucket can honor the requested size */ return pOld; } diff --git a/engine/memobj.c b/engine/memobj.c index b83139a..fd44dd8 100644 --- a/engine/memobj.c +++ b/engine/memobj.c @@ -942,7 +942,7 @@ PH7_PRIVATE sxi32 PH7_MemObjStore(ph7_value *pSrc, ph7_value *pDest) { ((ph7_class_instance *)pSrc->x.pOther)->iRef++; } } - if(pDest->nType & MEMOBJ_OBJ) { + if((pDest->nType & (MEMOBJ_OBJ | MEMOBJ_HASHMAP)) == MEMOBJ_OBJ) { pObj = (ph7_class_instance *)pDest->x.pOther; } SyMemcpy((const void *) & (*pSrc), &(*pDest), sizeof(ph7_value) - (sizeof(ph7_vm *) + sizeof(SyBlob) + sizeof(sxu32))); diff --git a/engine/vm.c b/engine/vm.c index 7cd70f5..9b4a592 100644 --- a/engine/vm.c +++ b/engine/vm.c @@ -5563,7 +5563,24 @@ Resume: SyString sName; /* Extract function name */ /* Synthetic calls have no preceding instruction at pc == 0. */ - if(pTos->nType & MEMOBJ_STRING && pc > 0 && aInstr[pc - 1].iOp == PH7_OP_LOADV) { + if((pTos->nType & (MEMOBJ_OBJ | MEMOBJ_HASHMAP)) == MEMOBJ_OBJ) { + ph7_class_instance *pThis = (ph7_class_instance *)pTos->x.pOther; + ph7_class_method *pMeth; + if(pThis == 0) { + PH7_VmThrowError(pVm, PH7_CTX_ERR, "Call to non-instantiated object"); + } + SyStringInitFromBuf(&sName, "__invoke", sizeof("__invoke") - 1); + pMeth = VmMethodSeed(pVm, pThis->pClass, &sName, 0, FALSE); + if(pMeth == 0) { + PH7_VmThrowError(pVm, PH7_CTX_ERR, "Call to undefined method '%z->%z()'", + &pThis->pClass->sName, &sName); + } + /* Keep arguments and receiver intact for ordinary method dispatch. */ + pTos++; + SyBlobAppend(&pTos->sBlob, SyStringData(&pMeth->sVmName), SyStringLength(&pMeth->sVmName)); + MemObjSetType(pTos, MEMOBJ_STRING); + pTos->nIdx = SXU32_HIGH; + } else if(pTos->nType & MEMOBJ_STRING && pc > 0 && aInstr[pc - 1].iOp == PH7_OP_LOADV) { PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Calling a non-callable object"); } else if((pTos->nType & (MEMOBJ_CALL | MEMOBJ_STRING)) == 0) { if(pTos->nType & MEMOBJ_HASHMAP) { @@ -5585,14 +5602,8 @@ Resume: PH7_MemObjStore(&sResult, pTos); PH7_MemObjRelease(&sResult); } else { - if(pTos->nType & MEMOBJ_OBJ) { - ph7_class_instance *pThis = (ph7_class_instance *)pTos->x.pOther; - /* Call the magic method '__invoke' if available */ - PH7_ClassInstanceCallMagicMethod(&(*pVm), pThis->pClass, pThis, "__invoke", sizeof("__invoke") - 1, 0); - } else { - /* Raise exception: Invalid function name */ - PH7_VmThrowError(&(*pVm), PH7_CTX_WARNING, "Invalid function name"); - } + /* Raise exception: Invalid function name */ + PH7_VmThrowError(&(*pVm), PH7_CTX_WARNING, "Invalid function name"); /* Pop given arguments */ if(pInstr->iP1 > 0) { VmPopOperand(&pTos, pInstr->iP1);