diff options
Diffstat (limited to 'src/VBox/Storage/testcase')
27 files changed, 6456 insertions, 1870 deletions
diff --git a/src/VBox/Storage/testcase/Makefile.kmk b/src/VBox/Storage/testcase/Makefile.kmk index 3d0f2b31..9d44e45c 100644 --- a/src/VBox/Storage/testcase/Makefile.kmk +++ b/src/VBox/Storage/testcase/Makefile.kmk @@ -22,7 +22,7 @@ include $(KBUILD_PATH)/subheader.kmk # vditool - useful too for manipulating VDIs, but now pretty obsolete and # probably will go away soon. Testcase only now. # -ifdef VBOX_WITH_TESTCASES +if defined(VBOX_WITH_TESTCASES) || defined(VBOX_WITH_VDITOOL) PROGRAMS += vditool vditool_TEMPLATE = VBOXR3TSTEXE vditool_LIBS = $(LIB_DDU) @@ -33,7 +33,7 @@ endif # Basic testcases for the VD code. # ifdef VBOX_WITH_TESTCASES - PROGRAMS += tstVD tstVD-2 tstVDCopy tstVDSnap tstVDShareable vbox-img + PROGRAMS += tstVD tstVD-2 tstVDCopy tstVDSnap tstVDShareable tstVD_TEMPLATE = VBOXR3TSTEXE tstVD_SOURCES = tstVD.cpp @@ -52,9 +52,14 @@ ifdef VBOX_WITH_TESTCASES tstVDIo_TEMPLATE = VBOXR3TSTEXE tstVDIo_SOURCES = tstVDIo.cpp \ + VDIoBackend.cpp \ VDIoBackendMem.cpp \ VDMemDisk.cpp \ - VDIoRnd.cpp + VDIoRnd.cpp \ + VDScript.cpp \ + VDScriptAst.cpp \ + VDScriptChecker.cpp \ + VDScriptInterp.cpp tstVDIo_LIBS = \ $(LIB_DDU) \ $(PATH_STAGE_LIB)/StorageDbgLib$(VBOX_SUFF_LIB) @@ -70,27 +75,32 @@ ifdef VBOX_WITH_TESTCASES tstVDSnap_TEMPLATE = VBOXR3TSTEXE tstVDSnap_LIBS = $(LIB_DDU) tstVDSnap_SOURCES = tstVDSnap.cpp +endif + +if defined(VBOX_WITH_TESTCASES) || defined(VBOX_WITH_VBOX_IMG) + PROGRAMS += vbox-img # # vbox-img - static because it migth be used as at standalone tool. # - vbox-img_TEMPLATE = VBOXR3STATIC - vbox-img_DEFS += IN_VBOXDDU IN_VBOXDDU_STATIC VBOX_HDD_NO_DYNAMIC_BACKENDS IN_RT_R3 + vbox-img_TEMPLATE = VBoxR3Static + vbox-img_DEFS += IN_VBOXDDU IN_VBOXDDU_STATIC VBOX_HDD_NO_DYNAMIC_BACKENDS vbox-img_SOURCES = \ vbox-img.cpp \ - $(VBOX_PATH_STORAGE_SRC)/VD.cpp \ - $(VBOX_PATH_STORAGE_SRC)/VDVfs.cpp \ - $(VBOX_PATH_STORAGE_SRC)/VDI.cpp \ - $(VBOX_PATH_STORAGE_SRC)/VMDK.cpp \ - $(VBOX_PATH_STORAGE_SRC)/VHD.cpp \ - $(VBOX_PATH_STORAGE_SRC)/DMG.cpp \ - $(VBOX_PATH_STORAGE_SRC)/Parallels.cpp \ - $(VBOX_PATH_STORAGE_SRC)/ISCSI.cpp \ - $(VBOX_PATH_STORAGE_SRC)/RAW.cpp \ - $(VBOX_PATH_STORAGE_SRC)/QED.cpp \ - $(VBOX_PATH_STORAGE_SRC)/QCOW.cpp \ - $(VBOX_PATH_STORAGE_SRC)/VHDX.cpp \ - $(VBOX_PATH_STORAGE_SRC)/VCICache.cpp + ../VD.cpp \ + ../VDVfs.cpp \ + ../VDI.cpp \ + ../VMDK.cpp \ + ../VHD.cpp \ + ../DMG.cpp \ + ../Parallels.cpp \ + ../ISCSI.cpp \ + ../RAW.cpp \ + ../QED.cpp \ + ../QCOW.cpp \ + ../VHDX.cpp \ + ../VCICache.cpp \ + ../VDIfVfs.cpp vbox-img_LIBS = \ $(VBOX_LIB_RUNTIME_STATIC) if1of ($(KBUILD_TARGET),os2 win) @@ -104,6 +114,9 @@ ifdef VBOX_WITH_TESTCASES endif ifeq ($(KBUILD_TARGET),linux) vbox-img_LIBS += crypt + ifdef SDK_VBOX_LIBXML2_LIBS + vbox-img_LIBS += xml2 + endif else if1of ($(KBUILD_TARGET),darwin freebsd) vbox-img_LIBS += iconv else ifeq ($(KBUILD_TARGET),win) diff --git a/src/VBox/Storage/testcase/VDDefs.h b/src/VBox/Storage/testcase/VDDefs.h new file mode 100644 index 00000000..729a8fff --- /dev/null +++ b/src/VBox/Storage/testcase/VDDefs.h @@ -0,0 +1,38 @@ +/** $Id: VDDefs.h $ */ +/** @file + * + * VBox HDD container test utility, common definitions. + */ + +/* + * Copyright (C) 2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ +#ifndef __VDDefs_h__ +#define __VDDefs_h__ + +#include <iprt/sg.h> + +/** + * I/O transfer direction. + */ +typedef enum VDIOTXDIR +{ + /** Read. */ + VDIOTXDIR_READ = 0, + /** Write. */ + VDIOTXDIR_WRITE, + /** Flush. */ + VDIOTXDIR_FLUSH, + /** Invalid. */ + VDIOTXDIR_INVALID +} VDIOTXDIR; + +#endif /* __VDDefs_h__ */ diff --git a/src/VBox/Storage/testcase/VDIoBackend.cpp b/src/VBox/Storage/testcase/VDIoBackend.cpp new file mode 100644 index 00000000..6458d7df --- /dev/null +++ b/src/VBox/Storage/testcase/VDIoBackend.cpp @@ -0,0 +1,283 @@ +/** $Id: VDIoBackend.cpp $ */ +/** @file + * + * VBox HDD container test utility, I/O backend API + */ + +/* + * Copyright (C) 2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ +#define LOGGROUP LOGGROUP_DEFAULT /** @todo: Log group */ +#include <iprt/err.h> +#include <iprt/log.h> +#include <iprt/assert.h> +#include <iprt/asm.h> +#include <iprt/mem.h> +#include <iprt/file.h> +#include <iprt/aiomgr.h> +#include <iprt/string.h> + +#include "VDIoBackend.h" +#include "VDMemDisk.h" +#include "VDIoBackendMem.h" + +typedef struct VDIOBACKEND +{ + /** Memory I/O backend handle. */ + PVDIOBACKENDMEM pIoMem; + /** Async I/O manager. */ + RTAIOMGR hAioMgr; + /** Users of the memory backend. */ + volatile uint32_t cRefsIoMem; + /** Users of the file backend. */ + volatile uint32_t cRefsFile; +} VDIOBACKEND; + +typedef struct VDIOSTORAGE +{ + /** Pointer to the I/O backend parent. */ + PVDIOBACKEND pIoBackend; + /** Completion callback. */ + PFNVDIOCOMPLETE pfnComplete; + /** Flag whether this storage is backed by a file or memory.disk. */ + bool fMemory; + /** Type dependent data. */ + union + { + /** Memory disk handle. */ + PVDMEMDISK pMemDisk; + struct + { + /** file handle. */ + RTFILE hFile; + /** I/O manager file handle. */ + RTAIOMGRFILE hAioMgrFile; + } File; + } u; +} VDIOSTORAGE; + +static DECLCALLBACK(void) vdIoBackendFileIoComplete(RTAIOMGRFILE hAioMgrFile, int rcReq, void *pvUser) +{ + PVDIOSTORAGE pIoStorage = (PVDIOSTORAGE)RTAioMgrFileGetUser(hAioMgrFile); + pIoStorage->pfnComplete(pvUser, rcReq); +} + +int VDIoBackendCreate(PPVDIOBACKEND ppIoBackend) +{ + int rc = VINF_SUCCESS; + PVDIOBACKEND pIoBackend; + + pIoBackend = (PVDIOBACKEND)RTMemAllocZ(sizeof(VDIOBACKEND)); + if (pIoBackend) + { + pIoBackend->hAioMgr = NIL_RTAIOMGR; + *ppIoBackend = pIoBackend; + } + else + rc = VERR_NO_MEMORY; + + return rc; +} + +void VDIoBackendDestroy(PVDIOBACKEND pIoBackend) +{ + if (pIoBackend->pIoMem) + VDIoBackendMemDestroy(pIoBackend->pIoMem); + if (pIoBackend->hAioMgr) + RTAioMgrRelease(pIoBackend->hAioMgr); + RTMemFree(pIoBackend); +} + +int VDIoBackendStorageCreate(PVDIOBACKEND pIoBackend, const char *pszBackend, + const char *pszName, PFNVDIOCOMPLETE pfnComplete, + PPVDIOSTORAGE ppIoStorage) +{ + int rc = VINF_SUCCESS; + PVDIOSTORAGE pIoStorage = (PVDIOSTORAGE)RTMemAllocZ(sizeof(VDIOSTORAGE)); + + if (pIoStorage) + { + pIoStorage->pIoBackend = pIoBackend; + pIoStorage->pfnComplete = pfnComplete; + if (!strcmp(pszBackend, "memory")) + { + pIoStorage->fMemory = true; + rc = VDMemDiskCreate(&pIoStorage->u.pMemDisk, 0 /* Growing */); + if (RT_SUCCESS(rc)) + { + uint32_t cRefs = ASMAtomicIncU32(&pIoBackend->cRefsIoMem); + if ( cRefs == 1 + && !pIoBackend->pIoMem) + { + rc = VDIoBackendMemCreate(&pIoBackend->pIoMem); + if (RT_FAILURE(rc)) + VDMemDiskDestroy(pIoStorage->u.pMemDisk); + } + } + } + else if (!strcmp(pszBackend, "file")) + { + pIoStorage->fMemory = false; + uint32_t cRefs = ASMAtomicIncU32(&pIoBackend->cRefsFile); + if ( cRefs == 1 + && pIoBackend->hAioMgr == NIL_RTAIOMGR) + rc = RTAioMgrCreate(&pIoBackend->hAioMgr, 1024); + + if (RT_SUCCESS(rc)) + { + /* Create file. */ + rc = RTFileOpen(&pIoStorage->u.File.hFile, pszName, + RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_ASYNC_IO | RTFILE_O_NO_CACHE | RTFILE_O_DENY_NONE); + if (RT_SUCCESS(rc)) + { + /* Create file handle for I/O manager. */ + rc = RTAioMgrFileCreate(pIoBackend->hAioMgr, pIoStorage->u.File.hFile, + vdIoBackendFileIoComplete, pIoStorage, + &pIoStorage->u.File.hAioMgrFile); + if (RT_FAILURE(rc)) + RTFileClose(pIoStorage->u.File.hFile); + } + } + + if (RT_FAILURE(rc)) + ASMAtomicDecU32(&pIoBackend->cRefsFile); + } + else + rc = VERR_NOT_SUPPORTED; + + if (RT_FAILURE(rc)) + RTMemFree(pIoStorage); + else + *ppIoStorage = pIoStorage; + } + else + rc = VERR_NO_MEMORY; + + return rc; +} + +void VDIoBackendStorageDestroy(PVDIOSTORAGE pIoStorage) +{ + if (pIoStorage->fMemory) + { + VDMemDiskDestroy(pIoStorage->u.pMemDisk); + ASMAtomicDecU32(&pIoStorage->pIoBackend->cRefsIoMem); + } + else + { + RTAioMgrFileRelease(pIoStorage->u.File.hAioMgrFile); + RTFileClose(pIoStorage->u.File.hFile); + ASMAtomicDecU32(&pIoStorage->pIoBackend->cRefsFile); + } + RTMemFree(pIoStorage); +} + +int VDIoBackendTransfer(PVDIOSTORAGE pIoStorage, VDIOTXDIR enmTxDir, uint64_t off, + size_t cbTransfer, PRTSGBUF pSgBuf, void *pvUser, bool fSync) +{ + int rc = VINF_SUCCESS; + + if (pIoStorage->fMemory) + { + if (!fSync) + { + rc = VDIoBackendMemTransfer(pIoStorage->pIoBackend->pIoMem, pIoStorage->u.pMemDisk, + enmTxDir, off, cbTransfer, pSgBuf, pIoStorage->pfnComplete, + pvUser); + } + else + { + switch (enmTxDir) + { + case VDIOTXDIR_READ: + rc = VDMemDiskRead(pIoStorage->u.pMemDisk, off, cbTransfer, pSgBuf); + break; + case VDIOTXDIR_WRITE: + rc = VDMemDiskWrite(pIoStorage->u.pMemDisk, off, cbTransfer, pSgBuf); + break; + case VDIOTXDIR_FLUSH: + break; + default: + AssertMsgFailed(("Invalid transfer type %d\n", enmTxDir)); + } + } + } + else + { + if (!fSync) + { + switch (enmTxDir) + { + case VDIOTXDIR_READ: + rc = RTAioMgrFileRead(pIoStorage->u.File.hAioMgrFile, off, pSgBuf, cbTransfer, pvUser); + break; + case VDIOTXDIR_WRITE: + rc = RTAioMgrFileWrite(pIoStorage->u.File.hAioMgrFile, off, pSgBuf, cbTransfer, pvUser); + break; + case VDIOTXDIR_FLUSH: + rc = RTAioMgrFileFlush(pIoStorage->u.File.hAioMgrFile, pvUser); + break; + default: + AssertMsgFailed(("Invalid transfer type %d\n", enmTxDir)); + } + if (rc == VERR_FILE_AIO_IN_PROGRESS) + rc = VINF_SUCCESS; + } + else + { + switch (enmTxDir) + { + case VDIOTXDIR_READ: + rc = RTFileSgReadAt(pIoStorage->u.File.hFile, off, pSgBuf, cbTransfer, NULL); + break; + case VDIOTXDIR_WRITE: + rc = RTFileSgWriteAt(pIoStorage->u.File.hFile, off, pSgBuf, cbTransfer, NULL); + break; + case VDIOTXDIR_FLUSH: + rc = RTFileFlush(pIoStorage->u.File.hFile); + break; + default: + AssertMsgFailed(("Invalid transfer type %d\n", enmTxDir)); + } + } + } + + return rc; +} + +int VDIoBackendStorageSetSize(PVDIOSTORAGE pIoStorage, uint64_t cbSize) +{ + int rc = VINF_SUCCESS; + + if (pIoStorage->fMemory) + { + rc = VDMemDiskSetSize(pIoStorage->u.pMemDisk, cbSize); + } + else + rc = RTFileSetSize(pIoStorage->u.File.hFile, cbSize); + + return rc; +} + +int VDIoBackendStorageGetSize(PVDIOSTORAGE pIoStorage, uint64_t *pcbSize) +{ + int rc = VINF_SUCCESS; + + if (pIoStorage->fMemory) + { + rc = VDMemDiskGetSize(pIoStorage->u.pMemDisk, pcbSize); + } + else + rc = RTFileGetSize(pIoStorage->u.File.hFile, pcbSize); + + return rc; +} + diff --git a/src/VBox/Storage/testcase/VDIoBackend.h b/src/VBox/Storage/testcase/VDIoBackend.h new file mode 100644 index 00000000..bec1bb7d --- /dev/null +++ b/src/VBox/Storage/testcase/VDIoBackend.h @@ -0,0 +1,90 @@ +/** $Id: VDIoBackend.h $ */ +/** @file + * + * VBox HDD container test utility, async I/O backend + */ + +/* + * Copyright (C) 2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ +#ifndef __VDIoBackend_h__ +#define __VDIoBackend_h__ + +#include <iprt/sg.h> + +#include "VDDefs.h" + +/** I/O backend handle. */ +typedef struct VDIOBACKEND *PVDIOBACKEND; +/** Pointer to a I/O backend handle. */ +typedef PVDIOBACKEND *PPVDIOBACKEND; + +/** Storage handle. */ +typedef struct VDIOSTORAGE *PVDIOSTORAGE; +/** Pointer to a storage handle. */ +typedef PVDIOSTORAGE *PPVDIOSTORAGE; + +/** + * Completion handler. + * + * @returns nothing. + * @param pvUser Opaque user data. + * @param rcReq Completion code for the request. + */ +typedef DECLCALLBACK(int) FNVDIOCOMPLETE(void *pvUser, int rcReq); +/** Pointer to a completion handler. */ +typedef FNVDIOCOMPLETE *PFNVDIOCOMPLETE; + +/** + * Creates a new memory I/O backend. + * + * @returns IPRT status code. + * + * @param ppIoBackend Where to store the handle on success. + */ +int VDIoBackendCreate(PPVDIOBACKEND ppIoBackend); + +/** + * Destroys a memory I/O backend. + * + * @returns nothing. + * + * @param pIoBackend The backend to destroy. + */ +void VDIoBackendDestroy(PVDIOBACKEND pIoBackend); + +int VDIoBackendStorageCreate(PVDIOBACKEND pIoBackend, const char *pszBackend, + const char *pszName, PFNVDIOCOMPLETE pfnComplete, + PPVDIOSTORAGE ppIoStorage); + +void VDIoBackendStorageDestroy(PVDIOSTORAGE pIoStorage); + +int VDIoBackendStorageSetSize(PVDIOSTORAGE pIoStorage, uint64_t cbSize); + +int VDIoBackendStorageGetSize(PVDIOSTORAGE pIoStorage, uint64_t *pcbSize); + +/** + * Enqueues a new I/O request. + * + * @returns IPRT status code. + * + * @param pIoStorage Storage handle. + * @param enmTxDir The transfer direction. + * @param off Start offset of the transfer. + * @param cbTransfer Size of the transfer. + * @param pSgBuf S/G buffer to use. + * @param pvUser Opaque user data. + * @param fSync Flag whether to wait for the operation to complete. + */ +int VDIoBackendTransfer(PVDIOSTORAGE pIoStorage, VDIOTXDIR enmTxDir, uint64_t off, + size_t cbTransfer, PRTSGBUF pSgBuf, void *pvUser, bool fSync); + +#endif /* __VDIoBackendMem_h__ */ diff --git a/src/VBox/Storage/testcase/VDIoBackendMem.cpp b/src/VBox/Storage/testcase/VDIoBackendMem.cpp index a3ab1793..b572fe77 100644 --- a/src/VBox/Storage/testcase/VDIoBackendMem.cpp +++ b/src/VBox/Storage/testcase/VDIoBackendMem.cpp @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2011 Oracle Corporation + * Copyright (C) 2011-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -43,13 +43,13 @@ typedef struct VDIOBACKENDREQ uint64_t off; /** Size of the transfer. */ size_t cbTransfer; - /** Number of segments in the array. */ - unsigned cSegs; /** Completion handler to call. */ PFNVDIOCOMPLETE pfnComplete; /** Opaque user data. */ void *pvUser; - /** Segment array - variable in size */ + /** S/G buffer. */ + RTSGBUF SgBuf; + /** Segment array - variable size. */ RTSGSEG aSegs[1]; } VDIOBACKENDREQ, *PVDIOBACKENDREQ; @@ -142,15 +142,19 @@ int VDIoBackendMemDestroy(PVDIOBACKENDMEM pIoBackend) } int VDIoBackendMemTransfer(PVDIOBACKENDMEM pIoBackend, PVDMEMDISK pMemDisk, - VDIOTXDIR enmTxDir, uint64_t off, size_t cbTransfer, PCRTSGSEG paSegs, - unsigned cSegs, PFNVDIOCOMPLETE pfnComplete, void *pvUser) + VDIOTXDIR enmTxDir, uint64_t off, size_t cbTransfer, + PRTSGBUF pSgBuf, PFNVDIOCOMPLETE pfnComplete, void *pvUser) { PVDIOBACKENDREQ pReq = NULL; PPVDIOBACKENDREQ ppReq = NULL; size_t cbData; + unsigned cSegs = 0; LogFlowFunc(("Queuing request\n")); + if (enmTxDir != VDIOTXDIR_FLUSH) + RTSgBufSegArrayCreate(pSgBuf, NULL, &cSegs, cbTransfer); + pReq = (PVDIOBACKENDREQ)RTMemAlloc(RT_OFFSETOF(VDIOBACKENDREQ, aSegs[cSegs])); if (!pReq) return VERR_NO_MEMORY; @@ -167,13 +171,12 @@ int VDIoBackendMemTransfer(PVDIOBACKENDMEM pIoBackend, PVDMEMDISK pMemDisk, pReq->cbTransfer = cbTransfer; pReq->off = off; pReq->pMemDisk = pMemDisk; - pReq->cSegs = cSegs; pReq->pfnComplete = pfnComplete; pReq->pvUser = pvUser; - for (unsigned i = 0; i < cSegs; i++) + if (enmTxDir != VDIOTXDIR_FLUSH) { - pReq->aSegs[i].pvSeg = paSegs[i].pvSeg; - pReq->aSegs[i].cbSeg = paSegs[i].cbSeg; + RTSgBufSegArrayCreate(pSgBuf, &pReq->aSegs[0], &cSegs, cbTransfer); + RTSgBufInit(&pReq->SgBuf, pReq->aSegs, cSegs); } *ppReq = pReq; @@ -225,16 +228,12 @@ static int vdIoBackendMemThread(RTTHREAD hThread, void *pvUser) { case VDIOTXDIR_READ: { - RTSGBUF SgBuf; - RTSgBufInit(&SgBuf, pReq->aSegs, pReq->cSegs); - rcReq = VDMemDiskRead(pReq->pMemDisk, pReq->off, pReq->cbTransfer, &SgBuf); + rcReq = VDMemDiskRead(pReq->pMemDisk, pReq->off, pReq->cbTransfer, &pReq->SgBuf); break; } case VDIOTXDIR_WRITE: { - RTSGBUF SgBuf; - RTSgBufInit(&SgBuf, pReq->aSegs, pReq->cSegs); - rcReq = VDMemDiskWrite(pReq->pMemDisk, pReq->off, pReq->cbTransfer, &SgBuf); + rcReq = VDMemDiskWrite(pReq->pMemDisk, pReq->off, pReq->cbTransfer, &pReq->SgBuf); break; } case VDIOTXDIR_FLUSH: diff --git a/src/VBox/Storage/testcase/VDIoBackendMem.h b/src/VBox/Storage/testcase/VDIoBackendMem.h index 89b16dc1..3e1f5b84 100644 --- a/src/VBox/Storage/testcase/VDIoBackendMem.h +++ b/src/VBox/Storage/testcase/VDIoBackendMem.h @@ -20,20 +20,7 @@ #include <iprt/sg.h> -/** - * I/O transfer direction. - */ -typedef enum VDIOTXDIR -{ - /** Read. */ - VDIOTXDIR_READ = 0, - /** Write. */ - VDIOTXDIR_WRITE, - /** Flush. */ - VDIOTXDIR_FLUSH, - /** Invalid. */ - VDIOTXDIR_INVALID -} VDIOTXDIR; +#include "VDDefs.h" /** Memory backend handle. */ typedef struct VDIOBACKENDMEM *PVDIOBACKENDMEM; @@ -85,8 +72,7 @@ int VDIoBackendMemDestroy(PVDIOBACKENDMEM pIoBackend); * @param pvUser Opaque user data. */ int VDIoBackendMemTransfer(PVDIOBACKENDMEM pIoBackend, PVDMEMDISK pMemDisk, - VDIOTXDIR enmTxDir, uint64_t off, size_t cbTransfer, PCRTSGSEG paSegs, - unsigned cSegs, - PFNVDIOCOMPLETE pfnComplete, void *pvUser); + VDIOTXDIR enmTxDir, uint64_t off, size_t cbTransfer, + PRTSGBUF pSgBuf, PFNVDIOCOMPLETE pfnComplete, void *pvUser); #endif /* __VDIoBackendMem_h__ */ diff --git a/src/VBox/Storage/testcase/VDScript.cpp b/src/VBox/Storage/testcase/VDScript.cpp new file mode 100644 index 00000000..63786311 --- /dev/null +++ b/src/VBox/Storage/testcase/VDScript.cpp @@ -0,0 +1,2733 @@ +/** $Id: VDScript.cpp $ */ +/** @file + * + * VBox HDD container test utility - scripting engine. + */ + +/* + * Copyright (C) 2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +/** @page pg_vd_script VDScript - Simple scripting language for VD I/O testing. + * + * This component implements a very simple scripting language to make testing the VD + * library more flexible and testcases faster to implement without the need to recompile + * everything after it changed. + * The language is a small subset of the C language. It doesn't support unions, structs, + * global variables, typedefed types or pointers (yet). It also adds a boolean and a string type. + * Strings are immutable and only to print messages from the script. + * There are also not the default types like int or unsigned because theire ranges are architecture + * dependent. Instead VDScript uses uint8_t, int8_t, ... as primitive types. + * + * Why inventing a completely new language? + * + * Well it is not a completely new language to start with, it is a subset of C and the + * language can be extended later on to reach the full C language later on. + * Second, there is no static typed scripting language I like which could be implemented + * and finally because I can ;) + * The code implementing the scripting engine is designed to be easily incorporated into other + * code. Could be used as a scripting language for the VBox debugger for example or in the scm + * tool to automatically rewrite C code using the AST VDSCript generates... + * + * The syntax of VDSCript is derived from the C syntax. The syntax of C in BNF was taken + * from: http://www.csci.csusb.edu/dick/samples/c.syntax.html + * and: http://slps.github.com/zoo/c/iso-9899-tc3.html + * and: http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1256.pdf + */ +#define LOGGROUP LOGGROUP_DEFAULT +#include <iprt/string.h> +#include <iprt/list.h> +#include <iprt/mem.h> +#include <iprt/ctype.h> +#include <iprt/stream.h> + +#include <VBox/log.h> + +#include "VDScriptAst.h" +#include "VDScriptInternal.h" + +/** + * VD script token class. + */ +typedef enum VDTOKENCLASS +{ + /** Invalid. */ + VDTOKENCLASS_INVALID = 0, + /** Identifier class. */ + VDTOKENCLASS_IDENTIFIER, + /** Numerical constant. */ + VDTOKENCLASS_NUMCONST, + /** String constant. */ + VDTOKENCLASS_STRINGCONST, + /** Operators */ + VDTOKENCLASS_OPERATORS, + /** Reserved keyword */ + VDTOKENCLASS_KEYWORD, + /** Punctuator */ + VDTOKENCLASS_PUNCTUATOR, + /** End of stream */ + VDTOKENCLASS_EOS, + /** 32bit hack. */ + VDTOKENCLASS_32BIT_HACK = 0x7fffffff +} VDTOKENCLASS; +/** Pointer to a token class. */ +typedef VDTOKENCLASS *PVDTOKENCLASS; + +/** + * Keyword types. + */ +typedef enum VDSCRIPTTOKENKEYWORD +{ + VDSCRIPTTOKENKEYWORD_INVALID = 0, + VDSCRIPTTOKENKEYWORD_CONTINUE, + VDSCRIPTTOKENKEYWORD_DEFAULT, + VDSCRIPTTOKENKEYWORD_RETURN, + VDSCRIPTTOKENKEYWORD_SWITCH, + VDSCRIPTTOKENKEYWORD_WHILE, + VDSCRIPTTOKENKEYWORD_BREAK, + VDSCRIPTTOKENKEYWORD_FALSE, + VDSCRIPTTOKENKEYWORD_TRUE, + VDSCRIPTTOKENKEYWORD_ELSE, + VDSCRIPTTOKENKEYWORD_CASE, + VDSCRIPTTOKENKEYWORD_FOR, + VDSCRIPTTOKENKEYWORD_IF, + VDSCRIPTTOKENKEYWORD_DO, + VDSCRIPTTOKENKEYWORD_32BIT_HACK = 0x7fffffff +} VDSCRIPTTOKENKEYWORD; +/** Pointer to a keyword type. */ +typedef VDSCRIPTTOKENKEYWORD *PVDSCRIPTTOKENKEYWORD; + +/** + * VD script token. + */ +typedef struct VDSCRIPTTOKEN +{ + /** Token class. */ + VDTOKENCLASS enmClass; + /** Token position in the source buffer. */ + VDSRCPOS Pos; + /** Data based on the token class. */ + union + { + /** Identifier. */ + struct + { + /** Pointer to the start of the identifier. */ + const char *pszIde; + /** Number of characters for the identifier excluding the null terminator. */ + size_t cchIde; + } Ide; + /** Numerical constant. */ + struct + { + uint64_t u64; + } NumConst; + /** String constant */ + struct + { + /** Pointer to the start of the string constant. */ + const char *pszString; + /** Number of characters of the string, including the null terminator. */ + size_t cchString; + } StringConst; + /** Operator */ + struct + { + /** The operator string. */ + char aszOp[4]; /** Maximum of 3 for >>= + null terminator. */ + } Operator; + /** Keyword. */ + struct + { + /** The keyword type. */ + VDSCRIPTTOKENKEYWORD enmKeyword; + } Keyword; + /** Punctuator. */ + struct + { + /** The punctuator in question. */ + char chPunctuator; + } Punctuator; + } Class; +} VDSCRIPTTOKEN; +/** Pointer to a script token. */ +typedef VDSCRIPTTOKEN *PVDSCRIPTTOKEN; +/** Pointer to a const script token. */ +typedef const VDSCRIPTTOKEN *PCVDSCRIPTTOKEN; + +/** + * Tokenizer state. + */ +typedef struct VDTOKENIZER +{ + /** Char buffer to read from. */ + const char *pszInput; + /** Current position ininput buffer. */ + VDSRCPOS Pos; + /** Token 1. */ + VDSCRIPTTOKEN Token1; + /** Token 2. */ + VDSCRIPTTOKEN Token2; + /** Pointer to the current active token. */ + PVDSCRIPTTOKEN pTokenCurr; + /** The next token in the input stream (used for peeking). */ + PVDSCRIPTTOKEN pTokenNext; +} VDTOKENIZER; + +/** + * Operators entry. + */ +typedef struct VDSCRIPTOP +{ + /** Operator string. */ + const char *pszOp; + /** Size of the operator in characters without zero terminator. */ + size_t cchOp; +} VDSCRIPTOP; +/** Pointer to a script operator. */ +typedef VDSCRIPTOP *PVDSCRIPTOP; + +/** + * Known operators array, sort from higest character count to lowest. + */ +static VDSCRIPTOP g_aScriptOps[] = +{ + {">>=", 3}, + {"<<=", 3}, + {"+=", 2}, + {"-=", 2}, + {"/=", 2}, + {"%=", 2}, + {"&=", 2}, + {"|=", 2}, + {"^=", 2}, + {"&&", 2}, + {"||", 2}, + {"<<", 2}, + {">>", 2}, + {"++", 2}, + {"--", 2}, + {"==", 2}, + {"!=", 2}, + {">=", 2}, + {"<=", 2}, + {"=", 1}, + {"+", 1}, + {"-", 1}, + {"*", 1}, + {"/", 1}, + {"%", 1}, + {"|", 1}, + {"&", 1}, + {"^", 1}, + {"<", 1}, + {">", 1}, + {"!", 1}, + {"~", 1} +}; + +/** + * Known punctuators. + */ +static VDSCRIPTOP g_aScriptPunctuators[] = +{ + {"(", 1}, + {")", 1}, + {"{", 1}, + {"}", 1}, + {",", 1}, + {";", 1}, +}; + +/** + * Keyword entry. + */ +typedef struct VDSCRIPTKEYWORD +{ + /** Keyword string. */ + const char *pszKeyword; + /** Size of the string in characters without zero terminator. */ + size_t cchKeyword; + /** Keyword type. */ + VDSCRIPTTOKENKEYWORD enmKeyword; +} VDSCRIPTKEYWORD; +/** */ +typedef VDSCRIPTKEYWORD *PVDSCRIPTKEYWORD; + +/** + * Known keywords. + */ +static VDSCRIPTKEYWORD g_aKeywords[] = +{ + {"continue", 8, VDSCRIPTTOKENKEYWORD_CONTINUE}, + {"default", 7, VDSCRIPTTOKENKEYWORD_DEFAULT}, + {"return", 6, VDSCRIPTTOKENKEYWORD_RETURN}, + {"switch", 6, VDSCRIPTTOKENKEYWORD_SWITCH}, + {"while", 5, VDSCRIPTTOKENKEYWORD_WHILE}, + {"break", 5, VDSCRIPTTOKENKEYWORD_BREAK}, + {"false", 5, VDSCRIPTTOKENKEYWORD_FALSE}, + {"true", 4, VDSCRIPTTOKENKEYWORD_TRUE}, + {"else", 4, VDSCRIPTTOKENKEYWORD_ELSE}, + {"case", 4, VDSCRIPTTOKENKEYWORD_CASE}, + {"for", 3, VDSCRIPTTOKENKEYWORD_FOR}, + {"if", 2, VDSCRIPTTOKENKEYWORD_IF}, + {"do", 2, VDSCRIPTTOKENKEYWORD_DO} +}; + +static int vdScriptParseCompoundStatement(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTSTMT *ppAstNodeCompound); +static int vdScriptParseStatement(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTSTMT *ppAstNodeStmt); +static int vdScriptParseExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr); +static int vdScriptParseAssignmentExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr); + +/** + * Returns whether the tokenizer reached the end of the stream. + * + * @returns true if the tokenizer reached the end of stream marker + * false otherwise. + * @param pTokenizer The tokenizer state. + */ +DECLINLINE(bool) vdScriptTokenizerIsEos(PVDTOKENIZER pTokenizer) +{ + return *pTokenizer->pszInput == '\0'; +} + +/** + * Skip one character in the input stream. + * + * @returns nothing. + * @param pTokenizer The tokenizer state. + */ +DECLINLINE(void) vdScriptTokenizerSkipCh(PVDTOKENIZER pTokenizer) +{ + pTokenizer->pszInput++; + pTokenizer->Pos.iChStart++; + pTokenizer->Pos.iChEnd++; +} + +/** + * Returns the next char in the input buffer without advancing it. + * + * @returns Next character in the input buffer. + * @param pTokenizer The tokenizer state. + */ +DECLINLINE(char) vdScriptTokenizerPeekCh(PVDTOKENIZER pTokenizer) +{ + return vdScriptTokenizerIsEos(pTokenizer) + ? '\0' + : *(pTokenizer->pszInput + 1); +} + +/** + * Returns the next character in the input buffer advancing the internal + * position. + * + * @returns Next character in the stream. + * @param pTokenizer The tokenizer state. + */ +DECLINLINE(char) vdScriptTokenizerGetCh(PVDTOKENIZER pTokenizer) +{ + char ch; + + if (vdScriptTokenizerIsEos(pTokenizer)) + ch = '\0'; + else + ch = *pTokenizer->pszInput; + + return ch; +} + +/** + * Sets a new line for the tokenizer. + * + * @returns nothing. + * @param pTokenizer The tokenizer state. + */ +DECLINLINE(void) vdScriptTokenizerNewLine(PVDTOKENIZER pTokenizer, unsigned cSkip) +{ + pTokenizer->pszInput += cSkip; + pTokenizer->Pos.iLine++; + pTokenizer->Pos.iChStart = 1; + pTokenizer->Pos.iChEnd = 1; +} + +/** + * Checks whether the current position in the input stream is a new line + * and skips it. + * + * @returns Flag whether there was a new line at the current position + * in the input buffer. + * @param pTokenizer The tokenizer state. + */ +DECLINLINE(bool) vdScriptTokenizerIsSkipNewLine(PVDTOKENIZER pTokenizer) +{ + bool fNewline = true; + + if ( vdScriptTokenizerGetCh(pTokenizer) == '\r' + && vdScriptTokenizerPeekCh(pTokenizer) == '\n') + vdScriptTokenizerNewLine(pTokenizer, 2); + else if (vdScriptTokenizerGetCh(pTokenizer) == '\n') + vdScriptTokenizerNewLine(pTokenizer, 1); + else + fNewline = false; + + return fNewline; +} + +/** + * Skips a multi line comment. + * + * @returns nothing. + * @param pTokenizer The tokenizer state. + */ +DECLINLINE(void) vdScriptTokenizerSkipComment(PVDTOKENIZER pTokenizer) +{ + while ( !vdScriptTokenizerIsEos(pTokenizer) + && ( vdScriptTokenizerGetCh(pTokenizer) != '*' + || vdScriptTokenizerPeekCh(pTokenizer) != '/')) + { + if (!vdScriptTokenizerIsSkipNewLine(pTokenizer)) + vdScriptTokenizerSkipCh(pTokenizer); + } + + if (!vdScriptTokenizerIsEos(pTokenizer)) + vdScriptTokenizerSkipCh(pTokenizer); + if (!vdScriptTokenizerIsEos(pTokenizer)) + vdScriptTokenizerSkipCh(pTokenizer); +} + +/** + * Skip all whitespace starting from the current input buffer position. + * Skips all present comments too. + * + * @returns nothing. + * @param pTokenizer The tokenizer state. + */ +DECLINLINE(void) vdScriptTokenizerSkipWhitespace(PVDTOKENIZER pTokenizer) +{ + while (!vdScriptTokenizerIsEos(pTokenizer)) + { + while ( vdScriptTokenizerGetCh(pTokenizer) == ' ' + || vdScriptTokenizerGetCh(pTokenizer) == '\t') + vdScriptTokenizerSkipCh(pTokenizer); + + if ( !vdScriptTokenizerIsEos(pTokenizer) + && !vdScriptTokenizerIsSkipNewLine(pTokenizer)) + { + if ( vdScriptTokenizerGetCh(pTokenizer) == '/' + && vdScriptTokenizerPeekCh(pTokenizer) == '*') + { + vdScriptTokenizerSkipCh(pTokenizer); + vdScriptTokenizerSkipCh(pTokenizer); + vdScriptTokenizerSkipComment(pTokenizer); + } + else + break; /* Skipped everything, next is some real content. */ + } + } +} + +/** + * Get an identifier token from the tokenizer. + * + * @returns nothing. + * @param pTokenizer The tokenizer state. + * @param pToken The uninitialized token. + */ +static void vdScriptTokenizerGetIdeOrKeyword(PVDTOKENIZER pTokenizer, PVDSCRIPTTOKEN pToken) +{ + char ch; + size_t cchIde = 0; + bool fIsKeyword = false; + const char *pszIde = pTokenizer->pszInput; + + pToken->Pos = pTokenizer->Pos; + + Assert(RT_C_IS_ALPHA(*pszIde) || *pszIde == '_' ); + + do + { + cchIde++; + vdScriptTokenizerSkipCh(pTokenizer); + ch = vdScriptTokenizerGetCh(pTokenizer); + } + while (RT_C_IS_ALNUM(ch) || ch == '_'); + + /* Check whether we got an identifier or an reserved keyword. */ + for (unsigned i = 0; i < RT_ELEMENTS(g_aKeywords); i++) + { + if (!RTStrNCmp(g_aKeywords[i].pszKeyword, pszIde, g_aKeywords[i].cchKeyword)) + { + fIsKeyword = true; + pToken->enmClass = VDTOKENCLASS_KEYWORD; + pToken->Class.Keyword.enmKeyword = g_aKeywords[i].enmKeyword; + break; + } + } + + if (!fIsKeyword) + { + pToken->enmClass = VDTOKENCLASS_IDENTIFIER; + pToken->Class.Ide.pszIde = pszIde; + pToken->Class.Ide.cchIde = cchIde; + } + pToken->Pos.iChEnd += cchIde; +} + +/** + * Get a numerical constant from the tokenizer. + * + * @returns nothing. + * @param pTokenizer The tokenizer state. + * @param pToken The uninitialized token. + */ +static void vdScriptTokenizerGetNumberConst(PVDTOKENIZER pTokenizer, PVDSCRIPTTOKEN pToken) +{ + unsigned uBase = 10; + char *pszNext = NULL; + + Assert(RT_C_IS_DIGIT(vdScriptTokenizerGetCh(pTokenizer))); + + /* Let RTStrToUInt64Ex() do all the work, looks C compliant :). */ + pToken->enmClass = VDTOKENCLASS_NUMCONST; + int rc = RTStrToUInt64Ex(pTokenizer->pszInput, &pszNext, 0, &pToken->Class.NumConst.u64); + Assert(RT_SUCCESS(rc) || rc == VWRN_TRAILING_CHARS || rc == VWRN_TRAILING_SPACES); + /** @todo: Handle number to big, throw a warning */ + + unsigned cchNumber = pszNext - pTokenizer->pszInput; + for (unsigned i = 0; i < cchNumber; i++) + vdScriptTokenizerSkipCh(pTokenizer); + + /* Check for a supported suffix, supported are K|M|G. */ + if (vdScriptTokenizerGetCh(pTokenizer) == 'K') + { + pToken->Class.NumConst.u64 *= _1K; + vdScriptTokenizerSkipCh(pTokenizer); + } + else if (vdScriptTokenizerGetCh(pTokenizer) == 'M') + { + pToken->Class.NumConst.u64 *= _1M; + vdScriptTokenizerSkipCh(pTokenizer); + } + else if (vdScriptTokenizerGetCh(pTokenizer) == 'G') + { + pToken->Class.NumConst.u64 *= _1G; + vdScriptTokenizerSkipCh(pTokenizer); + } +} + +/** + * Parses a string constant. + * + * @returns nothing. + * @param pTokenizer The tokenizer state. + * @param pToken The uninitialized token. + * + * @remarks: No escape sequences allowed at this time. + */ +static void vdScriptTokenizerGetStringConst(PVDTOKENIZER pTokenizer, PVDSCRIPTTOKEN pToken) +{ + size_t cchStr = 0; + + Assert(vdScriptTokenizerGetCh(pTokenizer) == '\"'); + vdScriptTokenizerSkipCh(pTokenizer); /* Skip " */ + + pToken->enmClass = VDTOKENCLASS_STRINGCONST; + pToken->Pos = pTokenizer->Pos; + pToken->Class.StringConst.pszString = pTokenizer->pszInput; + + while (vdScriptTokenizerGetCh(pTokenizer) != '\"') + { + cchStr++; + vdScriptTokenizerSkipCh(pTokenizer); + } + + vdScriptTokenizerSkipCh(pTokenizer); /* Skip closing " */ + + pToken->Class.StringConst.cchString = cchStr; + pToken->Pos.iChEnd += cchStr; +} + +/** + * Get the end of stream token. + * + * @returns nothing. + * @param pTokenizer The tokenizer state. + * @param pToken The uninitialized token. + */ +static void vdScriptTokenizerGetEos(PVDTOKENIZER pTokenizer, PVDSCRIPTTOKEN pToken) +{ + Assert(vdScriptTokenizerGetCh(pTokenizer) == '\0'); + + pToken->enmClass = VDTOKENCLASS_EOS; + pToken->Pos = pTokenizer->Pos; +} + +/** + * Get operator or punctuator token. + * + * @returns nothing. + * @param pTokenizer The tokenizer state. + * @param pToken The uninitialized token. + */ +static void vdScriptTokenizerGetOperatorOrPunctuator(PVDTOKENIZER pTokenizer, PVDSCRIPTTOKEN pToken) +{ + bool fOpFound = false; + + pToken->enmClass = VDTOKENCLASS_INVALID; + pToken->Pos = pTokenizer->Pos; + + /* + * Use table based approach here, not the fastest solution but enough for our purpose + * for now. + */ + for (unsigned i = 0; i < RT_ELEMENTS(g_aScriptOps); i++) + { + if (!RTStrNCmp(g_aScriptOps[i].pszOp, pTokenizer->pszInput, g_aScriptOps[i].cchOp)) + { + memset(pToken->Class.Operator.aszOp, 0, sizeof(pToken->Class.Operator.aszOp)); + + int rc = RTStrCopy(pToken->Class.Operator.aszOp, sizeof(pToken->Class.Operator.aszOp), g_aScriptOps[i].pszOp); + AssertRC(rc); + + pToken->enmClass = VDTOKENCLASS_OPERATORS; + pToken->Pos.iChEnd += g_aScriptOps[i].cchOp; + + /** @todo: Make this prettier. */ + for (unsigned j = 0; j < g_aScriptOps[i].cchOp; j++) + vdScriptTokenizerSkipCh(pTokenizer); + fOpFound = true; + break; + } + } + + if (!fOpFound) + { + for (unsigned i = 0; i < RT_ELEMENTS(g_aScriptPunctuators); i++) + { + if (!RTStrNCmp(g_aScriptPunctuators[i].pszOp, pTokenizer->pszInput, g_aScriptPunctuators[i].cchOp)) + { + pToken->Pos.iChEnd += g_aScriptPunctuators[i].cchOp; + pToken->enmClass = VDTOKENCLASS_PUNCTUATOR; + pToken->Class.Punctuator.chPunctuator = *g_aScriptPunctuators[i].pszOp; + + vdScriptTokenizerSkipCh(pTokenizer); + fOpFound = true; + break; + } + } + } +} + +/** + * Read the next token from the tokenizer stream. + * + * @returns nothing. + * @param pTokenizer The tokenizer to read from. + * @param pToken Uninitialized token to fill the token data into. + */ +static void vdScriptTokenizerReadNextToken(PVDTOKENIZER pTokenizer, PVDSCRIPTTOKEN pToken) +{ + /* Skip all eventually existing whitespace, newlines and comments first. */ + vdScriptTokenizerSkipWhitespace(pTokenizer); + + char ch = vdScriptTokenizerGetCh(pTokenizer); + if (RT_C_IS_ALPHA(ch) || ch == '_') + vdScriptTokenizerGetIdeOrKeyword(pTokenizer, pToken); + else if (RT_C_IS_DIGIT(ch)) + vdScriptTokenizerGetNumberConst(pTokenizer, pToken); + else if (ch == '\"') + vdScriptTokenizerGetStringConst(pTokenizer, pToken); + else if (ch == '\0') + vdScriptTokenizerGetEos(pTokenizer, pToken); + else + vdScriptTokenizerGetOperatorOrPunctuator(pTokenizer, pToken); +} + +/** + * Create a new tokenizer. + * + * @returns Pointer to the new tokenizer state on success. + * NULL if out of memory. + * @param pszInput The input to create the tokenizer for. + */ +static PVDTOKENIZER vdScriptTokenizerCreate(const char *pszInput) +{ + PVDTOKENIZER pTokenizer = (PVDTOKENIZER)RTMemAllocZ(sizeof(VDTOKENIZER)); + if (pTokenizer) + { + pTokenizer->pszInput = pszInput; + pTokenizer->Pos.iLine = 1; + pTokenizer->Pos.iChStart = 1; + pTokenizer->Pos.iChEnd = 1; + pTokenizer->pTokenCurr = &pTokenizer->Token1; + pTokenizer->pTokenNext = &pTokenizer->Token2; + /* Fill the tokenizer with two first tokens. */ + vdScriptTokenizerReadNextToken(pTokenizer, pTokenizer->pTokenCurr); + vdScriptTokenizerReadNextToken(pTokenizer, pTokenizer->pTokenNext); + } + + return pTokenizer; +} + +/** + * Destroys a given tokenizer state. + * + * @returns nothing. + * @param pTokenizer The tokenizer to destroy. + */ +static void vdScriptTokenizerDestroy(PVDTOKENIZER pTokenizer) +{ + RTMemFree(pTokenizer); +} + +/** + * Get the current token in the input stream. + * + * @returns Pointer to the next token in the stream. + * @param pTokenizer The tokenizer to destroy. + */ +DECLINLINE(PCVDSCRIPTTOKEN) vdScriptTokenizerGetToken(PVDTOKENIZER pTokenizer) +{ + return pTokenizer->pTokenCurr; +} + +/** + * Get the class of the current token. + * + * @returns Class of the current token. + * @param pTokenizer The tokenizer state. + */ +DECLINLINE(VDTOKENCLASS) vdScriptTokenizerGetTokenClass(PVDTOKENIZER pTokenizer) +{ + return pTokenizer->pTokenCurr->enmClass; +} + +/** + * Returns the token class of the next token in the stream. + * + * @returns Token class of the next token. + * @param pTokenizer The tokenizer state. + */ +DECLINLINE(VDTOKENCLASS) vdScriptTokenizerPeekNextClass(PVDTOKENIZER pTokenizer) +{ + return pTokenizer->pTokenNext->enmClass; +} + +/** + * Consume the current token advancing to the next in the stream. + * + * @returns nothing. + * @param pTokenizer The tokenizer state. + */ +static void vdScriptTokenizerConsume(PVDTOKENIZER pTokenizer) +{ + PVDSCRIPTTOKEN pTokenTmp = pTokenizer->pTokenCurr; + + /* Switch next token to current token and read in the next token. */ + pTokenizer->pTokenCurr = pTokenizer->pTokenNext; + pTokenizer->pTokenNext = pTokenTmp; + vdScriptTokenizerReadNextToken(pTokenizer, pTokenizer->pTokenNext); +} + +/** + * Check whether the next token in the input stream is a punctuator and matches the given + * character. + * + * @returns true if the token matched. + * false otherwise. + * @param pTokenizer The tokenizer state. + * @param chCheck The punctuator to check against. + */ +static bool vdScriptTokenizerIsPunctuatorEqual(PVDTOKENIZER pTokenizer, char chCheck) +{ + PCVDSCRIPTTOKEN pToken = vdScriptTokenizerGetToken(pTokenizer); + + if ( pToken->enmClass == VDTOKENCLASS_PUNCTUATOR + && pToken->Class.Punctuator.chPunctuator == chCheck) + return true; + + return false; +} + +/** + * Check whether the next token in the input stream is a punctuator and matches the given + * character and skips it. + * + * @returns true if the token matched and was skipped. + * false otherwise. + * @param pTokenizer The tokenizer state. + * @param chCheck The punctuator to check against. + */ +static bool vdScriptTokenizerSkipIfIsPunctuatorEqual(PVDTOKENIZER pTokenizer, char chCheck) +{ + bool fEqual = vdScriptTokenizerIsPunctuatorEqual(pTokenizer, chCheck); + if (fEqual) + vdScriptTokenizerConsume(pTokenizer); + + return fEqual; +} + +/** + * Check whether the next token in the input stream is a keyword and matches the given + * keyword. + * + * @returns true if the token matched. + * false otherwise. + * @param pTokenizer The tokenizer state. + * @param enmKey The keyword to check against. + */ +static bool vdScriptTokenizerIsKeywordEqual(PVDTOKENIZER pTokenizer, VDSCRIPTTOKENKEYWORD enmKeyword) +{ + PCVDSCRIPTTOKEN pToken = vdScriptTokenizerGetToken(pTokenizer); + + if ( pToken->enmClass == VDTOKENCLASS_KEYWORD + && pToken->Class.Keyword.enmKeyword == enmKeyword) + return true; + + return false; +} + +/** + * Check whether the next token in the input stream is a keyword and matches the given + * keyword and skips it. + * + * @returns true if the token matched and was skipped. + * false otherwise. + * @param pTokenizer The tokenizer state. + * @param enmKey The keyword to check against. + */ +static bool vdScriptTokenizerSkipIfIsKeywordEqual(PVDTOKENIZER pTokenizer, VDSCRIPTTOKENKEYWORD enmKeyword) +{ + bool fEqual = vdScriptTokenizerIsKeywordEqual(pTokenizer, enmKeyword); + if (fEqual) + vdScriptTokenizerConsume(pTokenizer); + + return fEqual; +} + +/** + * Check whether the next token in the input stream is a keyword and matches the given + * keyword. + * + * @returns true if the token matched. + * false otherwise. + * @param pTokenizer The tokenizer state. + * @param pszOp The operation to check against. + */ +static bool vdScriptTokenizerIsOperatorEqual(PVDTOKENIZER pTokenizer, const char *pszOp) +{ + PCVDSCRIPTTOKEN pToken = vdScriptTokenizerGetToken(pTokenizer); + + if ( pToken->enmClass == VDTOKENCLASS_OPERATORS + && !RTStrCmp(pToken->Class.Operator.aszOp, pszOp)) + return true; + + return false; +} + +/** + * Check whether the next token in the input stream is an operator and matches the given + * keyword and skips it. + * + * @returns true if the token matched and was skipped. + * false otherwise. + * @param pTokenizer The tokenizer state. + * @param pszOp The operation to check against. + */ +static bool vdScriptTokenizerSkipIfIsOperatorEqual(PVDTOKENIZER pTokenizer, const char *pszOp) +{ + bool fEqual = vdScriptTokenizerIsOperatorEqual(pTokenizer, pszOp); + if (fEqual) + vdScriptTokenizerConsume(pTokenizer); + + return fEqual; +} + +/** + * Record an error while parsing. + * + * @returns VBox status code passed. + */ +static int vdScriptParserError(PVDSCRIPTCTXINT pThis, int rc, RT_SRC_POS_DECL, const char *pszFmt, ...) +{ + NOREF(pThis); + NOREF(pszFmt); + RTPrintf(pszFmt); + return rc; +} + +/** + * Puts the next identifier AST node on the stack. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeIde Where to store the identifier AST node on success. + */ +static int vdScriptParseIde(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTIDE *ppAstNodeIde) +{ + int rc = VINF_SUCCESS; + PCVDSCRIPTTOKEN pToken; + + LogFlowFunc(("pThis=%p ppAstNodeIde=%p\n", pThis, ppAstNodeIde)); + + pToken = vdScriptTokenizerGetToken(pThis->pTokenizer); + if (pToken->enmClass != VDTOKENCLASS_IDENTIFIER) + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected identifer got...\n"); + else + { + /* Create new AST node and push onto stack. */ + PVDSCRIPTASTIDE pAstNodeIde = vdScriptAstNodeIdeAlloc(pToken->Class.Ide.cchIde); + if (pAstNodeIde) + { + rc = RTStrCopyEx(pAstNodeIde->aszIde, pToken->Class.Ide.cchIde + 1, pToken->Class.Ide.pszIde, pToken->Class.Ide.cchIde); + AssertRC(rc); + pAstNodeIde->cchIde = pToken->Class.Ide.cchIde; + + *ppAstNodeIde = pAstNodeIde; + vdScriptTokenizerConsume(pThis->pTokenizer); + } + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating identifier AST node\n"); + } + + LogFlowFunc(("returns %Rrc\n", rc)); + return rc; +} + +/** + * Parse a primary expression. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeExpr Where to store the primary expression on success. + */ +static int vdScriptParsePrimaryExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr) +{ + int rc = VINF_SUCCESS; + + LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n")); + + if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '(')) + { + rc = vdScriptParseExpression(pThis, ppAstNodeExpr); + if (RT_SUCCESS(rc) + && !vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')')) + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n"); + } + else + { + PVDSCRIPTASTEXPR pExpr = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExpr) + { + if (vdScriptTokenizerGetTokenClass(pThis->pTokenizer) == VDTOKENCLASS_IDENTIFIER) + { + PVDSCRIPTASTIDE pIde = NULL; + rc = vdScriptParseIde(pThis, &pIde); + if (RT_SUCCESS(rc)) + { + pExpr->enmType = VDSCRIPTEXPRTYPE_PRIMARY_IDENTIFIER; + pExpr->pIde = pIde; + } + } + else if (vdScriptTokenizerGetTokenClass(pThis->pTokenizer) == VDTOKENCLASS_NUMCONST) + { + PCVDSCRIPTTOKEN pToken = vdScriptTokenizerGetToken(pThis->pTokenizer); + pExpr->enmType = VDSCRIPTEXPRTYPE_PRIMARY_NUMCONST; + pExpr->u64 = pToken->Class.NumConst.u64; + vdScriptTokenizerConsume(pThis->pTokenizer); + } + else if (vdScriptTokenizerGetTokenClass(pThis->pTokenizer) == VDTOKENCLASS_STRINGCONST) + { + PCVDSCRIPTTOKEN pToken = vdScriptTokenizerGetToken(pThis->pTokenizer); + pExpr->enmType = VDSCRIPTEXPRTYPE_PRIMARY_STRINGCONST; + pExpr->pszStr = RTStrDupN(pToken->Class.StringConst.pszString, pToken->Class.StringConst.cchString); + vdScriptTokenizerConsume(pThis->pTokenizer); + + if (!pExpr->pszStr) + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating string\n"); + } + else if (vdScriptTokenizerGetTokenClass(pThis->pTokenizer) == VDTOKENCLASS_KEYWORD) + { + PCVDSCRIPTTOKEN pToken = vdScriptTokenizerGetToken(pThis->pTokenizer); + pExpr->enmType = VDSCRIPTEXPRTYPE_PRIMARY_BOOLEAN; + + if (pToken->Class.Keyword.enmKeyword == VDSCRIPTTOKENKEYWORD_TRUE) + pExpr->f = true; + else if (pToken->Class.Keyword.enmKeyword == VDSCRIPTTOKENKEYWORD_FALSE) + pExpr->f = false; + else + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Unexpected keyword, expected true or false\n"); + vdScriptTokenizerConsume(pThis->pTokenizer); + } + else + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"(\" | identifier | constant | string, got ...\n"); + + if (RT_FAILURE(rc)) + vdScriptAstNodeFree(&pExpr->Core); + else + *ppAstNodeExpr = pExpr; + } + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +/** + * Parse an argument list for a function call. + * + * @returns VBox status code. + * @param pThis The script context. + * @param pFnCall The function call AST node. + */ +static int vdScriptParseFnCallArgumentList(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR pFnCall) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTASTEXPR pExpr = NULL; + + LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n")); + + rc = vdScriptParseAssignmentExpression(pThis, &pExpr); + if (RT_SUCCESS(rc)) + { + RTListAppend(&pFnCall->FnCall.ListArgs, &pExpr->Core.ListNode); + while (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ',')) + { + rc = vdScriptParseAssignmentExpression(pThis, &pExpr); + if (RT_SUCCESS(rc)) + RTListAppend(&pFnCall->FnCall.ListArgs, &pExpr->Core.ListNode); + else + break; + } + if ( RT_SUCCESS(rc) + && !vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')')) + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n"); + } + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +/** + * Parse a postfix expression. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeExpr Where to store the expression AST node on success. + * + * @note Syntax: + * postfix-expression: + * primary-expression + * postfix-expression ( argument-expression ) + * postfix-expression ++ + * postfix-expression -- + */ +static int vdScriptParsePostfixExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTASTEXPR pExpr = NULL; + + LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n")); + + rc = vdScriptParsePrimaryExpression(pThis, &pExpr); + if (RT_SUCCESS(rc)) + { + while (true) + { + PVDSCRIPTASTEXPR pExprNew = NULL; + + if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "++")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + { + pExprNew->enmType = VDSCRIPTEXPRTYPE_POSTFIX_INCREMENT; + pExprNew->pExpr = pExpr; + pExpr = pExprNew; + } + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "--")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + { + pExprNew->enmType = VDSCRIPTEXPRTYPE_POSTFIX_DECREMENT; + pExprNew->pExpr = pExpr; + pExpr = pExprNew; + } + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '(')) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + { + pExprNew->enmType = VDSCRIPTEXPRTYPE_POSTFIX_FNCALL; + RTListInit(&pExprNew->FnCall.ListArgs); + if (!vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')')) + rc = vdScriptParseFnCallArgumentList(pThis, pExprNew); + pExprNew->FnCall.pFnIde = pExpr; + pExpr = pExprNew; + } + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else + break; + + if (RT_FAILURE(rc)) + break; + } + + if (RT_SUCCESS(rc)) + *ppAstNodeExpr = pExpr; + else + vdScriptAstNodeFree(&pExpr->Core); + } + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +/** + * Parse an unary expression. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeExpr Where to store the expression AST node on success. + * + * @note Syntax: + * unary-expression: + * postfix-expression + * ++ unary-expression + * -- unary-expression + * + unary-expression + * - unary-expression + * ~ unary-expression + * ! unary-expression + */ +static int vdScriptParseUnaryExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTASTEXPR pExpr = NULL; + PVDSCRIPTASTEXPR pExprTop = NULL; + + LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n")); + + while (true) + { + bool fQuit = false; + PVDSCRIPTASTEXPR pExprNew = NULL; + + if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "++")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_UNARY_INCREMENT; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "--")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_UNARY_DECREMENT; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "+")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_UNARY_POSSIGN; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "-")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_UNARY_NEGSIGN; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "~")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_UNARY_INVERT; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "!")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_UNARY_NEGATE; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else + { + /* Must be a postfix expression. */ + rc = vdScriptParsePostfixExpression(pThis, &pExprNew); + fQuit = true; + } + + if (RT_SUCCESS(rc)) + { + if (!pExprTop) + { + pExprTop = pExprNew; + pExpr = pExprNew; + } + else + { + pExpr->pExpr = pExprNew; + pExpr = pExprNew; + } + if (fQuit) + break; + } + else + break; + } + + if (RT_SUCCESS(rc)) + *ppAstNodeExpr = pExprTop; + else if (pExprTop) + vdScriptAstNodeFree(&pExprTop->Core); + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +/** + * Parse a multiplicative expression. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeExpr Where to store the expression AST node on success. + * + * @note Syntax: + * multiplicative-expression: + * unary-expression + * multiplicative-expression * unary-expression + * multiplicative-expression / unary-expression + * multiplicative-expression % unary-expression + */ +static int vdScriptParseMultiplicativeExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTASTEXPR pExpr = NULL; + + LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n")); + + rc = vdScriptParseUnaryExpression(pThis, &pExpr); + if (RT_SUCCESS(rc)) + { + PVDSCRIPTASTEXPR pExprNew = NULL; + while (RT_SUCCESS(rc)) + { + if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "*")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_MULTIPLICATION; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "/")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_DIVISION; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "%")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_MODULUS; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else + break; + + pExprNew->BinaryOp.pLeftExpr = pExpr; + pExpr = pExprNew; + rc = vdScriptParseUnaryExpression(pThis, &pExprNew); + if (RT_SUCCESS(rc)) + pExpr->BinaryOp.pRightExpr = pExprNew; + } + + if (RT_SUCCESS(rc)) + *ppAstNodeExpr = pExpr; + else + vdScriptAstNodeFree(&pExpr->Core); + } + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +/** + * Parse a additive expression. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeExpr Where to store the expression AST node on success. + * + * @note Syntax: + * additive-expression: + * multiplicative-expression + * additive-expression + multiplicative-expression + * additive-expression - multiplicative-expression + */ +static int vdScriptParseAdditiveExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTASTEXPR pExpr = NULL; + + LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n")); + + rc = vdScriptParseMultiplicativeExpression(pThis, &pExpr); + if (RT_SUCCESS(rc)) + { + PVDSCRIPTASTEXPR pExprNew = NULL; + while (RT_SUCCESS(rc)) + { + if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "+")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_ADDITION; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "-")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_SUBTRACTION; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else + break; + + pExprNew->BinaryOp.pLeftExpr = pExpr; + pExpr = pExprNew; + rc = vdScriptParseMultiplicativeExpression(pThis, &pExprNew); + if (RT_SUCCESS(rc)) + pExpr->BinaryOp.pRightExpr = pExprNew; + } + + if (RT_SUCCESS(rc)) + *ppAstNodeExpr = pExpr; + else + vdScriptAstNodeFree(&pExpr->Core); + } + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +/** + * Parse a shift expression. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeExpr Where to store the expression AST node on success. + * + * @note Syntax: + * shift-expression: + * additive-expression + * shift-expression << additive-expression + * shift-expression >> additive-expression + */ +static int vdScriptParseShiftExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTASTEXPR pExpr = NULL; + + LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n")); + + rc = vdScriptParseAdditiveExpression(pThis, &pExpr); + if (RT_SUCCESS(rc)) + { + PVDSCRIPTASTEXPR pExprNew = NULL; + while (RT_SUCCESS(rc)) + { + if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "<<")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_LSL; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, ">>")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_LSR; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else + break; + + pExprNew->BinaryOp.pLeftExpr = pExpr; + pExpr = pExprNew; + rc = vdScriptParseAdditiveExpression(pThis, &pExprNew); + if (RT_SUCCESS(rc)) + pExpr->BinaryOp.pRightExpr = pExprNew; + } + + if (RT_SUCCESS(rc)) + *ppAstNodeExpr = pExpr; + else + vdScriptAstNodeFree(&pExpr->Core); + } + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +/** + * Parse a relational expression. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeExpr Where to store the expression AST node on success. + * + * @note Syntax: + * relational-expression: + * shift-expression + * relational-expression < shift-expression + * relational-expression > shift-expression + * relational-expression >= shift-expression + * relational-expression <= shift-expression + */ +static int vdScriptParseRelationalExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTASTEXPR pExpr = NULL; + + LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n")); + + rc = vdScriptParseShiftExpression(pThis, &pExpr); + if (RT_SUCCESS(rc)) + { + PVDSCRIPTASTEXPR pExprNew = NULL; + while (RT_SUCCESS(rc)) + { + if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "<")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_LOWER; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, ">")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_HIGHER; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, ">=")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_HIGHEREQUAL; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "<=")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_LOWEREQUAL; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else + break; + + pExprNew->BinaryOp.pLeftExpr = pExpr; + pExpr = pExprNew; + rc = vdScriptParseShiftExpression(pThis, &pExprNew); + if (RT_SUCCESS(rc)) + pExpr->BinaryOp.pRightExpr = pExprNew; + } + + if (RT_SUCCESS(rc)) + *ppAstNodeExpr = pExpr; + else + vdScriptAstNodeFree(&pExpr->Core); + } + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +/** + * Parse a equality expression. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeExpr Where to store the expression AST node on success. + * + * @note Syntax: + * equality-expression: + * relational-expression + * equality-expression == relational-expression + * equality-expression != relational-expression + */ +static int vdScriptParseEqualityExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTASTEXPR pExpr = NULL; + + LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n")); + + rc = vdScriptParseRelationalExpression(pThis, &pExpr); + if (RT_SUCCESS(rc)) + { + PVDSCRIPTASTEXPR pExprNew = NULL; + while (RT_SUCCESS(rc)) + { + if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "==")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_EQUAL; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "!=")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_NOTEQUAL; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else + break; + + pExprNew->BinaryOp.pLeftExpr = pExpr; + pExpr = pExprNew; + rc = vdScriptParseRelationalExpression(pThis, &pExprNew); + if (RT_SUCCESS(rc)) + pExpr->BinaryOp.pRightExpr = pExprNew; + } + + if (RT_SUCCESS(rc)) + *ppAstNodeExpr = pExpr; + else + vdScriptAstNodeFree(&pExpr->Core); + } + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +/** + * Parse a bitwise and expression. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeExpr Where to store the expression AST node on success. + * + * @note Syntax: + * and-expression: + * equality-expression + * and-expression & equality-expression + */ +static int vdScriptParseBitwiseAndExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTASTEXPR pExpr = NULL; + + LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n")); + + rc = vdScriptParseEqualityExpression(pThis, &pExpr); + if (RT_SUCCESS(rc)) + { + PVDSCRIPTASTEXPR pExprNew = NULL; + while ( RT_SUCCESS(rc) + && vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "&")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + { + pExprNew->enmType = VDSCRIPTEXPRTYPE_EQUAL; + pExprNew->BinaryOp.pLeftExpr = pExpr; + pExpr = pExprNew; + rc = vdScriptParseEqualityExpression(pThis, &pExprNew); + if (RT_SUCCESS(rc)) + pExpr->BinaryOp.pRightExpr = pExprNew; + } + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + + if (RT_SUCCESS(rc)) + *ppAstNodeExpr = pExpr; + else + vdScriptAstNodeFree(&pExpr->Core); + } + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +/** + * Parse a bitwise xor expression. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeExpr Where to store the expression AST node on success. + * + * @note Syntax: + * xor-expression: + * and-expression + * xor-expression ^ equality-expression + */ +static int vdScriptParseBitwiseXorExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTASTEXPR pExpr = NULL; + + LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n")); + + rc = vdScriptParseBitwiseAndExpression(pThis, &pExpr); + if (RT_SUCCESS(rc)) + { + PVDSCRIPTASTEXPR pExprNew = NULL; + while ( RT_SUCCESS(rc) + && vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "^")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + { + pExprNew->enmType = VDSCRIPTEXPRTYPE_BITWISE_XOR; + pExprNew->BinaryOp.pLeftExpr = pExpr; + pExpr = pExprNew; + rc = vdScriptParseBitwiseAndExpression(pThis, &pExprNew); + if (RT_SUCCESS(rc)) + pExpr->BinaryOp.pRightExpr = pExprNew; + } + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + + if (RT_SUCCESS(rc)) + *ppAstNodeExpr = pExpr; + else + vdScriptAstNodeFree(&pExpr->Core); + } + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +/** + * Parse a bitwise or expression. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeExpr Where to store the expression AST node on success. + * + * @note Syntax: + * or-expression: + * xor-expression + * or-expression | xor-expression + */ +static int vdScriptParseBitwiseOrExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTASTEXPR pExpr = NULL; + + LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n")); + + rc = vdScriptParseBitwiseXorExpression(pThis, &pExpr); + if (RT_SUCCESS(rc)) + { + PVDSCRIPTASTEXPR pExprNew = NULL; + while ( RT_SUCCESS(rc) + && vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "|")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + { + pExprNew->enmType = VDSCRIPTEXPRTYPE_BITWISE_OR; + pExprNew->BinaryOp.pLeftExpr = pExpr; + pExpr = pExprNew; + rc = vdScriptParseBitwiseXorExpression(pThis, &pExprNew); + if (RT_SUCCESS(rc)) + pExpr->BinaryOp.pRightExpr = pExprNew; + } + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + + if (RT_SUCCESS(rc)) + *ppAstNodeExpr = pExpr; + else + vdScriptAstNodeFree(&pExpr->Core); + } + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +/** + * Parse a logical and expression. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeExpr Where to store the expression AST node on success. + * + * @note Syntax: + * logical-and-expression: + * or-expression + * logical-and-expression | or-expression + */ +static int vdScriptParseLogicalAndExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTASTEXPR pExpr = NULL; + + LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n")); + + rc = vdScriptParseBitwiseOrExpression(pThis, &pExpr); + if (RT_SUCCESS(rc)) + { + PVDSCRIPTASTEXPR pExprNew = NULL; + while ( RT_SUCCESS(rc) + && vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "&&")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + { + pExprNew->enmType = VDSCRIPTEXPRTYPE_LOGICAL_AND; + pExprNew->BinaryOp.pLeftExpr = pExpr; + pExpr = pExprNew; + rc = vdScriptParseBitwiseOrExpression(pThis, &pExprNew); + if (RT_SUCCESS(rc)) + pExpr->BinaryOp.pRightExpr = pExprNew; + } + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + + if (RT_SUCCESS(rc)) + *ppAstNodeExpr = pExpr; + else + vdScriptAstNodeFree(&pExpr->Core); + } + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +/** + * Parse a logical or expression. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeExpr Where to store the expression AST node on success. + * + * @note Syntax: + * logical-or-expression: + * logical-and-expression + * logical-or-expression | logical-and-expression + */ +static int vdScriptParseLogicalOrExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTASTEXPR pExpr = NULL; + + LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n")); + + rc = vdScriptParseLogicalAndExpression(pThis, &pExpr); + if (RT_SUCCESS(rc)) + { + PVDSCRIPTASTEXPR pExprNew = NULL; + while ( RT_SUCCESS(rc) + && vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "||")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + { + pExprNew->enmType = VDSCRIPTEXPRTYPE_LOGICAL_OR; + pExprNew->BinaryOp.pLeftExpr = pExpr; + pExpr = pExprNew; + rc = vdScriptParseLogicalAndExpression(pThis, &pExprNew); + if (RT_SUCCESS(rc)) + pExpr->BinaryOp.pRightExpr = pExprNew; + } + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + + if (RT_SUCCESS(rc)) + *ppAstNodeExpr = pExpr; + else + vdScriptAstNodeFree(&pExpr->Core); + } + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +/** + * Parse a conditional expression. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeExpr Where to store the expression AST node on success. + * + * @note: VDScript doesn't support logical-or-expression ? expression : conditional-expression + * so a conditional expression is equal to a logical-or-expression. + */ +static int vdScriptParseCondExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr) +{ + return vdScriptParseLogicalOrExpression(pThis, ppAstNodeExpr); +} + +/** + * Parse an assignment expression. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeExpr Where to store the expression AST node on success. + * + * @note Syntax: + * assignment-expression: + * conditional-expression + * unary-expression assignment-operator assignment-expression + */ +static int vdScriptParseAssignmentExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTASTEXPR pExpr; + + LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n")); + + rc = vdScriptParseLogicalOrExpression(pThis, &pExpr); + if (RT_SUCCESS(rc)) + { + PVDSCRIPTASTEXPR pExprNew = NULL; + while (RT_SUCCESS(rc)) + { + if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "=")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "*=")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_MULT; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "/=")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_DIV; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "%=")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_MOD; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "+=")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_ADD; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "-=")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_SUB; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "<<=")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_LSL; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, ">>=")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_LSR; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "&=")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_AND; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "^=")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_XOR; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (vdScriptTokenizerSkipIfIsOperatorEqual(pThis->pTokenizer, "|=")) + { + pExprNew = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pExprNew) + pExprNew->enmType = VDSCRIPTEXPRTYPE_ASSIGN_OR; + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else + break; + + pExprNew->BinaryOp.pLeftExpr = pExpr; + pExpr = pExprNew; + rc = vdScriptParseLogicalOrExpression(pThis, &pExprNew); + if (RT_SUCCESS(rc)) + pExpr->BinaryOp.pRightExpr = pExprNew; + } + + if (RT_SUCCESS(rc)) + *ppAstNodeExpr = pExpr; + else + vdScriptAstNodeFree(&pExpr->Core); + } + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +/** + * Parse an expression. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeExpr Where to store the expression AST node on success. + * + * @note Syntax: + * expression: + * assignment-expression + * expression , assignment-expression + */ +static int vdScriptParseExpression(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTEXPR *ppAstNodeExpr) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTASTEXPR pAssignExpr = NULL; + + LogFlowFunc(("pThis=%p ppAstNodeExpr=%p\n")); + + rc = vdScriptParseAssignmentExpression(pThis, &pAssignExpr); + if ( RT_SUCCESS(rc) + && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ',')) + { + PVDSCRIPTASTEXPR pListAssignExpr = (PVDSCRIPTASTEXPR)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_EXPRESSION); + if (pListAssignExpr) + { + pListAssignExpr->enmType = VDSCRIPTEXPRTYPE_ASSIGNMENT_LIST; + RTListInit(&pListAssignExpr->ListExpr); + RTListAppend(&pListAssignExpr->ListExpr, &pAssignExpr->Core.ListNode); + do + { + rc = vdScriptParseAssignmentExpression(pThis, &pAssignExpr); + if (RT_SUCCESS(rc)) + RTListAppend(&pListAssignExpr->ListExpr, &pAssignExpr->Core.ListNode); + } while ( RT_SUCCESS(rc) + && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ',')); + + if (RT_FAILURE(rc)) + vdScriptAstNodeFree(&pListAssignExpr->Core); + else + *ppAstNodeExpr = pListAssignExpr; + } + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating expression AST node\n"); + } + else if (RT_SUCCESS(rc)) + *ppAstNodeExpr = pAssignExpr; + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +/** + * Parse an if statement. + * + * @returns VBox status code. + * @param pThis The script context. + * @param pAstNodeIf Uninitialized if AST node. + * + * @note The caller skipped the "if" token already. + */ +static int vdScriptParseIf(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTIF pAstNodeIf) +{ + int rc = VINF_SUCCESS; + + if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '(')) + { + PVDSCRIPTASTEXPR pCondExpr = NULL; + rc = vdScriptParseExpression(pThis, &pCondExpr); + if (RT_SUCCESS(rc)) + { + if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')')) + { + PVDSCRIPTASTSTMT pStmt = NULL; + PVDSCRIPTASTSTMT pElseStmt = NULL; + rc = vdScriptParseStatement(pThis, &pStmt); + if ( RT_SUCCESS(rc) + && vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_ELSE)) + rc = vdScriptParseStatement(pThis, &pElseStmt); + + if (RT_SUCCESS(rc)) + { + pAstNodeIf->pCond = pCondExpr; + pAstNodeIf->pTrueStmt = pStmt; + pAstNodeIf->pElseStmt = pElseStmt; + } + else if (pStmt) + vdScriptAstNodeFree(&pStmt->Core); + } + else + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n"); + + if (RT_FAILURE(rc)) + vdScriptAstNodeFree(&pCondExpr->Core); + } + } + else + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"(\", got ...\n"); + + return rc; +} + +/** + * Parse a switch statement. + * + * @returns VBox status code. + * @param pThis The script context. + * @param pAstNodeSwitch Uninitialized switch AST node. + * + * @note The caller skipped the "switch" token already. + */ +static int vdScriptParseSwitch(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTSWITCH pAstNodeSwitch) +{ + int rc = VINF_SUCCESS; + + if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '(')) + { + PVDSCRIPTASTEXPR pExpr = NULL; + + rc = vdScriptParseExpression(pThis, &pExpr); + if ( RT_SUCCESS(rc) + && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')')) + { + PVDSCRIPTASTSTMT pStmt = NULL; + rc = vdScriptParseStatement(pThis, &pStmt); + if (RT_SUCCESS(rc)) + { + pAstNodeSwitch->pCond = pExpr; + pAstNodeSwitch->pStmt = pStmt; + } + else + vdScriptAstNodeFree(&pExpr->Core); + } + else if (RT_SUCCESS(rc)) + { + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n"); + vdScriptAstNodeFree(&pExpr->Core); + } + } + else + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"(\", got ...\n"); + + return rc; +} + +/** + * Parse a while or do ... while statement. + * + * @returns VBox status code. + * @param pThis The script context. + * @param pAstNodeWhile Uninitialized while AST node. + * + * @note The caller skipped the "while" or "do" token already. + */ +static int vdScriptParseWhile(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTWHILE pAstNodeWhile, bool fDoWhile) +{ + int rc = VINF_SUCCESS; + + pAstNodeWhile->fDoWhile = fDoWhile; + + if (fDoWhile) + { + PVDSCRIPTASTSTMT pStmt = NULL; + rc = vdScriptParseStatement(pThis, &pStmt); + if ( RT_SUCCESS(rc) + && vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_WHILE)) + { + if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '(')) + { + PVDSCRIPTASTEXPR pExpr = NULL; + + rc = vdScriptParseExpression(pThis, &pExpr); + if ( RT_SUCCESS(rc) + && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')')) + { + if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ';')) + { + pAstNodeWhile->pCond = pExpr; + pAstNodeWhile->pStmt = pStmt; + } + else + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \";\", got ...\n"); + } + else if (RT_SUCCESS(rc)) + { + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n"); + vdScriptAstNodeFree(&pExpr->Core); + } + } + else + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"(\", got ...\n"); + } + else if (RT_SUCCESS(rc)) + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"while\", got ...\n"); + + if ( RT_FAILURE(rc) + && pStmt) + vdScriptAstNodeFree(&pStmt->Core); + } + else + { + if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '(')) + { + PVDSCRIPTASTEXPR pExpr = NULL; + + rc = vdScriptParseExpression(pThis, &pExpr); + if ( RT_SUCCESS(rc) + && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')')) + { + PVDSCRIPTASTSTMT pStmt = NULL; + rc = vdScriptParseStatement(pThis, &pStmt); + if (RT_SUCCESS(rc)) + { + pAstNodeWhile->pCond = pExpr; + pAstNodeWhile->pStmt = pStmt; + } + else + vdScriptAstNodeFree(&pExpr->Core); + } + else if (RT_SUCCESS(rc)) + { + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \")\", got ...\n"); + vdScriptAstNodeFree(&pExpr->Core); + } + } + else + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"(\", got ...\n"); + } + + return rc; +} + +/** + * Parse a for statement. + * + * @returns VBox status code. + * @param pThis The script context. + * @param pAstNodeFor Uninitialized for AST node. + * + * @note The caller skipped the "for" token already. + */ +static int vdScriptParseFor(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTFOR pAstNodeFor) +{ + int rc = VINF_SUCCESS; + + if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '(')) + { + PVDSCRIPTASTEXPR pExprStart = NULL; + PVDSCRIPTASTEXPR pExprCond = NULL; + PVDSCRIPTASTEXPR pExpr3 = NULL; + + rc = vdScriptParseExpression(pThis, &pExprStart); + if ( RT_SUCCESS(rc) + && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ';')) + { + rc = vdScriptParseExpression(pThis, &pExprCond); + if ( RT_SUCCESS(rc) + && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ';')) + { + rc = vdScriptParseExpression(pThis, &pExpr3); + if ( RT_SUCCESS(rc) + && vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')')) + { + PVDSCRIPTASTSTMT pStmt = NULL; + rc = vdScriptParseStatement(pThis, &pStmt); + if (RT_SUCCESS(rc)) + { + pAstNodeFor->pExprStart = pExprStart; + pAstNodeFor->pExprCond = pExprCond; + pAstNodeFor->pExpr3 = pExpr3; + pAstNodeFor->pStmt = pStmt; + } + } + } + else if (RT_SUCCESS(rc)) + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \";\", got ...\n"); + } + else if (RT_SUCCESS(rc)) + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \";\", got ...\n"); + + if (RT_FAILURE(rc)) + { + if (pExprStart) + vdScriptAstNodeFree(&pExprStart->Core); + if (pExprCond) + vdScriptAstNodeFree(&pExprCond->Core); + if (pExpr3) + vdScriptAstNodeFree(&pExpr3->Core); + } + } + else + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"(\", got ...\n"); + + return rc; +} + +/** + * Parse a declaration. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeDecl Where to store the declaration AST node on success. + */ +static int vdScriptParseDeclaration(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTDECL *ppAstNodeDecl) +{ + int rc = VERR_NOT_IMPLEMENTED; + return rc; +} + +/** + * Parse a statement. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeStmt Where to store the statement AST node on success. + */ +static int vdScriptParseStatement(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTSTMT *ppAstNodeStmt) +{ + int rc = VINF_SUCCESS; + + /* Shortcut for a new compound statement. */ + if (vdScriptTokenizerIsPunctuatorEqual(pThis->pTokenizer, '{')) + rc = vdScriptParseCompoundStatement(pThis, ppAstNodeStmt); + else + { + PVDSCRIPTASTSTMT pAstNodeStmt = (PVDSCRIPTASTSTMT)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_STATEMENT); + + if (pAstNodeStmt) + { + + if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_DEFAULT)) + { + if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ':')) + { + PVDSCRIPTASTSTMT pAstNodeStmtDef = NULL; + pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_DEFAULT; + rc = vdScriptParseStatement(pThis, &pAstNodeStmtDef); + if (RT_SUCCESS(rc)) + pAstNodeStmt->pStmt = pAstNodeStmtDef; + } + else + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \":\", got ...\n"); + } + else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_CASE)) + { + PVDSCRIPTASTEXPR pAstNodeExpr = NULL; + rc = vdScriptParseCondExpression(pThis, &pAstNodeExpr); + if (RT_SUCCESS(rc)) + { + if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ':')) + { + PVDSCRIPTASTSTMT pAstNodeCaseStmt = NULL; + rc = vdScriptParseStatement(pThis, &pAstNodeCaseStmt); + if (RT_SUCCESS(rc)) + { + pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_CASE; + pAstNodeStmt->Case.pExpr = pAstNodeExpr; + pAstNodeStmt->Case.pStmt = pAstNodeCaseStmt; + } + } + else + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \":\", got ...\n"); + + if (RT_FAILURE(rc)) + vdScriptAstNodeFree(&pAstNodeExpr->Core); + } + } + else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_IF)) + { + pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_IF; + rc = vdScriptParseIf(pThis, &pAstNodeStmt->If); + } + else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_SWITCH)) + { + pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_SWITCH; + rc = vdScriptParseSwitch(pThis, &pAstNodeStmt->Switch); + } + else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_WHILE)) + { + pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_WHILE; + rc = vdScriptParseWhile(pThis, &pAstNodeStmt->While, false /* fDoWhile */); + } + else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_DO)) + { + pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_WHILE; + rc = vdScriptParseWhile(pThis, &pAstNodeStmt->While, true /* fDoWhile */); + } + else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_FOR)) + { + pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_FOR; + rc = vdScriptParseFor(pThis, &pAstNodeStmt->For); + } + else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_CONTINUE)) + { + pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_CONTINUE; + if (!vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ';')) + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \";\", got ...\n"); + } + else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_BREAK)) + { + pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_BREAK; + if (!vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ';')) + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \";\", got ...\n"); + } + else if (vdScriptTokenizerSkipIfIsKeywordEqual(pThis->pTokenizer, VDSCRIPTTOKENKEYWORD_RETURN)) + { + pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_RETURN; + if (!vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ';')) + { + rc = vdScriptParseExpression(pThis, &pAstNodeStmt->pExpr); + if ( RT_SUCCESS(rc) + && !vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ';')) + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \";\", got ...\n"); + } + else + pAstNodeStmt->pExpr = NULL; /* No expression for return. */ + } + else + { + /* Must be an expression. */ + pAstNodeStmt->enmStmtType = VDSCRIPTSTMTTYPE_EXPRESSION; + rc = vdScriptParseExpression(pThis, &pAstNodeStmt->pExpr); + if ( RT_SUCCESS(rc) + && !vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ';')) + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \";\", got ...\n"); + } + + if (RT_SUCCESS(rc)) + *ppAstNodeStmt = pAstNodeStmt; + else + vdScriptAstNodeFree(&pAstNodeStmt->Core); + } + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory creating statement node\n"); + } + + return rc; +} + +/** + * Parses a compound statement. + * + * @returns VBox status code. + * @param pThis The script context. + * @param ppAstNodeCompound Where to store the compound AST node on success. + */ +static int vdScriptParseCompoundStatement(PVDSCRIPTCTXINT pThis, PVDSCRIPTASTSTMT *ppAstNodeCompound) +{ + int rc = VINF_SUCCESS; + + if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '{')) + { + PVDSCRIPTASTSTMT pAstNodeCompound = (PVDSCRIPTASTSTMT)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_STATEMENT); + if (pAstNodeCompound) + { + pAstNodeCompound->enmStmtType = VDSCRIPTSTMTTYPE_COMPOUND; + RTListInit(&pAstNodeCompound->Compound.ListDecls); + RTListInit(&pAstNodeCompound->Compound.ListStmts); + while (!vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '}')) + { + /* + * Check whether we have a declaration or a statement. + * For now we assume that 2 identifier tokens specify a declaration + * (type + variable name). Having two consecutive identifers is not possible + * for a statement. + */ + if ( vdScriptTokenizerGetTokenClass(pThis->pTokenizer) == VDTOKENCLASS_IDENTIFIER + && vdScriptTokenizerPeekNextClass(pThis->pTokenizer) == VDTOKENCLASS_IDENTIFIER) + { + PVDSCRIPTASTDECL pAstNodeDecl = NULL; + rc = vdScriptParseDeclaration(pThis, &pAstNodeDecl); + if (RT_SUCCESS(rc)) + RTListAppend(&pAstNodeCompound->Compound.ListDecls, &pAstNodeDecl->Core.ListNode); + } + else + { + PVDSCRIPTASTSTMT pAstNodeStmt = NULL; + rc = vdScriptParseStatement(pThis, &pAstNodeStmt); + if (RT_SUCCESS(rc)) + RTListAppend(&pAstNodeCompound->Compound.ListStmts, &pAstNodeStmt->Core.ListNode); + } + + if (RT_FAILURE(rc)) + break; + } + + if (RT_SUCCESS(rc)) + *ppAstNodeCompound = pAstNodeCompound; + else + vdScriptAstNodeFree(&pAstNodeCompound->Core); + } + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory creating compound statement node\n"); + } + else + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"{\" got...\n"); + + + return rc; +} + +/** + * Parses a function definition from the given tokenizer. + * + * @returns VBox status code. + * @param pThis The script context. + */ +static int vdScriptParseAddFnDef(PVDSCRIPTCTXINT pThis) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTASTIDE pRetType = NULL; + PVDSCRIPTASTIDE pFnIde = NULL; + + LogFlowFunc(("pThis=%p\n", pThis)); + + /* Put return type on the stack. */ + rc = vdScriptParseIde(pThis, &pRetType); + if (RT_SUCCESS(rc)) + { + /* Function name */ + rc = vdScriptParseIde(pThis, &pFnIde); + if (RT_SUCCESS(rc)) + { + if (vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, '(')) + { + PVDSCRIPTASTFN pAstNodeFn = (PVDSCRIPTASTFN)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_FUNCTION); + + if (pAstNodeFn) + { + pAstNodeFn->pFnIde = pFnIde; + pAstNodeFn->pRetType = pRetType; + RTListInit(&pAstNodeFn->ListArgs); + + pFnIde = NULL; + pRetType = NULL; + + /* Parse parameter list, create empty parameter list AST node and put it on the stack. */ + while (!vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ')')) + { + PVDSCRIPTASTIDE pArgType = NULL; + PVDSCRIPTASTIDE pArgIde = NULL; + /* Parse two identifiers, first one is the type, second the name. */ + rc = vdScriptParseIde(pThis, &pArgType); + if (RT_SUCCESS(rc)) + rc = vdScriptParseIde(pThis, &pArgIde); + + if (RT_SUCCESS(rc)) + { + PVDSCRIPTASTFNARG pAstNodeFnArg = (PVDSCRIPTASTFNARG)vdScriptAstNodeAlloc(VDSCRIPTASTCLASS_FUNCTIONARG); + if (pAstNodeFnArg) + { + pAstNodeFnArg->pArgIde = pArgIde; + pAstNodeFnArg->pType = pArgType; + RTListAppend(&pAstNodeFn->ListArgs, &pAstNodeFnArg->Core.ListNode); + pAstNodeFn->cArgs++; + } + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating function argument AST node\n"); + } + + if (RT_FAILURE(rc)) + { + if (pArgType) + vdScriptAstNodeFree(&pArgType->Core); + if (pArgIde) + vdScriptAstNodeFree(&pArgIde->Core); + } + + if ( !vdScriptTokenizerSkipIfIsPunctuatorEqual(pThis->pTokenizer, ',') + && !vdScriptTokenizerIsPunctuatorEqual(pThis->pTokenizer, ')')) + { + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \",\" or \")\" got...\n"); + break; + } + } + + /* Parse the compound or statement block now. */ + if (RT_SUCCESS(rc)) + { + PVDSCRIPTASTSTMT pAstCompound = NULL; + + rc = vdScriptParseCompoundStatement(pThis, &pAstCompound); + if (RT_SUCCESS(rc)) + { + /* + * Link compound statement block to function AST node and add it to the + * list of functions. + */ + pAstNodeFn->pCompoundStmts = pAstCompound; + RTListAppend(&pThis->ListAst, &pAstNodeFn->Core.ListNode); + + PVDSCRIPTFN pFn = (PVDSCRIPTFN)RTMemAllocZ(sizeof(VDSCRIPTFN)); + if (pFn) + { + pFn->Core.pszString = pAstNodeFn->pFnIde->aszIde; + pFn->Core.cchString = strlen(pFn->Core.pszString); + pFn->fExternal = false; + pFn->Type.Internal.pAstFn = pAstNodeFn; + /** @todo: Parameters. */ + RTStrSpaceInsert(&pThis->hStrSpaceFn, &pFn->Core); + } + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory allocating memory for function\n"); + } + } + + if (RT_FAILURE(rc)) + vdScriptAstNodeFree(&pAstNodeFn->Core); + } + else + rc = vdScriptParserError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Parser: Out of memory allocating function AST node\n"); + } + else + rc = vdScriptParserError(pThis, VERR_INVALID_PARAMETER, RT_SRC_POS, "Parser: Expected \"(\" got...\n"); + } + } + + if (RT_FAILURE(rc)) + { + if (pRetType) + vdScriptAstNodeFree(&pRetType->Core); + if (pFnIde) + vdScriptAstNodeFree(&pFnIde->Core); + } + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +/** + * Parses the script from the given tokenizer. + * + * @returns VBox status code. + * @param pThis The script context. + */ +static int vdScriptParseFromTokenizer(PVDSCRIPTCTXINT pThis) +{ + int rc = VINF_SUCCESS; + + LogFlowFunc(("pThis=%p\n", pThis)); + + /* This is a very very simple LL(1) parser, don't expect much from it for now :). */ + while ( RT_SUCCESS(rc) + && !vdScriptTokenizerIsEos(pThis->pTokenizer)) + rc = vdScriptParseAddFnDef(pThis); + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +DECLHIDDEN(int) VDScriptCtxCreate(PVDSCRIPTCTX phScriptCtx) +{ + int rc = VINF_SUCCESS; + + LogFlowFunc(("phScriptCtx=%p\n", phScriptCtx)); + + AssertPtrReturn(phScriptCtx, VERR_INVALID_POINTER); + + PVDSCRIPTCTXINT pThis = (PVDSCRIPTCTXINT)RTMemAllocZ(sizeof(VDSCRIPTCTXINT)); + if (pThis) + { + pThis->hStrSpaceFn = NULL; + RTListInit(&pThis->ListAst); + *phScriptCtx = pThis; + } + else + rc = VINF_SUCCESS; + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +static int vdScriptCtxDestroyFnSpace(PRTSTRSPACECORE pStr, void *pvUser) +{ + NOREF(pvUser); + + /* + * Just free the whole structure, the AST for internal functions will be + * destroyed later. + */ + RTMemFree(pStr); + return VINF_SUCCESS; +} + +DECLHIDDEN(void) VDScriptCtxDestroy(VDSCRIPTCTX hScriptCtx) +{ + PVDSCRIPTCTXINT pThis = hScriptCtx; + + AssertPtrReturnVoid(pThis); + + LogFlowFunc(("hScriptCtx=%p\n", pThis)); + + RTStrSpaceDestroy(&pThis->hStrSpaceFn, vdScriptCtxDestroyFnSpace, NULL); + + /** @todo: Go through the list and destroy all ASTs. */ + RTMemFree(pThis); +} + +DECLHIDDEN(int) VDScriptCtxCallbacksRegister(VDSCRIPTCTX hScriptCtx, PCVDSCRIPTCALLBACK paCallbacks, + unsigned cCallbacks, void *pvUser) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTCTXINT pThis = hScriptCtx; + + LogFlowFunc(("hScriptCtx=%p paCallbacks=%p cCallbacks=%u pvUser=%p\n", + pThis, paCallbacks, cCallbacks, pvUser)); + + AssertPtrReturn(pThis, VERR_INVALID_POINTER); + AssertPtrReturn(paCallbacks, VERR_INVALID_POINTER); + AssertReturn(cCallbacks > 0, VERR_INVALID_PARAMETER); + + /** @todo: Unregister already registered callbacks in case of an error. */ + do + { + PVDSCRIPTFN pFn = NULL; + + if (RTStrSpaceGet(&pThis->hStrSpaceFn, paCallbacks->pszFnName)) + { + rc = VERR_DUPLICATE; + break; + } + + pFn = (PVDSCRIPTFN)RTMemAllocZ(RT_OFFSETOF(VDSCRIPTFN, aenmArgTypes[paCallbacks->cArgs])); + if (!pFn) + { + rc = VERR_NO_MEMORY; + break; + } + + /** @todo: Validate argument and returns types. */ + pFn->Core.pszString = paCallbacks->pszFnName; + pFn->Core.cchString = strlen(pFn->Core.pszString); + pFn->fExternal = true; + pFn->Type.External.pfnCallback = paCallbacks->pfnCallback; + pFn->Type.External.pvUser = pvUser; + pFn->enmTypeRetn = paCallbacks->enmTypeReturn; + pFn->cArgs = paCallbacks->cArgs; + + for (unsigned i = 0; i < paCallbacks->cArgs; i++) + pFn->aenmArgTypes[i] = paCallbacks->paArgs[i]; + + RTStrSpaceInsert(&pThis->hStrSpaceFn, &pFn->Core); + cCallbacks--; + paCallbacks++; + } + while (cCallbacks); + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +DECLHIDDEN(int) VDScriptCtxLoadScript(VDSCRIPTCTX hScriptCtx, const char *pszScript) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTCTXINT pThis = hScriptCtx; + + LogFlowFunc(("hScriptCtx=%p pszScript=%p\n", pThis, pszScript)); + + AssertPtrReturn(pThis, VERR_INVALID_POINTER); + AssertPtrReturn(pszScript, VERR_INVALID_POINTER); + + PVDTOKENIZER pTokenizer = vdScriptTokenizerCreate(pszScript); + if (pTokenizer) + { + pThis->pTokenizer = pTokenizer; + rc = vdScriptParseFromTokenizer(pThis); + pThis->pTokenizer = NULL; + RTMemFree(pTokenizer); + } + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +DECLHIDDEN(int) VDScriptCtxCallFn(VDSCRIPTCTX hScriptCtx, const char *pszFnCall, + PVDSCRIPTARG paArgs, unsigned cArgs) +{ + PVDSCRIPTCTXINT pThis = hScriptCtx; + VDSCRIPTARG Ret; + return vdScriptCtxInterprete(pThis, pszFnCall, paArgs, cArgs, &Ret); +} diff --git a/src/VBox/Storage/testcase/VDScript.h b/src/VBox/Storage/testcase/VDScript.h new file mode 100644 index 00000000..f1731dfd --- /dev/null +++ b/src/VBox/Storage/testcase/VDScript.h @@ -0,0 +1,153 @@ +/** @file + * + * VBox HDD container test utility - scripting engine. + */ + +/* + * Copyright (C) 2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ +#ifndef _VDScript_h__ +#define _VDScript_h__ + +/** Handle to the scripting context. */ +typedef struct VDSCRIPTCTXINT *VDSCRIPTCTX; +/** Pointer to a scripting context handle. */ +typedef VDSCRIPTCTX *PVDSCRIPTCTX; + +/** + * Supprted primitive types in the scripting engine. + */ +typedef enum VDSCRIPTTYPE +{ + /** Invalid type, do not use. */ + VDSCRIPTTYPE_INVALID = 0, + /** void type, used for no return value of methods. */ + VDSCRIPTTYPE_VOID, + /** unsigned 8bit integer. */ + VDSCRIPTTYPE_UINT8, + VDSCRIPTTYPE_INT8, + VDSCRIPTTYPE_UINT16, + VDSCRIPTTYPE_INT16, + VDSCRIPTTYPE_UINT32, + VDSCRIPTTYPE_INT32, + VDSCRIPTTYPE_UINT64, + VDSCRIPTTYPE_INT64, + VDSCRIPTTYPE_STRING, + VDSCRIPTTYPE_BOOL, + /** As usual, the 32bit blowup hack. */ + VDSCRIPTTYPE_32BIT_HACK = 0x7fffffff +} VDSCRIPTTYPE; +/** Pointer to a type. */ +typedef VDSCRIPTTYPE *PVDSCRIPTTYPE; +/** Pointer to a const type. */ +typedef const VDSCRIPTTYPE *PCVDSCRIPTTYPE; + +/** + * Script argument. + */ +typedef struct VDSCRIPTARG +{ + /** Type of the argument. */ + VDSCRIPTTYPE enmType; + /** Value */ + union + { + uint8_t u8; + int8_t i8; + uint16_t u16; + int16_t i16; + uint32_t u32; + int32_t i32; + uint64_t u64; + int64_t i64; + const char *psz; + bool f; + }; +} VDSCRIPTARG; +/** Pointer to an argument. */ +typedef VDSCRIPTARG *PVDSCRIPTARG; + +/** Script callback. */ +typedef DECLCALLBACK(int) FNVDSCRIPTCALLBACK(PVDSCRIPTARG paScriptArgs, void *pvUser); +/** Pointer to a script callback. */ +typedef FNVDSCRIPTCALLBACK *PFNVDSCRIPTCALLBACK; + +/** + * Callback registration structure. + */ +typedef struct VDSCRIPTCALLBACK +{ + /** The function name. */ + const char *pszFnName; + /** The return type of the function. */ + VDSCRIPTTYPE enmTypeReturn; + /** Pointer to the array of argument types. */ + PCVDSCRIPTTYPE paArgs; + /** Number of arguments this method takes. */ + unsigned cArgs; + /** The callback handler. */ + PFNVDSCRIPTCALLBACK pfnCallback; +} VDSCRIPTCALLBACK; +/** Pointer to a callback register entry. */ +typedef VDSCRIPTCALLBACK *PVDSCRIPTCALLBACK; +/** Pointer to a const callback register entry. */ +typedef const VDSCRIPTCALLBACK *PCVDSCRIPTCALLBACK; + +/** + * Create a new scripting context. + * + * @returns VBox status code. + * @param phScriptCtx Where to store the scripting context on success. + */ +DECLHIDDEN(int) VDScriptCtxCreate(PVDSCRIPTCTX phScriptCtx); + +/** + * Destroys the given scripting context. + * + * @returns nothing. + * @param hScriptCtx The script context to destroy. + */ +DECLHIDDEN(void) VDScriptCtxDestroy(VDSCRIPTCTX hScriptCtx); + +/** + * Register callbacks for the scripting context. + * + * @returns VBox status code. + * @param hScriptCtx The script context handle. + * @param paCallbacks Pointer to the callbacks to register. + * @param cCallbacks Number of callbacks in the array. + * @param pvUser Opaque user data to pass on the callback invocation. + */ +DECLHIDDEN(int) VDScriptCtxCallbacksRegister(VDSCRIPTCTX hScriptCtx, PCVDSCRIPTCALLBACK paCallbacks, + unsigned cCallbacks, void *pvUser); + +/** + * Load a given script into the context. + * + * @returns VBox status code. + * @param hScriptCtx The script context handle. + * @param pszScript Pointer to the char buffer containing the script. + */ +DECLHIDDEN(int) VDScriptCtxLoadScript(VDSCRIPTCTX hScriptCtx, const char *pszScript); + +/** + * Execute a given method in the script context. + * + * @returns VBox status code. + * @param hScriptCtx The script context handle. + * @param pszFnCall The method to call. + * @param paArgs Pointer to arguments to pass. + * @param cArgs Number of arguments. + */ +DECLHIDDEN(int) VDScriptCtxCallFn(VDSCRIPTCTX hScriptCtx, const char *pszFnCall, + PVDSCRIPTARG paArgs, unsigned cArgs); + +#endif /* _VDScript_h__ */ diff --git a/src/VBox/Storage/testcase/VDScriptAst.cpp b/src/VBox/Storage/testcase/VDScriptAst.cpp new file mode 100644 index 00000000..8b3d0011 --- /dev/null +++ b/src/VBox/Storage/testcase/VDScriptAst.cpp @@ -0,0 +1,343 @@ +/** $Id: VDScriptAst.cpp $ */ +/** @file + * + * VBox HDD container test utility - scripting engine AST node related functions. + */ + +/* + * Copyright (C) 2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ +#define LOGGROUP LOGGROUP_DEFAULT +#include <iprt/list.h> +#include <iprt/mem.h> +#include <iprt/assert.h> +#include <iprt/string.h> + +#include <VBox/log.h> + +#include "VDScriptAst.h" + +/** + * Put all child nodes of the given expression AST node onto the given to free list. + * + * @returns nothing. + * @param pList The free list to append everything to. + * @param pAstNode The expression node to free. + */ +static void vdScriptAstNodeExpressionPutOnFreeList(PRTLISTANCHOR pList, PVDSCRIPTASTCORE pAstNode) +{ + AssertMsgReturnVoid(pAstNode->enmClass == VDSCRIPTASTCLASS_EXPRESSION, + ("Given AST node is not a statement\n")); + + PVDSCRIPTASTEXPR pExpr = (PVDSCRIPTASTEXPR)pAstNode; + switch (pExpr->enmType) + { + case VDSCRIPTEXPRTYPE_PRIMARY_NUMCONST: + case VDSCRIPTEXPRTYPE_PRIMARY_BOOLEAN: + break; + case VDSCRIPTEXPRTYPE_PRIMARY_STRINGCONST: + RTStrFree((char *)pExpr->pszStr); + break; + case VDSCRIPTEXPRTYPE_PRIMARY_IDENTIFIER: + { + RTListAppend(pList, &pExpr->pIde->Core.ListNode); + break; + } + case VDSCRIPTEXPRTYPE_ASSIGNMENT_LIST: + { + while (!RTListIsEmpty(&pExpr->ListExpr)) + { + PVDSCRIPTASTCORE pNode = RTListGetFirst(&pExpr->ListExpr, VDSCRIPTASTCORE, ListNode); + RTListNodeRemove(&pNode->ListNode); + RTListAppend(pList, &pNode->ListNode); + } + break; + } + case VDSCRIPTEXPRTYPE_POSTFIX_FNCALL: + { + RTListAppend(pList, &pExpr->FnCall.pFnIde->Core.ListNode); + while (!RTListIsEmpty(&pExpr->FnCall.ListArgs)) + { + PVDSCRIPTASTCORE pNode = RTListGetFirst(&pExpr->FnCall.ListArgs, VDSCRIPTASTCORE, ListNode); + RTListNodeRemove(&pNode->ListNode); + RTListAppend(pList, &pNode->ListNode); + } + break; + } + case VDSCRIPTEXPRTYPE_POSTFIX_INCREMENT: + case VDSCRIPTEXPRTYPE_POSTFIX_DECREMENT: + case VDSCRIPTEXPRTYPE_UNARY_INCREMENT: + case VDSCRIPTEXPRTYPE_UNARY_DECREMENT: + case VDSCRIPTEXPRTYPE_UNARY_POSSIGN: + case VDSCRIPTEXPRTYPE_UNARY_NEGSIGN: + case VDSCRIPTEXPRTYPE_UNARY_INVERT: + case VDSCRIPTEXPRTYPE_UNARY_NEGATE: + { + RTListAppend(pList, &pExpr->pExpr->Core.ListNode); + break; + } + case VDSCRIPTEXPRTYPE_MULTIPLICATION: + case VDSCRIPTEXPRTYPE_DIVISION: + case VDSCRIPTEXPRTYPE_MODULUS: + case VDSCRIPTEXPRTYPE_ADDITION: + case VDSCRIPTEXPRTYPE_SUBTRACTION: + case VDSCRIPTEXPRTYPE_LSR: + case VDSCRIPTEXPRTYPE_LSL: + case VDSCRIPTEXPRTYPE_LOWER: + case VDSCRIPTEXPRTYPE_HIGHER: + case VDSCRIPTEXPRTYPE_LOWEREQUAL: + case VDSCRIPTEXPRTYPE_HIGHEREQUAL: + case VDSCRIPTEXPRTYPE_EQUAL: + case VDSCRIPTEXPRTYPE_NOTEQUAL: + case VDSCRIPTEXPRTYPE_BITWISE_AND: + case VDSCRIPTEXPRTYPE_BITWISE_XOR: + case VDSCRIPTEXPRTYPE_BITWISE_OR: + case VDSCRIPTEXPRTYPE_LOGICAL_AND: + case VDSCRIPTEXPRTYPE_LOGICAL_OR: + case VDSCRIPTEXPRTYPE_ASSIGN: + case VDSCRIPTEXPRTYPE_ASSIGN_MULT: + case VDSCRIPTEXPRTYPE_ASSIGN_DIV: + case VDSCRIPTEXPRTYPE_ASSIGN_MOD: + case VDSCRIPTEXPRTYPE_ASSIGN_ADD: + case VDSCRIPTEXPRTYPE_ASSIGN_SUB: + case VDSCRIPTEXPRTYPE_ASSIGN_LSL: + case VDSCRIPTEXPRTYPE_ASSIGN_LSR: + case VDSCRIPTEXPRTYPE_ASSIGN_AND: + case VDSCRIPTEXPRTYPE_ASSIGN_XOR: + case VDSCRIPTEXPRTYPE_ASSIGN_OR: + { + RTListAppend(pList, &pExpr->BinaryOp.pLeftExpr->Core.ListNode); + RTListAppend(pList, &pExpr->BinaryOp.pRightExpr->Core.ListNode); + break; + } + case VDSCRIPTEXPRTYPE_INVALID: + default: + AssertMsgFailedReturnVoid(("Invalid AST node expression type %d\n", + pExpr->enmType)); + } +} + +/** + * Free a given statement AST node and put everything on the given to free list. + * + * @returns nothing. + * @param pList The free list to append everything to. + * @param pAstNode The statement node to free. + */ +static void vdScriptAstNodeStatmentPutOnFreeList(PRTLISTANCHOR pList, PVDSCRIPTASTCORE pAstNode) +{ + AssertMsgReturnVoid(pAstNode->enmClass == VDSCRIPTASTCLASS_STATEMENT, + ("Given AST node is not a statement\n")); + + PVDSCRIPTASTSTMT pStmt = (PVDSCRIPTASTSTMT)pAstNode; + switch (pStmt->enmStmtType) + { + case VDSCRIPTSTMTTYPE_COMPOUND: + { + /* Put all declarations on the to free list. */ + while (!RTListIsEmpty(&pStmt->Compound.ListDecls)) + { + PVDSCRIPTASTCORE pNode = RTListGetFirst(&pStmt->Compound.ListDecls, VDSCRIPTASTCORE, ListNode); + RTListNodeRemove(&pNode->ListNode); + RTListAppend(pList, &pNode->ListNode); + } + + /* Put all statements on the to free list. */ + while (!RTListIsEmpty(&pStmt->Compound.ListStmts)) + { + PVDSCRIPTASTCORE pNode = RTListGetFirst(&pStmt->Compound.ListDecls, VDSCRIPTASTCORE, ListNode); + RTListNodeRemove(&pNode->ListNode); + RTListAppend(pList, &pNode->ListNode); + } + break; + } + case VDSCRIPTSTMTTYPE_EXPRESSION: + { + if (pStmt->pExpr) + RTListAppend(pList, &pStmt->pExpr->Core.ListNode); + break; + } + case VDSCRIPTSTMTTYPE_IF: + { + RTListAppend(pList, &pStmt->If.pCond->Core.ListNode); + RTListAppend(pList, &pStmt->If.pTrueStmt->Core.ListNode); + if (pStmt->If.pElseStmt) + RTListAppend(pList, &pStmt->If.pElseStmt->Core.ListNode); + break; + } + case VDSCRIPTSTMTTYPE_SWITCH: + { + RTListAppend(pList, &pStmt->Switch.pCond->Core.ListNode); + RTListAppend(pList, &pStmt->Switch.pStmt->Core.ListNode); + break; + } + case VDSCRIPTSTMTTYPE_WHILE: + { + RTListAppend(pList, &pStmt->While.pCond->Core.ListNode); + RTListAppend(pList, &pStmt->While.pStmt->Core.ListNode); + break; + } + case VDSCRIPTSTMTTYPE_FOR: + { + RTListAppend(pList, &pStmt->For.pExprStart->Core.ListNode); + RTListAppend(pList, &pStmt->For.pExprCond->Core.ListNode); + RTListAppend(pList, &pStmt->For.pExpr3->Core.ListNode); + RTListAppend(pList, &pStmt->For.pStmt->Core.ListNode); + break; + } + case VDSCRIPTSTMTTYPE_RETURN: + { + if (pStmt->pExpr) + RTListAppend(pList, &pStmt->pExpr->Core.ListNode); + break; + } + case VDSCRIPTSTMTTYPE_CASE: + { + RTListAppend(pList, &pStmt->Case.pExpr->Core.ListNode); + RTListAppend(pList, &pStmt->Case.pStmt->Core.ListNode); + break; + } + case VDSCRIPTSTMTTYPE_DEFAULT: + { + RTListAppend(pList, &pStmt->Case.pStmt->Core.ListNode); + break; + } + case VDSCRIPTSTMTTYPE_CONTINUE: + case VDSCRIPTSTMTTYPE_BREAK: + break; + case VDSCRIPTSTMTTYPE_INVALID: + default: + AssertMsgFailedReturnVoid(("Invalid AST node statement type %d\n", + pStmt->enmStmtType)); + } +} + +DECLHIDDEN(void) vdScriptAstNodeFree(PVDSCRIPTASTCORE pAstNode) +{ + RTLISTANCHOR ListFree; + + /* + * The node is not allowed to be part of a list because we need it + * for the nodes to free list. + */ + Assert(RTListIsEmpty(&pAstNode->ListNode)); + RTListInit(&ListFree); + RTListAppend(&ListFree, &pAstNode->ListNode); + + do + { + pAstNode = RTListGetFirst(&ListFree, VDSCRIPTASTCORE, ListNode); + RTListNodeRemove(&pAstNode->ListNode); + + switch (pAstNode->enmClass) + { + case VDSCRIPTASTCLASS_FUNCTION: + { + PVDSCRIPTASTFN pFn = (PVDSCRIPTASTFN)pAstNode; + + if (pFn->pRetType) + RTListAppend(&ListFree, &pFn->pRetType->Core.ListNode); + if (pFn->pFnIde) + RTListAppend(&ListFree, &pFn->pFnIde->Core.ListNode); + + /* Put argument list on the to free list. */ + while (!RTListIsEmpty(&pFn->ListArgs)) + { + PVDSCRIPTASTCORE pArg = RTListGetFirst(&pFn->ListArgs, VDSCRIPTASTCORE, ListNode); + RTListNodeRemove(&pArg->ListNode); + RTListAppend(&ListFree, &pArg->ListNode); + } + + /* Put compound statement onto the list. */ + RTListAppend(&ListFree, &pFn->pCompoundStmts->Core.ListNode); + break; + } + case VDSCRIPTASTCLASS_FUNCTIONARG: + { + PVDSCRIPTASTFNARG pAstNodeArg = (PVDSCRIPTASTFNARG)pAstNode; + if (pAstNodeArg->pType) + RTListAppend(&ListFree, &pAstNodeArg->pType->Core.ListNode); + if (pAstNodeArg->pArgIde) + RTListAppend(&ListFree, &pAstNodeArg->pArgIde->Core.ListNode); + break; + } + case VDSCRIPTASTCLASS_IDENTIFIER: + break; + case VDSCRIPTASTCLASS_DECLARATION: + break; + case VDSCRIPTASTCLASS_STATEMENT: + { + vdScriptAstNodeStatmentPutOnFreeList(&ListFree, pAstNode); + break; + } + case VDSCRIPTASTCLASS_EXPRESSION: + { + vdScriptAstNodeExpressionPutOnFreeList(&ListFree, pAstNode); + break; + } + case VDSCRIPTASTCLASS_INVALID: + default: + AssertMsgFailedReturnVoid(("Invalid AST node class given %d\n", pAstNode->enmClass)); + } + + RTMemFree(pAstNode); + } while (!RTListIsEmpty(&ListFree)); + +} + +DECLHIDDEN(PVDSCRIPTASTCORE) vdScriptAstNodeAlloc(VDSCRIPTASTCLASS enmClass) +{ + size_t cbAlloc = 0; + + switch (enmClass) + { + case VDSCRIPTASTCLASS_FUNCTION: + cbAlloc = sizeof(VDSCRIPTASTFN); + break; + case VDSCRIPTASTCLASS_FUNCTIONARG: + cbAlloc = sizeof(VDSCRIPTASTFNARG); + break; + case VDSCRIPTASTCLASS_DECLARATION: + cbAlloc = sizeof(VDSCRIPTASTDECL); + break; + case VDSCRIPTASTCLASS_STATEMENT: + cbAlloc = sizeof(VDSCRIPTASTSTMT); + break; + case VDSCRIPTASTCLASS_EXPRESSION: + cbAlloc = sizeof(VDSCRIPTASTEXPR); + break; + case VDSCRIPTASTCLASS_IDENTIFIER: + case VDSCRIPTASTCLASS_INVALID: + default: + AssertMsgFailedReturn(("Invalid AST node class given %d\n", enmClass), NULL); + } + + PVDSCRIPTASTCORE pAstNode = (PVDSCRIPTASTCORE)RTMemAllocZ(cbAlloc); + if (pAstNode) + { + pAstNode->enmClass = enmClass; + RTListInit(&pAstNode->ListNode); + } + + return pAstNode; +} + +DECLHIDDEN(PVDSCRIPTASTIDE) vdScriptAstNodeIdeAlloc(unsigned cchIde) +{ + PVDSCRIPTASTIDE pAstNode = (PVDSCRIPTASTIDE)RTMemAllocZ(RT_OFFSETOF(VDSCRIPTASTIDE, aszIde[cchIde + 1])); + if (pAstNode) + { + pAstNode->Core.enmClass = VDSCRIPTASTCLASS_IDENTIFIER; + RTListInit(&pAstNode->Core.ListNode); + } + + return pAstNode; +} diff --git a/src/VBox/Storage/testcase/VDScriptAst.h b/src/VBox/Storage/testcase/VDScriptAst.h new file mode 100644 index 00000000..3f324094 --- /dev/null +++ b/src/VBox/Storage/testcase/VDScriptAst.h @@ -0,0 +1,452 @@ +/** @file + * + * VBox HDD container test utility - scripting engine, AST related structures. + */ + +/* + * Copyright (C) 2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ +#ifndef _VDScriptAst_h__ +#define _VDScriptAst_h__ + +#include <iprt/list.h> + +/** + * Position information. + */ +typedef struct VDSRCPOS +{ + /** Line in the source. */ + unsigned iLine; + /** Current start character .*/ + unsigned iChStart; + /** Current end character. */ + unsigned iChEnd; +} VDSRCPOS; +/** Pointer to a source position. */ +typedef struct VDSRCPOS *PVDSRCPOS; + +/** + * AST node classes. + */ +typedef enum VDSCRIPTASTCLASS +{ + /** Invalid. */ + VDSCRIPTASTCLASS_INVALID = 0, + /** Function node. */ + VDSCRIPTASTCLASS_FUNCTION, + /** Function argument. */ + VDSCRIPTASTCLASS_FUNCTIONARG, + /** Identifier node. */ + VDSCRIPTASTCLASS_IDENTIFIER, + /** Declaration node. */ + VDSCRIPTASTCLASS_DECLARATION, + /** Statement node. */ + VDSCRIPTASTCLASS_STATEMENT, + /** Expression node. */ + VDSCRIPTASTCLASS_EXPRESSION, + /** 32bit blowup. */ + VDSCRIPTASTCLASS_32BIT_HACK = 0x7fffffff +} VDSCRIPTASTCLASS; +/** Pointer to an AST node class. */ +typedef VDSCRIPTASTCLASS *PVDSCRIPTASTCLASS; + +/** + * Core AST structure. + */ +typedef struct VDSCRIPTASTCORE +{ + /** The node class, used for verification. */ + VDSCRIPTASTCLASS enmClass; + /** List which might be used. */ + RTLISTNODE ListNode; + /** Position in the source file of this node. */ + VDSRCPOS Pos; +} VDSCRIPTASTCORE; +/** Pointer to an AST core structure. */ +typedef VDSCRIPTASTCORE *PVDSCRIPTASTCORE; + +/** Pointer to an statement node - forward declaration. */ +typedef struct VDSCRIPTASTSTMT *PVDSCRIPTASTSTMT; +/** Pointer to an expression node - forward declaration. */ +typedef struct VDSCRIPTASTEXPR *PVDSCRIPTASTEXPR; + +/** + * AST identifier node. + */ +typedef struct VDSCRIPTASTIDE +{ + /** Core structure. */ + VDSCRIPTASTCORE Core; + /** Number of characters in the identifier, excluding the zero terminator. */ + unsigned cchIde; + /** Identifier, variable size. */ + char aszIde[1]; +} VDSCRIPTASTIDE; +/** Pointer to an identifer node. */ +typedef VDSCRIPTASTIDE *PVDSCRIPTASTIDE; + +/** + * AST declaration node. + */ +typedef struct VDSCRIPTASTDECL +{ + /** Core structure. */ + VDSCRIPTASTCORE Core; + /** @todo */ +} VDSCRIPTASTDECL; +/** Pointer to an declaration node. */ +typedef VDSCRIPTASTDECL *PVDSCRIPTASTDECL; + +/** + * Expression types. + */ +typedef enum VDSCRIPTEXPRTYPE +{ + /** Invalid. */ + VDSCRIPTEXPRTYPE_INVALID = 0, + /** Numerical constant. */ + VDSCRIPTEXPRTYPE_PRIMARY_NUMCONST, + /** String constant. */ + VDSCRIPTEXPRTYPE_PRIMARY_STRINGCONST, + /** Boolean constant. */ + VDSCRIPTEXPRTYPE_PRIMARY_BOOLEAN, + /** Identifier. */ + VDSCRIPTEXPRTYPE_PRIMARY_IDENTIFIER, + /** List of assignment expressions as in a = b = c = ... . */ + VDSCRIPTEXPRTYPE_ASSIGNMENT_LIST, + /** Postfix increment expression. */ + VDSCRIPTEXPRTYPE_POSTFIX_INCREMENT, + /** Postfix decrement expression. */ + VDSCRIPTEXPRTYPE_POSTFIX_DECREMENT, + /** Postfix function call expression. */ + VDSCRIPTEXPRTYPE_POSTFIX_FNCALL, + /** Unary increment expression. */ + VDSCRIPTEXPRTYPE_UNARY_INCREMENT, + /** Unary decrement expression. */ + VDSCRIPTEXPRTYPE_UNARY_DECREMENT, + /** Unary positive sign expression. */ + VDSCRIPTEXPRTYPE_UNARY_POSSIGN, + /** Unary negtive sign expression. */ + VDSCRIPTEXPRTYPE_UNARY_NEGSIGN, + /** Unary invert expression. */ + VDSCRIPTEXPRTYPE_UNARY_INVERT, + /** Unary negate expression. */ + VDSCRIPTEXPRTYPE_UNARY_NEGATE, + /** Multiplicative expression. */ + VDSCRIPTEXPRTYPE_MULTIPLICATION, + /** Division expression. */ + VDSCRIPTEXPRTYPE_DIVISION, + /** Modulus expression. */ + VDSCRIPTEXPRTYPE_MODULUS, + /** Addition expression. */ + VDSCRIPTEXPRTYPE_ADDITION, + /** Subtraction expression. */ + VDSCRIPTEXPRTYPE_SUBTRACTION, + /** Logical shift right. */ + VDSCRIPTEXPRTYPE_LSR, + /** Logical shift left. */ + VDSCRIPTEXPRTYPE_LSL, + /** Lower than expression */ + VDSCRIPTEXPRTYPE_LOWER, + /** Higher than expression */ + VDSCRIPTEXPRTYPE_HIGHER, + /** Lower or equal than expression */ + VDSCRIPTEXPRTYPE_LOWEREQUAL, + /** Higher or equal than expression */ + VDSCRIPTEXPRTYPE_HIGHEREQUAL, + /** Equals expression */ + VDSCRIPTEXPRTYPE_EQUAL, + /** Not equal expression */ + VDSCRIPTEXPRTYPE_NOTEQUAL, + /** Bitwise and expression */ + VDSCRIPTEXPRTYPE_BITWISE_AND, + /** Bitwise xor expression */ + VDSCRIPTEXPRTYPE_BITWISE_XOR, + /** Bitwise or expression */ + VDSCRIPTEXPRTYPE_BITWISE_OR, + /** Logical and expression */ + VDSCRIPTEXPRTYPE_LOGICAL_AND, + /** Logical or expression */ + VDSCRIPTEXPRTYPE_LOGICAL_OR, + /** Assign expression */ + VDSCRIPTEXPRTYPE_ASSIGN, + /** Multiplicative assign expression */ + VDSCRIPTEXPRTYPE_ASSIGN_MULT, + /** Division assign expression */ + VDSCRIPTEXPRTYPE_ASSIGN_DIV, + /** Modulus assign expression */ + VDSCRIPTEXPRTYPE_ASSIGN_MOD, + /** Additive assign expression */ + VDSCRIPTEXPRTYPE_ASSIGN_ADD, + /** Subtractive assign expression */ + VDSCRIPTEXPRTYPE_ASSIGN_SUB, + /** Bitwise left shift assign expression */ + VDSCRIPTEXPRTYPE_ASSIGN_LSL, + /** Bitwise right shift assign expression */ + VDSCRIPTEXPRTYPE_ASSIGN_LSR, + /** Bitwise and assign expression */ + VDSCRIPTEXPRTYPE_ASSIGN_AND, + /** Bitwise xor assign expression */ + VDSCRIPTEXPRTYPE_ASSIGN_XOR, + /** Bitwise or assign expression */ + VDSCRIPTEXPRTYPE_ASSIGN_OR, + /** 32bit hack. */ + VDSCRIPTEXPRTYPE_32BIT_HACK = 0x7fffffff +} VDSCRIPTEXPRTYPE; +/** Pointer to an expression type. */ +typedef VDSCRIPTEXPRTYPE *PVDSCRIPTEXPRTYPE; + +/** + * AST expression node. + */ +typedef struct VDSCRIPTASTEXPR +{ + /** Core structure. */ + VDSCRIPTASTCORE Core; + /** Expression type. */ + VDSCRIPTEXPRTYPE enmType; + /** Expression type dependent data. */ + union + { + /** Numerical constant. */ + uint64_t u64; + /** Primary identifier. */ + PVDSCRIPTASTIDE pIde; + /** String literal */ + const char *pszStr; + /** Boolean constant. */ + bool f; + /** List of expressions - VDSCRIPTASTEXPR. */ + RTLISTANCHOR ListExpr; + /** Pointer to another expression. */ + PVDSCRIPTASTEXPR pExpr; + /** Function call expression. */ + struct + { + /** Other postfix expression used as the identifier for the function. */ + PVDSCRIPTASTEXPR pFnIde; + /** Argument list if existing. */ + RTLISTANCHOR ListArgs; + } FnCall; + /** Binary operation. */ + struct + { + /** Left operator. */ + PVDSCRIPTASTEXPR pLeftExpr; + /** Right operator. */ + PVDSCRIPTASTEXPR pRightExpr; + } BinaryOp; + }; +} VDSCRIPTASTEXPR; + +/** + * AST if node. + */ +typedef struct VDSCRIPTASTIF +{ + /** Conditional expression. */ + PVDSCRIPTASTEXPR pCond; + /** The true branch */ + PVDSCRIPTASTSTMT pTrueStmt; + /** The else branch, can be NULL if no else branch. */ + PVDSCRIPTASTSTMT pElseStmt; +} VDSCRIPTASTIF; +/** Pointer to an expression node. */ +typedef VDSCRIPTASTIF *PVDSCRIPTASTIF; + +/** + * AST switch node. + */ +typedef struct VDSCRIPTASTSWITCH +{ + /** Conditional expression. */ + PVDSCRIPTASTEXPR pCond; + /** The statement to follow. */ + PVDSCRIPTASTSTMT pStmt; +} VDSCRIPTASTSWITCH; +/** Pointer to an expression node. */ +typedef VDSCRIPTASTSWITCH *PVDSCRIPTASTSWITCH; + +/** + * AST while or do ... while node. + */ +typedef struct VDSCRIPTASTWHILE +{ + /** Flag whether this is a do while loop. */ + bool fDoWhile; + /** Conditional expression. */ + PVDSCRIPTASTEXPR pCond; + /** The statement to follow. */ + PVDSCRIPTASTSTMT pStmt; +} VDSCRIPTASTWHILE; +/** Pointer to an expression node. */ +typedef VDSCRIPTASTWHILE *PVDSCRIPTASTWHILE; + +/** + * AST for node. + */ +typedef struct VDSCRIPTASTFOR +{ + /** Initializer expression. */ + PVDSCRIPTASTEXPR pExprStart; + /** The exit condition. */ + PVDSCRIPTASTEXPR pExprCond; + /** The third expression (normally used to increase/decrease loop variable). */ + PVDSCRIPTASTEXPR pExpr3; + /** The for loop body. */ + PVDSCRIPTASTSTMT pStmt; +} VDSCRIPTASTFOR; +/** Pointer to an expression node. */ +typedef VDSCRIPTASTFOR *PVDSCRIPTASTFOR; + +/** + * Statement types. + */ +typedef enum VDSCRIPTSTMTTYPE +{ + /** Invalid. */ + VDSCRIPTSTMTTYPE_INVALID = 0, + /** Compound statement. */ + VDSCRIPTSTMTTYPE_COMPOUND, + /** Expression statement. */ + VDSCRIPTSTMTTYPE_EXPRESSION, + /** if statement. */ + VDSCRIPTSTMTTYPE_IF, + /** switch statement. */ + VDSCRIPTSTMTTYPE_SWITCH, + /** while statement. */ + VDSCRIPTSTMTTYPE_WHILE, + /** for statement. */ + VDSCRIPTSTMTTYPE_FOR, + /** continue statement. */ + VDSCRIPTSTMTTYPE_CONTINUE, + /** break statement. */ + VDSCRIPTSTMTTYPE_BREAK, + /** return statement. */ + VDSCRIPTSTMTTYPE_RETURN, + /** case statement. */ + VDSCRIPTSTMTTYPE_CASE, + /** default statement. */ + VDSCRIPTSTMTTYPE_DEFAULT, + /** 32bit hack. */ + VDSCRIPTSTMTTYPE_32BIT_HACK = 0x7fffffff +} VDSCRIPTSTMTTYPE; +/** Pointer to a statement type. */ +typedef VDSCRIPTSTMTTYPE *PVDSCRIPTSTMTTYPE; + +/** + * AST statement node. + */ +typedef struct VDSCRIPTASTSTMT +{ + /** Core structure. */ + VDSCRIPTASTCORE Core; + /** Statement type */ + VDSCRIPTSTMTTYPE enmStmtType; + /** Statement type dependent data. */ + union + { + /** Compound statement. */ + struct + { + /** List of declarations - VDSCRIPTASTDECL. */ + RTLISTANCHOR ListDecls; + /** List of statements - VDSCRIPTASTSTMT. */ + RTLISTANCHOR ListStmts; + } Compound; + /** case, default statement. */ + struct + { + /** Pointer to the expression. */ + PVDSCRIPTASTEXPR pExpr; + /** Pointer to the statement. */ + PVDSCRIPTASTSTMT pStmt; + } Case; + /** "if" statement. */ + VDSCRIPTASTIF If; + /** "switch" statement. */ + VDSCRIPTASTSWITCH Switch; + /** "while" or "do ... while" loop. */ + VDSCRIPTASTWHILE While; + /** "for" loop. */ + VDSCRIPTASTFOR For; + /** Pointer to another statement. */ + PVDSCRIPTASTSTMT pStmt; + /** Expression statement. */ + PVDSCRIPTASTEXPR pExpr; + }; +} VDSCRIPTASTSTMT; + +/** + * AST node for one function argument. + */ +typedef struct VDSCRIPTASTFNARG +{ + /** Core structure. */ + VDSCRIPTASTCORE Core; + /** Identifier describing the type of the argument. */ + PVDSCRIPTASTIDE pType; + /** The name of the argument. */ + PVDSCRIPTASTIDE pArgIde; +} VDSCRIPTASTFNARG; +/** Pointer to a AST function argument node. */ +typedef VDSCRIPTASTFNARG *PVDSCRIPTASTFNARG; + +/** + * AST node describing a function. + */ +typedef struct VDSCRIPTASTFN +{ + /** Core structure. */ + VDSCRIPTASTCORE Core; + /** Identifier describing the return type. */ + PVDSCRIPTASTIDE pRetType; + /** Name of the function. */ + PVDSCRIPTASTIDE pFnIde; + /** Number of arguments in the list. */ + unsigned cArgs; + /** Argument list - VDSCRIPTASTFNARG. */ + RTLISTANCHOR ListArgs; + /** Compound statement node. */ + PVDSCRIPTASTSTMT pCompoundStmts; +} VDSCRIPTASTFN; +/** Pointer to a function AST node. */ +typedef VDSCRIPTASTFN *PVDSCRIPTASTFN; + +/** + * Free the given AST node and all subsequent nodes pointed to + * by the given node. + * + * @returns nothing. + * @param pAstNode The AST node to free. + */ +DECLHIDDEN(void) vdScriptAstNodeFree(PVDSCRIPTASTCORE pAstNode); + +/** + * Allocate a non variable in size AST node of the given class. + * + * @returns Pointer to the allocated node. + * NULL if out of memory. + * @param enmClass The class of the AST node. + */ +DECLHIDDEN(PVDSCRIPTASTCORE) vdScriptAstNodeAlloc(VDSCRIPTASTCLASS enmClass); + +/** + * Allocate a IDE node which can hold the given number of characters. + * + * @returns Pointer to the allocated node. + * NULL if out of memory. + * @param cchIde Number of characters which can be stored in the node. + */ +DECLHIDDEN(PVDSCRIPTASTIDE) vdScriptAstNodeIdeAlloc(unsigned cchIde); + +#endif /* _VDScriptAst_h__ */ diff --git a/src/VBox/Storage/testcase/VDScriptChecker.cpp b/src/VBox/Storage/testcase/VDScriptChecker.cpp new file mode 100644 index 00000000..42dfd51a --- /dev/null +++ b/src/VBox/Storage/testcase/VDScriptChecker.cpp @@ -0,0 +1,31 @@ +/** $Id: VDScriptChecker.cpp $ */ +/** @file + * + * VBox HDD container test utility - scripting engine, type and context checker. + */ + +/* + * Copyright (C) 2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ +#define LOGGROUP LOGGROUP_DEFAULT +#include <iprt/list.h> +#include <iprt/mem.h> +#include <iprt/assert.h> + +#include <VBox/log.h> + +#include "VDScriptAst.h" +#include "VDScriptInternal.h" + +DECLHIDDEN(int) vdScriptCtxCheck(PVDSCRIPTCTXINT pThis) +{ + return VERR_NOT_IMPLEMENTED; +} diff --git a/src/VBox/Storage/testcase/VDScriptInternal.h b/src/VBox/Storage/testcase/VDScriptInternal.h new file mode 100644 index 00000000..9e1b78a2 --- /dev/null +++ b/src/VBox/Storage/testcase/VDScriptInternal.h @@ -0,0 +1,107 @@ +/** @file + * + * VBox HDD container test utility - scripting engine, internal script structures. + */ + +/* + * Copyright (C) 2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ +#ifndef _VDScriptInternal_h__ +#define _VDScriptInternal_h__ + +#include <iprt/list.h> +#include <iprt/string.h> + +#include "VDScript.h" + +/** + * Script function which can be called. + */ +typedef struct VDSCRIPTFN +{ + /** String space core. */ + RTSTRSPACECORE Core; + /** Flag whether function is defined in the source or was + * registered from the outside. */ + bool fExternal; + /** Flag dependent data. */ + union + { + /** Data for functions defined in the source. */ + struct + { + /** Pointer to the AST defining the function. */ + PVDSCRIPTASTFN pAstFn; + } Internal; + /** Data for external defined functions. */ + struct + { + /** Callback function. */ + PFNVDSCRIPTCALLBACK pfnCallback; + /** Opaque user data. */ + void *pvUser; + } External; + } Type; + /** Return type of the function. */ + VDSCRIPTTYPE enmTypeRetn; + /** Number of arguments the function takes. */ + unsigned cArgs; + /** Variable sized array of argument types. */ + VDSCRIPTTYPE aenmArgTypes[1]; +} VDSCRIPTFN; +/** Pointer to a script function registration structure. */ +typedef VDSCRIPTFN *PVDSCRIPTFN; + +/** Pointer to a tokenize state. */ +typedef struct VDTOKENIZER *PVDTOKENIZER; + +/** + * Script context. + */ +typedef struct VDSCRIPTCTXINT +{ + /** String space of external registered and source defined functions. */ + RTSTRSPACE hStrSpaceFn; + /** List of ASTs for functions - VDSCRIPTASTFN. */ + RTLISTANCHOR ListAst; + /** Pointer to the current tokenizer state. */ + PVDTOKENIZER pTokenizer; +} VDSCRIPTCTXINT; +/** Pointer to a script context. */ +typedef VDSCRIPTCTXINT *PVDSCRIPTCTXINT; + +/** + * Check the context for type correctness. + * + * @returns VBox status code. + * @param pThis The script context. + */ +DECLHIDDEN(int) vdScriptCtxCheck(PVDSCRIPTCTXINT pThis); + +/** + * Interprete a given function AST. The executed functions + * must be type correct, otherwise the behavior is undefined + * (Will assert in debug builds). + * + * @returns VBox status code. + * @param pThis The script context. + * @param pszFn The function name to interprete. + * @param paArgs Arguments to pass to the function. + * @param cArgs Number of arguments. + * @param pRet Where to store the return value on success. + * + * @note: The AST is not modified in any way during the interpretation process. + */ +DECLHIDDEN(int) vdScriptCtxInterprete(PVDSCRIPTCTXINT pThis, const char *pszFn, + PVDSCRIPTARG paArgs, unsigned cArgs, + PVDSCRIPTARG pRet); + +#endif /* _VDScriptInternal_h__ */ diff --git a/src/VBox/Storage/testcase/VDScriptInterp.cpp b/src/VBox/Storage/testcase/VDScriptInterp.cpp new file mode 100644 index 00000000..27f2bd12 --- /dev/null +++ b/src/VBox/Storage/testcase/VDScriptInterp.cpp @@ -0,0 +1,1054 @@ +/** $Id: VDScriptInterp.cpp $ */ +/** @file + * + * VBox HDD container test utility - scripting engine, interpreter. + */ + +/* + * Copyright (C) 2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ +#define LOGGROUP LOGGROUP_DEFAULT +#include <iprt/list.h> +#include <iprt/mem.h> +#include <iprt/assert.h> +#include <iprt/string.h> +#include <iprt/stream.h> + +#include <VBox/log.h> + +#include "VDScriptAst.h" +#include "VDScriptStack.h" +#include "VDScriptInternal.h" + +/** + * Interpreter variable. + */ +typedef struct VDSCRIPTINTERPVAR +{ + /** String space core. */ + RTSTRSPACECORE Core; + /** Value. */ + VDSCRIPTARG Value; +} VDSCRIPTINTERPVAR; +/** Pointer to an interpreter variable. */ +typedef VDSCRIPTINTERPVAR *PVDSCRIPTINTERPVAR; + +/** + * Block scope. + */ +typedef struct VDSCRIPTINTERPSCOPE +{ + /** Pointer to the enclosing scope if available. */ + struct VDSCRIPTINTERPSCOPE *pParent; + /** String space of declared variables in this scope. */ + RTSTRSPACE hStrSpaceVar; +} VDSCRIPTINTERPSCOPE; +/** Pointer to a scope block. */ +typedef VDSCRIPTINTERPSCOPE *PVDSCRIPTINTERPSCOPE; + +/** + * Function call. + */ +typedef struct VDSCRIPTINTERPFNCALL +{ + /** Pointer to the caller of this function. */ + struct VDSCRIPTINTERPFNCALL *pCaller; + /** Root scope of this function. */ + VDSCRIPTINTERPSCOPE ScopeRoot; + /** Current scope in this function. */ + PVDSCRIPTINTERPSCOPE pScopeCurr; +} VDSCRIPTINTERPFNCALL; +/** Pointer to a function call. */ +typedef VDSCRIPTINTERPFNCALL *PVDSCRIPTINTERPFNCALL; + +/** + * Interpreter context. + */ +typedef struct VDSCRIPTINTERPCTX +{ + /** Pointer to the script context. */ + PVDSCRIPTCTXINT pScriptCtx; + /** Current function call entry. */ + PVDSCRIPTINTERPFNCALL pFnCallCurr; + /** Stack of calculated values. */ + VDSCRIPTSTACK StackValues; + /** Evaluation control stack. */ + VDSCRIPTSTACK StackCtrl; +} VDSCRIPTINTERPCTX; +/** Pointer to an interpreter context. */ +typedef VDSCRIPTINTERPCTX *PVDSCRIPTINTERPCTX; + +/** + * Interpreter control type. + */ +typedef enum VDSCRIPTINTERPCTRLTYPE +{ + VDSCRIPTINTERPCTRLTYPE_INVALID = 0, + /** Function call to evaluate now, all values are computed + * and are stored on the value stack. + */ + VDSCRIPTINTERPCTRLTYPE_FN_CALL, + /** Cleanup the function call, deleting the scope and restoring the previous one. */ + VDSCRIPTINTERPCTRLTYPE_FN_CALL_CLEANUP, + /** If statement to evaluate now, the guard is on the stack. */ + VDSCRIPTINTERPCTRLTYPE_IF, + /** While or for statement. */ + VDSCRIPTINTERPCTRLTYPE_LOOP, + /** switch statement. */ + VDSCRIPTINTERPCTRLTYPE_SWITCH, + /** Compound statement. */ + VDSCRIPTINTERPCTRLTYPE_COMPOUND, + /** 32bit blowup. */ + VDSCRIPTINTERPCTRLTYPE_32BIT_HACK = 0x7fffffff +} VDSCRIPTINTERPCTRLTYPE; +/** Pointer to a control type. */ +typedef VDSCRIPTINTERPCTRLTYPE *PVDSCRIPTINTERPCTRLTYPE; + +/** + * Interpreter stack control entry. + */ +typedef struct VDSCRIPTINTERPCTRL +{ + /** Flag whether this entry points to an AST node to evaluate. */ + bool fEvalAst; + /** Flag dependent data. */ + union + { + /** Pointer to the AST node to interprete. */ + PVDSCRIPTASTCORE pAstNode; + /** Interpreter control structure. */ + struct + { + /** Type of control. */ + VDSCRIPTINTERPCTRLTYPE enmCtrlType; + /** Function call data. */ + struct + { + /** Function to call. */ + PVDSCRIPTFN pFn; + } FnCall; + /** Compound statement. */ + struct + { + /** The compound statement node. */ + PVDSCRIPTASTSTMT pStmtCompound; + /** Current statement evaluated. */ + PVDSCRIPTASTSTMT pStmtCurr; + } Compound; + /** Pointer to an AST node. */ + PVDSCRIPTASTCORE pAstNode; + } Ctrl; + }; +} VDSCRIPTINTERPCTRL; +/** Pointer to an exec stack control entry. */ +typedef VDSCRIPTINTERPCTRL *PVDSCRIPTINTERPCTRL; + +/** + * Record an error while interpreting. + * + * @returns VBox status code passed. + * @param pThis The interpreter context. + * @param rc The status code to record. + * @param RT_SRC_POS Position in the source code. + * @param pszFmt Format string. + */ +static int vdScriptInterpreterError(PVDSCRIPTINTERPCTX pThis, int rc, RT_SRC_POS_DECL, const char *pszFmt, ...) +{ + RTPrintf(pszFmt); + return rc; +} + +/** + * Pops the topmost value from the value stack. + * + * @returns nothing. + * @param pThis The interpreter context. + * @param pVal Where to store the value. + */ +DECLINLINE(void) vdScriptInterpreterPopValue(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTARG pVal) +{ + PVDSCRIPTARG pValStack = (PVDSCRIPTARG)vdScriptStackGetUsed(&pThis->StackValues); + if (!pValStack) + { + RT_ZERO(*pVal); + AssertPtrReturnVoid(pValStack); + } + + *pVal = *pValStack; + vdScriptStackPop(&pThis->StackValues); +} + +/** + * Pushes a given value onto the value stack. + */ +DECLINLINE(int) vdScriptInterpreterPushValue(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTARG pVal) +{ + PVDSCRIPTARG pValStack = (PVDSCRIPTARG)vdScriptStackGetUnused(&pThis->StackValues); + if (!pValStack) + return vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory pushing a value on the value stack"); + + *pValStack = *pVal; + vdScriptStackPush(&pThis->StackValues); + return VINF_SUCCESS; +} + +/** + * Pushes an AST node onto the control stack. + * + * @returns VBox status code. + * @param pThis The interpreter context. + * @param enmCtrlType The control entry type. + */ +DECLINLINE(int) vdScriptInterpreterPushAstEntry(PVDSCRIPTINTERPCTX pThis, + PVDSCRIPTASTCORE pAstNode) +{ + PVDSCRIPTINTERPCTRL pCtrl = NULL; + + pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUnused(&pThis->StackCtrl); + + if (pCtrl) + { + pCtrl->fEvalAst = true; + pCtrl->pAstNode = pAstNode; + vdScriptStackPush(&pThis->StackCtrl); + return VINF_SUCCESS; + } + + return vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory adding an entry on the control stack"); +} + +/** + * Pushes a control entry without additional data onto the stack. + * + * @returns VBox status code. + * @param pThis The interpreter context. + * @param enmCtrlType The control entry type. + */ +DECLINLINE(int) vdScriptInterpreterPushNonDataCtrlEntry(PVDSCRIPTINTERPCTX pThis, + VDSCRIPTINTERPCTRLTYPE enmCtrlType) +{ + PVDSCRIPTINTERPCTRL pCtrl = NULL; + + pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUnused(&pThis->StackCtrl); + if (pCtrl) + { + pCtrl->fEvalAst = false; + pCtrl->Ctrl.enmCtrlType = enmCtrlType; + vdScriptStackPush(&pThis->StackCtrl); + return VINF_SUCCESS; + } + + return vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory adding an entry on the control stack"); +} + +/** + * Pushes a compound statement control entry onto the stack. + * + * @returns VBox status code. + * @param pThis The interpreter context. + * @param pStmtFirst The first statement of the compound statement + */ +DECLINLINE(int) vdScriptInterpreterPushCompoundCtrlEntry(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTASTSTMT pStmt) +{ + PVDSCRIPTINTERPCTRL pCtrl = NULL; + + pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUnused(&pThis->StackCtrl); + if (pCtrl) + { + pCtrl->fEvalAst = false; + pCtrl->Ctrl.enmCtrlType = VDSCRIPTINTERPCTRLTYPE_COMPOUND; + pCtrl->Ctrl.Compound.pStmtCompound = pStmt; + pCtrl->Ctrl.Compound.pStmtCurr = RTListGetFirst(&pStmt->Compound.ListStmts, VDSCRIPTASTSTMT, Core.ListNode); + vdScriptStackPush(&pThis->StackCtrl); + return VINF_SUCCESS; + } + + return vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory adding an entry on the control stack"); +} + +/** + * Pushes a while statement control entry onto the stack. + * + * @returns VBox status code. + * @param pThis The interpreter context. + * @param pStmt The while statement. + */ +DECLINLINE(int) vdScriptInterpreterPushWhileCtrlEntry(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTASTSTMT pStmt) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTINTERPCTRL pCtrl = NULL; + + pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUnused(&pThis->StackCtrl); + if (pCtrl) + { + pCtrl->fEvalAst = false; + pCtrl->Ctrl.enmCtrlType = VDSCRIPTINTERPCTRLTYPE_LOOP; + pCtrl->Ctrl.pAstNode = &pStmt->Core; + vdScriptStackPush(&pThis->StackCtrl); + + rc = vdScriptInterpreterPushAstEntry(pThis, &pStmt->While.pCond->Core); + if ( RT_SUCCESS(rc) + && pStmt->While.fDoWhile) + { + /* Push the statement to execute for do ... while loops because they run at least once. */ + rc = vdScriptInterpreterPushAstEntry(pThis, &pStmt->While.pStmt->Core); + if (RT_FAILURE(rc)) + { + /* Cleanup while control statement and AST node. */ + vdScriptStackPop(&pThis->StackCtrl); + vdScriptStackPop(&pThis->StackCtrl); + } + } + else if (RT_FAILURE(rc)) + vdScriptStackPop(&pThis->StackCtrl); /* Cleanup the while control statement. */ + } + else + rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory adding an entry on the control stack"); + + return rc; +} + +/** + * Pushes an if statement control entry onto the stack. + * + * @returns VBox status code. + * @param pThis The interpreter context. + * @param pStmt The if statement. + */ +static int vdScriptInterpreterPushIfCtrlEntry(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTASTSTMT pStmt) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTINTERPCTRL pCtrl = NULL; + + pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUnused(&pThis->StackCtrl); + if (pCtrl) + { + pCtrl->fEvalAst = false; + pCtrl->Ctrl.enmCtrlType = VDSCRIPTINTERPCTRLTYPE_IF; + pCtrl->Ctrl.pAstNode = &pStmt->Core; + vdScriptStackPush(&pThis->StackCtrl); + + rc = vdScriptInterpreterPushAstEntry(pThis, &pStmt->If.pCond->Core); + } + else + rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory adding an entry on the control stack"); + + return rc; +} + +/** + * Pushes a for statement control entry onto the stack. + * + * @returns VBox status code. + * @param pThis The interpreter context. + * @param pStmt The while statement. + */ +DECLINLINE(int) vdScriptInterpreterPushForCtrlEntry(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTASTSTMT pStmt) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTINTERPCTRL pCtrl = NULL; + + pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUnused(&pThis->StackCtrl); + if (pCtrl) + { + pCtrl->fEvalAst = false; + pCtrl->Ctrl.enmCtrlType = VDSCRIPTINTERPCTRLTYPE_LOOP; + pCtrl->Ctrl.pAstNode = &pStmt->Core; + vdScriptStackPush(&pThis->StackCtrl); + + /* Push the conditional first and the the initializer .*/ + rc = vdScriptInterpreterPushAstEntry(pThis, &pStmt->For.pExprCond->Core); + if (RT_SUCCESS(rc)) + { + rc = vdScriptInterpreterPushAstEntry(pThis, &pStmt->For.pExprStart->Core); + if (RT_FAILURE(rc)) + vdScriptStackPop(&pThis->StackCtrl); + } + } + else + rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory adding an entry on the control stack"); + + return rc; +} + +/** + * Destroy variable string space callback. + */ +static DECLCALLBACK(int) vdScriptInterpreterVarSpaceDestroy(PRTSTRSPACECORE pStr, void *pvUser) +{ + RTMemFree(pStr); + return VINF_SUCCESS; +} + +/** + * Setsup a new scope in the current function call. + * + * @returns VBox status code. + * @param pThis The interpreter context. + */ +static int vdScriptInterpreterScopeCreate(PVDSCRIPTINTERPCTX pThis) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTINTERPSCOPE pScope = (PVDSCRIPTINTERPSCOPE)RTMemAllocZ(sizeof(VDSCRIPTINTERPSCOPE)); + if (pScope) + { + pScope->pParent = pThis->pFnCallCurr->pScopeCurr; + pScope->hStrSpaceVar = NULL; + pThis->pFnCallCurr->pScopeCurr = pScope; + } + else + rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory allocating new scope"); + + return rc; +} + +/** + * Destroys the current scope. + * + * @returns nothing. + * @param pThis The interpreter context. + */ +static void vdScriptInterpreterScopeDestroyCurr(PVDSCRIPTINTERPCTX pThis) +{ + AssertMsgReturnVoid(pThis->pFnCallCurr->pScopeCurr != &pThis->pFnCallCurr->ScopeRoot, + ("Current scope is root scope of function call\n")); + + PVDSCRIPTINTERPSCOPE pScope = pThis->pFnCallCurr->pScopeCurr; + pThis->pFnCallCurr->pScopeCurr = pScope->pParent; + RTStrSpaceDestroy(&pScope->hStrSpaceVar, vdScriptInterpreterVarSpaceDestroy, NULL); + RTMemFree(pScope); +} + +/** + * Get the content of the given variable identifier from the current or parent scope. + */ +static PVDSCRIPTINTERPVAR vdScriptInterpreterGetVar(PVDSCRIPTINTERPCTX pThis, const char *pszVar) +{ + PVDSCRIPTINTERPSCOPE pScopeCurr = pThis->pFnCallCurr->pScopeCurr; + PVDSCRIPTINTERPVAR pVar = NULL; + + while ( !pVar + && pScopeCurr) + { + pVar = (PVDSCRIPTINTERPVAR)RTStrSpaceGet(&pScopeCurr->hStrSpaceVar, pszVar); + if (pVar) + break; + pScopeCurr = pScopeCurr->pParent; + } + + + return pVar; +} + +/** + * Evaluate an expression. + * + * @returns VBox status code. + * @param pThis The interpreter context. + * @param pExpr The expression to evaluate. + */ +static int vdScriptInterpreterEvaluateExpression(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTASTEXPR pExpr) +{ + int rc = VINF_SUCCESS; + + switch (pExpr->enmType) + { + case VDSCRIPTEXPRTYPE_PRIMARY_NUMCONST: + { + /* Push the numerical constant on the value stack. */ + VDSCRIPTARG NumConst; + NumConst.enmType = VDSCRIPTTYPE_UINT64; + NumConst.u64 = pExpr->u64; + rc = vdScriptInterpreterPushValue(pThis, &NumConst); + break; + } + case VDSCRIPTEXPRTYPE_PRIMARY_STRINGCONST: + { + /* Push the string literal on the value stack. */ + VDSCRIPTARG StringConst; + StringConst.enmType = VDSCRIPTTYPE_STRING; + StringConst.psz = pExpr->pszStr; + rc = vdScriptInterpreterPushValue(pThis, &StringConst); + break; + } + case VDSCRIPTEXPRTYPE_PRIMARY_BOOLEAN: + { + VDSCRIPTARG BoolConst; + BoolConst.enmType = VDSCRIPTTYPE_BOOL; + BoolConst.f = pExpr->f; + rc = vdScriptInterpreterPushValue(pThis, &BoolConst); + break; + } + case VDSCRIPTEXPRTYPE_PRIMARY_IDENTIFIER: + { + /* Look it up and push the value onto the value stack. */ + PVDSCRIPTINTERPVAR pVar = vdScriptInterpreterGetVar(pThis, pExpr->pIde->aszIde); + + AssertPtrReturn(pVar, VERR_IPE_UNINITIALIZED_STATUS); + rc = vdScriptInterpreterPushValue(pThis, &pVar->Value); + break; + } + case VDSCRIPTEXPRTYPE_POSTFIX_INCREMENT: + case VDSCRIPTEXPRTYPE_POSTFIX_DECREMENT: + AssertMsgFailed(("TODO\n")); + case VDSCRIPTEXPRTYPE_POSTFIX_FNCALL: + { + PVDSCRIPTFN pFn = (PVDSCRIPTFN)RTStrSpaceGet(&pThis->pScriptCtx->hStrSpaceFn, pExpr->FnCall.pFnIde->pIde->aszIde); + if (pFn) + { + /* Push a function call control entry on the stack. */ + PVDSCRIPTINTERPCTRL pCtrlFn = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUnused(&pThis->StackCtrl); + if (pCtrlFn) + { + pCtrlFn->fEvalAst = false; + pCtrlFn->Ctrl.enmCtrlType = VDSCRIPTINTERPCTRLTYPE_FN_CALL; + pCtrlFn->Ctrl.FnCall.pFn = pFn; + vdScriptStackPush(&pThis->StackCtrl); + + /* Push parameter expressions on the stack. */ + PVDSCRIPTASTEXPR pArg = RTListGetFirst(&pExpr->FnCall.ListArgs, VDSCRIPTASTEXPR, Core.ListNode); + while (pArg) + { + rc = vdScriptInterpreterPushAstEntry(pThis, &pArg->Core); + if (RT_FAILURE(rc)) + break; + pArg = RTListGetNext(&pExpr->FnCall.ListArgs, pArg, VDSCRIPTASTEXPR, Core.ListNode); + } + } + } + else + AssertMsgFailed(("Invalid program given, unknown function: %s\n", pExpr->FnCall.pFnIde->pIde->aszIde)); + break; + } + case VDSCRIPTEXPRTYPE_UNARY_INCREMENT: + case VDSCRIPTEXPRTYPE_UNARY_DECREMENT: + case VDSCRIPTEXPRTYPE_UNARY_POSSIGN: + case VDSCRIPTEXPRTYPE_UNARY_NEGSIGN: + case VDSCRIPTEXPRTYPE_UNARY_INVERT: + case VDSCRIPTEXPRTYPE_UNARY_NEGATE: + case VDSCRIPTEXPRTYPE_MULTIPLICATION: + case VDSCRIPTEXPRTYPE_DIVISION: + case VDSCRIPTEXPRTYPE_MODULUS: + case VDSCRIPTEXPRTYPE_ADDITION: + case VDSCRIPTEXPRTYPE_SUBTRACTION: + case VDSCRIPTEXPRTYPE_LSR: + case VDSCRIPTEXPRTYPE_LSL: + case VDSCRIPTEXPRTYPE_LOWER: + case VDSCRIPTEXPRTYPE_HIGHER: + case VDSCRIPTEXPRTYPE_LOWEREQUAL: + case VDSCRIPTEXPRTYPE_HIGHEREQUAL: + case VDSCRIPTEXPRTYPE_EQUAL: + case VDSCRIPTEXPRTYPE_NOTEQUAL: + case VDSCRIPTEXPRTYPE_BITWISE_AND: + case VDSCRIPTEXPRTYPE_BITWISE_XOR: + case VDSCRIPTEXPRTYPE_BITWISE_OR: + case VDSCRIPTEXPRTYPE_LOGICAL_AND: + case VDSCRIPTEXPRTYPE_LOGICAL_OR: + case VDSCRIPTEXPRTYPE_ASSIGN: + case VDSCRIPTEXPRTYPE_ASSIGN_MULT: + case VDSCRIPTEXPRTYPE_ASSIGN_DIV: + case VDSCRIPTEXPRTYPE_ASSIGN_MOD: + case VDSCRIPTEXPRTYPE_ASSIGN_ADD: + case VDSCRIPTEXPRTYPE_ASSIGN_SUB: + case VDSCRIPTEXPRTYPE_ASSIGN_LSL: + case VDSCRIPTEXPRTYPE_ASSIGN_LSR: + case VDSCRIPTEXPRTYPE_ASSIGN_AND: + case VDSCRIPTEXPRTYPE_ASSIGN_XOR: + case VDSCRIPTEXPRTYPE_ASSIGN_OR: + case VDSCRIPTEXPRTYPE_ASSIGNMENT_LIST: + AssertMsgFailed(("TODO\n")); + default: + AssertMsgFailed(("Invalid expression type: %d\n", pExpr->enmType)); + } + return rc; +} + +/** + * Evaluate a statement. + * + * @returns VBox status code. + * @param pThis The interpreter context. + * @param pStmt The statement to evaluate. + */ +static int vdScriptInterpreterEvaluateStatement(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTASTSTMT pStmt) +{ + int rc = VINF_SUCCESS; + + switch (pStmt->enmStmtType) + { + case VDSCRIPTSTMTTYPE_COMPOUND: + { + /* Setup new scope. */ + rc = vdScriptInterpreterScopeCreate(pThis); + if (RT_SUCCESS(rc)) + { + /** @todo: Declarations */ + rc = vdScriptInterpreterPushCompoundCtrlEntry(pThis, pStmt); + } + break; + } + case VDSCRIPTSTMTTYPE_EXPRESSION: + { + rc = vdScriptInterpreterPushAstEntry(pThis, &pStmt->pExpr->Core); + break; + } + case VDSCRIPTSTMTTYPE_IF: + { + rc = vdScriptInterpreterPushIfCtrlEntry(pThis, pStmt); + break; + } + case VDSCRIPTSTMTTYPE_SWITCH: + AssertMsgFailed(("TODO\n")); + break; + case VDSCRIPTSTMTTYPE_WHILE: + { + rc = vdScriptInterpreterPushWhileCtrlEntry(pThis, pStmt); + break; + } + case VDSCRIPTSTMTTYPE_RETURN: + { + /* Walk up the control stack until we reach a function cleanup entry. */ + PVDSCRIPTINTERPCTRL pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl); + while ( pCtrl + && ( pCtrl->fEvalAst + || pCtrl->Ctrl.enmCtrlType != VDSCRIPTINTERPCTRLTYPE_FN_CALL_CLEANUP)) + { + /* Cleanup up any compound statement scope. */ + if ( !pCtrl->fEvalAst + && pCtrl->Ctrl.enmCtrlType == VDSCRIPTINTERPCTRLTYPE_COMPOUND) + vdScriptInterpreterScopeDestroyCurr(pThis); + + vdScriptStackPop(&pThis->StackCtrl); + pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl); + } + AssertMsg(VALID_PTR(pCtrl), ("Incorrect program, return outside of function\n")); + break; + } + case VDSCRIPTSTMTTYPE_FOR: + { + rc = vdScriptInterpreterPushForCtrlEntry(pThis, pStmt); + break; + } + case VDSCRIPTSTMTTYPE_CONTINUE: + { + /* Remove everything up to a loop control entry. */ + PVDSCRIPTINTERPCTRL pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl); + while ( pCtrl + && ( pCtrl->fEvalAst + || pCtrl->Ctrl.enmCtrlType != VDSCRIPTINTERPCTRLTYPE_LOOP)) + { + /* Cleanup up any compound statement scope. */ + if ( !pCtrl->fEvalAst + && pCtrl->Ctrl.enmCtrlType == VDSCRIPTINTERPCTRLTYPE_COMPOUND) + vdScriptInterpreterScopeDestroyCurr(pThis); + + vdScriptStackPop(&pThis->StackCtrl); + pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl); + } + AssertMsg(VALID_PTR(pCtrl), ("Incorrect program, continue outside of loop\n")); + + /* Put the conditionals for while and for loops onto the control stack again. */ + PVDSCRIPTASTSTMT pLoopStmt = (PVDSCRIPTASTSTMT)pCtrl->Ctrl.pAstNode; + + AssertMsg( pLoopStmt->enmStmtType == VDSCRIPTSTMTTYPE_WHILE + || pLoopStmt->enmStmtType == VDSCRIPTSTMTTYPE_FOR, + ("Invalid statement type, must be for or while loop\n")); + + if (pLoopStmt->enmStmtType == VDSCRIPTSTMTTYPE_FOR) + rc = vdScriptInterpreterPushAstEntry(pThis, &pLoopStmt->For.pExprCond->Core); + else if (!pLoopStmt->While.fDoWhile) + rc = vdScriptInterpreterPushAstEntry(pThis, &pLoopStmt->While.pCond->Core); + break; + } + case VDSCRIPTSTMTTYPE_BREAK: + { + /* Remove everything including the loop control statement. */ + PVDSCRIPTINTERPCTRL pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl); + while ( pCtrl + && ( pCtrl->fEvalAst + || pCtrl->Ctrl.enmCtrlType != VDSCRIPTINTERPCTRLTYPE_LOOP)) + { + /* Cleanup up any compound statement scope. */ + if ( !pCtrl->fEvalAst + && pCtrl->Ctrl.enmCtrlType == VDSCRIPTINTERPCTRLTYPE_COMPOUND) + vdScriptInterpreterScopeDestroyCurr(pThis); + + vdScriptStackPop(&pThis->StackCtrl); + pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl); + } + AssertMsg(VALID_PTR(pCtrl), ("Incorrect program, break outside of loop\n")); + vdScriptStackPop(&pThis->StackCtrl); /* Remove loop control statement. */ + break; + } + case VDSCRIPTSTMTTYPE_CASE: + case VDSCRIPTSTMTTYPE_DEFAULT: + AssertMsgFailed(("TODO\n")); + break; + default: + AssertMsgFailed(("Invalid statement type: %d\n", pStmt->enmStmtType)); + } + + return rc; +} + +/** + * Evaluates the given AST node. + * + * @returns VBox statuse code. + * @param pThis The interpreter context. + * @param pAstNode The AST node to interpret. + */ +static int vdScriptInterpreterEvaluateAst(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTASTCORE pAstNode) +{ + int rc = VERR_NOT_IMPLEMENTED; + + switch (pAstNode->enmClass) + { + case VDSCRIPTASTCLASS_DECLARATION: + { + AssertMsgFailed(("TODO\n")); + break; + } + case VDSCRIPTASTCLASS_STATEMENT: + { + rc = vdScriptInterpreterEvaluateStatement(pThis, (PVDSCRIPTASTSTMT)pAstNode); + break; + } + case VDSCRIPTASTCLASS_EXPRESSION: + { + rc = vdScriptInterpreterEvaluateExpression(pThis, (PVDSCRIPTASTEXPR)pAstNode); + break; + } + /* These should never ever appear here. */ + case VDSCRIPTASTCLASS_IDENTIFIER: + case VDSCRIPTASTCLASS_FUNCTION: + case VDSCRIPTASTCLASS_FUNCTIONARG: + case VDSCRIPTASTCLASS_INVALID: + default: + AssertMsgFailed(("Invalid AST node class: %d\n", pAstNode->enmClass)); + } + + return rc; +} + +/** + * Evaluate a function call. + * + * @returns VBox status code. + * @param pThis The interpreter context. + * @param pFn The function execute. + */ +static int vdScriptInterpreterFnCall(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTFN pFn) +{ + int rc = VINF_SUCCESS; + + if (!pFn->fExternal) + { + PVDSCRIPTASTFN pAstFn = pFn->Type.Internal.pAstFn; + + /* Add function call cleanup marker on the stack first. */ + rc = vdScriptInterpreterPushNonDataCtrlEntry(pThis, VDSCRIPTINTERPCTRLTYPE_FN_CALL_CLEANUP); + if (RT_SUCCESS(rc)) + { + /* Create function call frame and set it up. */ + PVDSCRIPTINTERPFNCALL pFnCall = (PVDSCRIPTINTERPFNCALL)RTMemAllocZ(sizeof(VDSCRIPTINTERPFNCALL)); + if (pFnCall) + { + pFnCall->pCaller = pThis->pFnCallCurr; + pFnCall->ScopeRoot.pParent = NULL; + pFnCall->ScopeRoot.hStrSpaceVar = NULL; + pFnCall->pScopeCurr = &pFnCall->ScopeRoot; + + /* Add the variables, remember order. The first variable in the argument has the value at the top of the value stack. */ + PVDSCRIPTASTFNARG pArg = RTListGetFirst(&pAstFn->ListArgs, VDSCRIPTASTFNARG, Core.ListNode); + for (unsigned i = 0; i < pAstFn->cArgs; i++) + { + PVDSCRIPTINTERPVAR pVar = (PVDSCRIPTINTERPVAR)RTMemAllocZ(sizeof(VDSCRIPTINTERPVAR)); + if (pVar) + { + pVar->Core.pszString = pArg->pArgIde->aszIde; + pVar->Core.cchString = pArg->pArgIde->cchIde; + vdScriptInterpreterPopValue(pThis, &pVar->Value); + bool fInserted = RTStrSpaceInsert(&pFnCall->ScopeRoot.hStrSpaceVar, &pVar->Core); + Assert(fInserted); + } + else + { + rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory creating a variable"); + break; + } + pArg = RTListGetNext(&pAstFn->ListArgs, pArg, VDSCRIPTASTFNARG, Core.ListNode); + } + + if (RT_SUCCESS(rc)) + { + /* + * Push compount statement on the control stack and make the newly created + * call frame the current one. + */ + rc = vdScriptInterpreterPushAstEntry(pThis, &pAstFn->pCompoundStmts->Core); + if (RT_SUCCESS(rc)) + pThis->pFnCallCurr = pFnCall; + } + + if (RT_FAILURE(rc)) + { + RTStrSpaceDestroy(&pFnCall->ScopeRoot.hStrSpaceVar, vdScriptInterpreterVarSpaceDestroy, NULL); + RTMemFree(pFnCall); + } + } + else + rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory creating a call frame"); + } + } + else + { + /* External function call, build the argument list. */ + if (pFn->cArgs) + { + PVDSCRIPTARG paArgs = (PVDSCRIPTARG)RTMemAllocZ(pFn->cArgs * sizeof(VDSCRIPTARG)); + if (paArgs) + { + for (unsigned i = 0; i < pFn->cArgs; i++) + vdScriptInterpreterPopValue(pThis, &paArgs[i]); + + rc = pFn->Type.External.pfnCallback(paArgs, pFn->Type.External.pvUser); + RTMemFree(paArgs); + } + else + rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, + "Out of memory creating argument array for external function call"); + } + else + rc = pFn->Type.External.pfnCallback(NULL, pFn->Type.External.pvUser); + } + + return rc; +} + +/** + * Evaluate interpreter control statement. + * + * @returns VBox status code. + * @param pThis The interpreter context. + * @param pCtrl The control entry to evaluate. + */ +static int vdScriptInterpreterEvaluateCtrlEntry(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTINTERPCTRL pCtrl) +{ + int rc = VINF_SUCCESS; + + Assert(!pCtrl->fEvalAst); + switch (pCtrl->Ctrl.enmCtrlType) + { + case VDSCRIPTINTERPCTRLTYPE_FN_CALL: + { + PVDSCRIPTFN pFn = pCtrl->Ctrl.FnCall.pFn; + + vdScriptStackPop(&pThis->StackCtrl); + rc = vdScriptInterpreterFnCall(pThis, pFn); + break; + } + case VDSCRIPTINTERPCTRLTYPE_FN_CALL_CLEANUP: + { + vdScriptStackPop(&pThis->StackCtrl); + + /* Delete function call entry. */ + AssertPtr(pThis->pFnCallCurr); + PVDSCRIPTINTERPFNCALL pFnCallFree = pThis->pFnCallCurr; + + pThis->pFnCallCurr = pFnCallFree->pCaller; + Assert(pFnCallFree->pScopeCurr == &pFnCallFree->ScopeRoot); + RTStrSpaceDestroy(&pFnCallFree->ScopeRoot.hStrSpaceVar, vdScriptInterpreterVarSpaceDestroy, NULL); + RTMemFree(pFnCallFree); + break; + } + case VDSCRIPTINTERPCTRLTYPE_COMPOUND: + { + if (!pCtrl->Ctrl.Compound.pStmtCurr) + { + /* Evaluated last statement, cleanup and remove the control statement from the stack. */ + vdScriptInterpreterScopeDestroyCurr(pThis); + vdScriptStackPop(&pThis->StackCtrl); + } + else + { + /* Push the current statement onto the control stack and move on. */ + rc = vdScriptInterpreterPushAstEntry(pThis, &pCtrl->Ctrl.Compound.pStmtCurr->Core); + if (RT_SUCCESS(rc)) + { + pCtrl->Ctrl.Compound.pStmtCurr = RTListGetNext(&pCtrl->Ctrl.Compound.pStmtCompound->Compound.ListStmts, + pCtrl->Ctrl.Compound.pStmtCurr, VDSCRIPTASTSTMT, Core.ListNode); + } + } + break; + } + case VDSCRIPTINTERPCTRLTYPE_LOOP: + { + PVDSCRIPTASTSTMT pLoopStmt = (PVDSCRIPTASTSTMT)pCtrl->Ctrl.pAstNode; + + /* Check whether the condition passed. */ + VDSCRIPTARG Cond; + vdScriptInterpreterPopValue(pThis, &Cond); + AssertMsg(Cond.enmType == VDSCRIPTTYPE_BOOL, + ("Value on stack is not of boolean type\n")); + + if (Cond.f) + { + /* Execute the loop another round. */ + if (pLoopStmt->enmStmtType == VDSCRIPTSTMTTYPE_WHILE) + { + rc = vdScriptInterpreterPushAstEntry(pThis, &pLoopStmt->While.pCond->Core); + if (RT_SUCCESS(rc)) + { + rc = vdScriptInterpreterPushAstEntry(pThis, &pLoopStmt->While.pStmt->Core); + if (RT_FAILURE(rc)) + vdScriptStackPop(&pThis->StackCtrl); + } + } + else + { + AssertMsg(pLoopStmt->enmStmtType == VDSCRIPTSTMTTYPE_FOR, + ("Not a for statement\n")); + + rc = vdScriptInterpreterPushAstEntry(pThis, &pLoopStmt->For.pExprCond->Core); + if (RT_SUCCESS(rc)) + { + rc = vdScriptInterpreterPushAstEntry(pThis, &pLoopStmt->For.pExpr3->Core); + if (RT_SUCCESS(rc)) + { + rc = vdScriptInterpreterPushAstEntry(pThis, &pLoopStmt->For.pStmt->Core); + if (RT_FAILURE(rc)) + vdScriptStackPop(&pThis->StackCtrl); + } + + if (RT_FAILURE(rc)) + vdScriptStackPop(&pThis->StackCtrl); + } + } + } + else + vdScriptStackPop(&pThis->StackCtrl); /* Remove loop control statement. */ + break; + } + case VDSCRIPTINTERPCTRLTYPE_IF: + { + PVDSCRIPTASTSTMT pIfStmt = (PVDSCRIPTASTSTMT)pCtrl->Ctrl.pAstNode; + + vdScriptStackPop(&pThis->StackCtrl); /* Remove if control statement. */ + + /* Check whether the condition passed. */ + VDSCRIPTARG Cond; + vdScriptInterpreterPopValue(pThis, &Cond); + AssertMsg(Cond.enmType == VDSCRIPTTYPE_BOOL, + ("Value on stack is not of boolean type\n")); + + if (Cond.f) + { + /* Execute the true branch. */ + rc = vdScriptInterpreterPushAstEntry(pThis, &pIfStmt->If.pTrueStmt->Core); + } + else if (pIfStmt->If.pElseStmt) + rc = vdScriptInterpreterPushAstEntry(pThis, &pIfStmt->If.pElseStmt->Core); + + break; + } + default: + AssertMsgFailed(("Invalid evaluation control type on the stack: %d\n", + pCtrl->Ctrl.enmCtrlType)); + } + + return rc; +} + +/** + * The interpreter evaluation core loop. + * + * @returns VBox status code. + * @param pThis The interpreter context. + */ +static int vdScriptInterpreterEvaluate(PVDSCRIPTINTERPCTX pThis) +{ + int rc = VINF_SUCCESS; + PVDSCRIPTINTERPCTRL pCtrl = NULL; + + pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl); + while (pCtrl) + { + if (pCtrl->fEvalAst) + { + PVDSCRIPTASTCORE pAstNode = pCtrl->pAstNode; + vdScriptStackPop(&pThis->StackCtrl); + + rc = vdScriptInterpreterEvaluateAst(pThis, pAstNode); + } + else + rc = vdScriptInterpreterEvaluateCtrlEntry(pThis, pCtrl); + + pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl); + } + + return rc; +} + +DECLHIDDEN(int) vdScriptCtxInterprete(PVDSCRIPTCTXINT pThis, const char *pszFn, + PVDSCRIPTARG paArgs, unsigned cArgs, + PVDSCRIPTARG pRet) +{ + int rc = VINF_SUCCESS; + VDSCRIPTINTERPCTX InterpCtx; + PVDSCRIPTFN pFn = NULL; + + AssertPtrReturn(pThis, VERR_INVALID_POINTER); + AssertPtrReturn(pszFn, VERR_INVALID_POINTER); + AssertReturn( (!cArgs && !paArgs) + || (cArgs && paArgs), VERR_INVALID_PARAMETER); + + InterpCtx.pScriptCtx = pThis; + InterpCtx.pFnCallCurr = NULL; + vdScriptStackInit(&InterpCtx.StackValues, sizeof(VDSCRIPTARG)); + vdScriptStackInit(&InterpCtx.StackCtrl, sizeof(VDSCRIPTINTERPCTRL)); + + pFn = (PVDSCRIPTFN)RTStrSpaceGet(&pThis->hStrSpaceFn, pszFn); + if (pFn) + { + if (cArgs == pFn->cArgs) + { + /* Push the arguments onto the stack. */ + /** @todo: Check expected and given argument types. */ + for (unsigned i = 0; i < cArgs; i++) + { + PVDSCRIPTARG pArg = (PVDSCRIPTARG)vdScriptStackGetUnused(&InterpCtx.StackValues); + *pArg = paArgs[i]; + vdScriptStackPush(&InterpCtx.StackValues); + } + + if (RT_SUCCESS(rc)) + { + /* Setup function call frame and parameters. */ + rc = vdScriptInterpreterFnCall(&InterpCtx, pFn); + if (RT_SUCCESS(rc)) + { + /* Run the interpreter. */ + rc = vdScriptInterpreterEvaluate(&InterpCtx); + vdScriptStackDestroy(&InterpCtx.StackValues); + vdScriptStackDestroy(&InterpCtx.StackCtrl); + } + } + } + else + rc = vdScriptInterpreterError(&InterpCtx, VERR_INVALID_PARAMETER, RT_SRC_POS, "Invalid number of parameters, expected %d got %d", pFn->cArgs, cArgs); + } + else + rc = vdScriptInterpreterError(&InterpCtx, VERR_NOT_FOUND, RT_SRC_POS, "Function with identifier \"%s\" not found", pszFn); + + + return rc; +} diff --git a/src/VBox/Storage/testcase/VDScriptStack.h b/src/VBox/Storage/testcase/VDScriptStack.h new file mode 100644 index 00000000..93eb1c36 --- /dev/null +++ b/src/VBox/Storage/testcase/VDScriptStack.h @@ -0,0 +1,142 @@ +/** @file + * + * VBox HDD container test utility - scripting engine, internal stack implementation. + */ + +/* + * Copyright (C) 2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ +#ifndef _VDScriptStack_h__ +#define _VDScriptStack_h__ + +#include <iprt/list.h> +#include <iprt/string.h> + +#include "VDScript.h" + +/** + * Stack structure. + */ +typedef struct VDSCRIPTSTACK +{ + /** Size of one stack element. */ + size_t cbStackEntry; + /** Stack memory. */ + void *pvStack; + /** Number of elements on the stack. */ + unsigned cOnStack; + /** Maximum number of elements the stack can hold. */ + unsigned cOnStackMax; +} VDSCRIPTSTACK; +/** Pointer to a stack. */ +typedef VDSCRIPTSTACK *PVDSCRIPTSTACK; + +/** + * Init the stack structure. + * + * @returns nothing. + * @param pStack The stack to initialize. + * @param cbStackEntry The size of one stack entry. + */ +DECLINLINE(void) vdScriptStackInit(PVDSCRIPTSTACK pStack, size_t cbStackEntry) +{ + pStack->cbStackEntry = cbStackEntry; + pStack->pvStack = NULL; + pStack->cOnStack = 0; + pStack->cOnStackMax = 0; +} + +/** + * Destroys the given stack freeing all memory. + * + * @returns nothing. + * @param pStack The stack to destroy. + */ +DECLINLINE(void) vdScriptStackDestroy(PVDSCRIPTSTACK pStack) +{ + if (pStack->pvStack) + RTMemFree(pStack->pvStack); + pStack->cbStackEntry = 0; + pStack->pvStack = NULL; + pStack->cOnStack = 0; + pStack->cOnStackMax = 0; +} + +/** + * Gets the topmost unused stack entry. + * + * @returns Pointer to the first unused entry. + * NULL if there is no room left and increasing the stack failed. + * @param pStack The stack. + */ +DECLINLINE(void *)vdScriptStackGetUnused(PVDSCRIPTSTACK pStack) +{ + void *pvElem = NULL; + + if (pStack->cOnStack >= pStack->cOnStackMax) + { + unsigned cOnStackMaxNew = pStack->cOnStackMax + 10; + void *pvStackNew = NULL; + + /* Try to increase stack space. */ + pvStackNew = RTMemRealloc(pStack->pvStack, cOnStackMaxNew * pStack->cbStackEntry); + if (pvStackNew) + { + pStack->pvStack = pvStackNew; + pStack->cOnStackMax = cOnStackMaxNew; + } + + } + + if (pStack->cOnStack < pStack->cOnStackMax) + pvElem = (char *)pStack->pvStack + pStack->cOnStack * pStack->cbStackEntry; + + return pvElem; +} + +/** + * Gets the topmost used entry on the stack. + * + * @returns Pointer to the first used entry + * or NULL if the stack is empty. + * @param pStack The stack. + */ +DECLINLINE(void *)vdScriptStackGetUsed(PVDSCRIPTSTACK pStack) +{ + if (!pStack->cOnStack) + return NULL; + else + return (char *)pStack->pvStack + (pStack->cOnStack - 1) * pStack->cbStackEntry; +} + +/** + * Increases the used element count for the given stack. + * + * @returns nothing. + * @param pStack The stack. + */ +DECLINLINE(void) vdScriptStackPush(PVDSCRIPTSTACK pStack) +{ + pStack->cOnStack++; +} + +/** + * Decreases the used element count for the given stack. + * + * @returns nothing. + * @param pStack The stack. + */ +DECLINLINE(void) vdScriptStackPop(PVDSCRIPTSTACK pStack) +{ + pStack->cOnStack--; +} + +#endif /* _VDScriptStack_h__ */ diff --git a/src/VBox/Storage/testcase/tstVD-2.cpp b/src/VBox/Storage/testcase/tstVD-2.cpp index 13821789..7e81ffc9 100644 --- a/src/VBox/Storage/testcase/tstVD-2.cpp +++ b/src/VBox/Storage/testcase/tstVD-2.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Storage/testcase/tstVD.cpp b/src/VBox/Storage/testcase/tstVD.cpp index c858e96c..953b5c34 100644 --- a/src/VBox/Storage/testcase/tstVD.cpp +++ b/src/VBox/Storage/testcase/tstVD.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2010 Oracle Corporation + * Copyright (C) 2006-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Storage/testcase/tstVDCompact.vd b/src/VBox/Storage/testcase/tstVDCompact.vd index 9d511baa..95532d23 100644 --- a/src/VBox/Storage/testcase/tstVDCompact.vd +++ b/src/VBox/Storage/testcase/tstVDCompact.vd @@ -1,77 +1,62 @@ -# $Id: tstVDCompact.vd $ -# -# Storage: Testcase for compacting disks. -# +/* $Id: tstVDCompact.vd $ */ +/** + * Storage: Testcase for compacting disks. + */ -# -# Copyright (C) 2011 Oracle Corporation -# -# This file is part of VirtualBox Open Source Edition (OSE), as -# available from http://www.virtualbox.org. This file is free software; -# you can redistribute it and/or modify it under the terms of the GNU -# General Public License (GPL) as published by the Free Software -# Foundation, in version 2 as it comes in the "COPYING" file of the -# VirtualBox OSE distribution. VirtualBox OSE is distributed in the -# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. -# +/* + * Copyright (C) 2011 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ -# Init I/O RNG for generating random data for writes -iorngcreate size=10M mode=manual seed=1234567890 +void tstCompact(string strMsg, string strBackend) +{ + print(strMsg); -# Create zero pattern -iopatterncreatefromnumber name=zero size=1M pattern=0 + /* Create disk containers, read verification is on. */ + createdisk("disk", true); + create("disk", "base", "tstCompact.disk", "dynamic", strBackend, 200M, false); -print msg=Testing_VDI -# Create disk containers, read verification is on. -createdisk name=disk verify=yes -# Create the disk. -create disk=disk mode=base name=tstCompact.vdi type=dynamic backend=VDI size=200M -# Fill the disk with random data -io disk=disk async=no mode=seq blocksize=64k off=0-200M size=200M writes=100 -# Read the data to verify it once. -io disk=disk async=no mode=seq blocksize=64k off=0-200M size=200M writes=0 -# Fill a part with 0's -io disk=disk async=no mode=seq blocksize=64k off=100M-150M size=50M writes=100 pattern=zero -# Now compact -compact disk=disk image=0 -# Read again to verify that the content hasn't changed -io disk=disk async=no mode=seq blocksize=64k off=0-200M size=200M writes=0 -# Fill everything with 0 -io disk=disk async=no mode=seq blocksize=64k off=0M-200M size=200M writes=100 pattern=zero -# Now compact -compact disk=disk image=0 -# Read again to verify that the content hasn't changed -io disk=disk async=no mode=seq blocksize=64k off=0-200M size=200M writes=0 -# Cleanup -close disk=disk mode=single delete=yes -destroydisk name=disk + /* Fill the disk with random data. */ + io("disk", false, 1, "seq", 64K, 0, 200M, 200M, 100, "none"); + /* Read the data to verify it once. */ + io("disk", false, 1, "seq", 64K, 0, 200M, 200M, 0, "none"); + /* Fill a part with 0's. */ + io("disk", false, 1, "seq", 64K, 100M, 150M, 50M, 100, "zero"); -print msg=Testing_VHD -# Create disk containers, read verification is on. -createdisk name=disk verify=yes -# Create the disk. -create disk=disk mode=base name=tstCompact.vhd type=dynamic backend=VHD size=200M -# Fill the disk with random data -io disk=disk async=no mode=seq blocksize=64k off=0-200M size=200M writes=100 -# Read the data to verify it once. -io disk=disk async=no mode=seq blocksize=64k off=0-200M size=200M writes=0 -# Fill a part with 0's -io disk=disk async=no mode=seq blocksize=64k off=100M-150M size=50M writes=100 pattern=zero -# Now compact -compact disk=disk image=0 -# Read again to verify that the content hasn't changed -io disk=disk async=no mode=seq blocksize=64k off=0-200M size=200M writes=0 -# Fill everything with 0 -io disk=disk async=no mode=seq blocksize=64k off=0M-200M size=200M writes=100 pattern=zero -# Now compact -compact disk=disk image=0 -# Read again to verify that the content hasn't changed -io disk=disk async=no mode=seq blocksize=64k off=0-200M size=200M writes=0 -# Cleanup -close disk=disk mode=single delete=yes -destroydisk name=disk + /* Now compact. */ + compact("disk", 0); + /* Read again to verify that the content hasn't changed. */ + io("disk", false, 1, "seq", 64K, 0, 200M, 200M, 0, "none"); + /* Fill everything with 0. */ + io("disk", false, 1, "seq", 64K, 0, 200M, 200M, 100, "zero"); + /* Now compact again. */ + compact("disk", 0); + /* Read again to verify that the content hasn't changed. */ + io("disk", false, 1, "seq", 64K, 0, 200M, 200M, 0, "none"); -# Destroy RNG and pattern -iopatterndestroy name=zero -iorngdestroy + close("disk", "single", true); + destroydisk("disk"); +} +void main() +{ + /* Init I/O RNG for generating random data for writes. */ + iorngcreate(10M, "manual", 1234567890); + + /* Create zero pattern */ + iopatterncreatefromnumber("zero", 1M, 0); + + tstCompact("Testing VDI", "VDI"); + tstCompact("Testing VHD", "VHD"); + + /* Destroy RNG and pattern */ + iopatterndestroy("zero"); + iorngdestroy(); +} diff --git a/src/VBox/Storage/testcase/tstVDCopy.cpp b/src/VBox/Storage/testcase/tstVDCopy.cpp index ef8a37e2..6d73560b 100644 --- a/src/VBox/Storage/testcase/tstVDCopy.cpp +++ b/src/VBox/Storage/testcase/tstVDCopy.cpp @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2006-2010 Oracle Corporation + * Copyright (C) 2006-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Storage/testcase/tstVDCopy.vd b/src/VBox/Storage/testcase/tstVDCopy.vd index cd4c97f3..9cd4e034 100644 --- a/src/VBox/Storage/testcase/tstVDCopy.vd +++ b/src/VBox/Storage/testcase/tstVDCopy.vd @@ -1,89 +1,86 @@ -# $Id: tstVDCopy.vd $ -# -# Storage: Testcase for VDCopy with snapshots and optimizations. -# - -# -# Copyright (C) 2011 Oracle Corporation -# -# This file is part of VirtualBox Open Source Edition (OSE), as -# available from http://www.virtualbox.org. This file is free software; -# you can redistribute it and/or modify it under the terms of the GNU -# General Public License (GPL) as published by the Free Software -# Foundation, in version 2 as it comes in the "COPYING" file of the -# VirtualBox OSE distribution. VirtualBox OSE is distributed in the -# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. -# - -# Init I/O RNG for generating random data for writes -iorngcreate size=10M mode=manual seed=1234567890 - -# Create source disk and fill data -print msg=Creating_Source_Disk - createdisk name=source verify=no - create disk=source mode=base name=source_base.vdi type=dynamic backend=VDI size=1G - io disk=source async=no mode=rnd blocksize=64k off=0-512M size=256M writes=100 - -# Create 1st snapshot -print msg=Creating_First_Diff - create disk=source mode=diff name=source_diff1.vdi type=dynamic backend=VDI size=1G - io disk=source async=no mode=rnd blocksize=64k off=512M-1G size=256M writes=50 - -# Create 2nd snapshot -print msg=Creating_Second_Diff - create disk=source mode=diff name=source_diff2.vdi type=dynamic backend=VDI size=1G - io disk=source async=no mode=rnd blocksize=1M off=0M-1G size=45M writes=100 - -print msg=Creating_Third_Diff - create disk=source mode=diff name=source_diff3.vdi type=dynamic backend=VDI size=1G - io disk=source async=no mode=rnd blocksize=1M off=0M-1G size=45M writes=100 - -print msg=Creating_Fourth_Diff - create disk=source mode=diff name=source_diff4.vdi type=dynamic backend=VDI size=1G - io disk=source async=no mode=rnd blocksize=1M off=0M-1G size=45M writes=100 - -# Create destination disk -print msg=Creating_Destination_Disk - createdisk name=dest verify=no - -# Copy base image -print msg=Copying_Base_Image - copy diskfrom=source diskto=dest imagefrom=0 backend=VDI filename=dest_base.vdi - -print msg=Copying_First_Diff_optimized - copy diskfrom=source diskto=dest imagefrom=1 backend=VDI filename=dest_diff1.vdi fromsame=0 tosame=0 - -print msg=Copying_Second_Diff_optimized - copy diskfrom=source diskto=dest imagefrom=2 backend=VDI filename=dest_diff2.vdi fromsame=1 tosame=1 - copy diskfrom=source diskto=dest imagefrom=3 backend=VDI filename=dest_diff3.vdi fromsame=2 tosame=2 - copy diskfrom=source diskto=dest imagefrom=4 backend=VDI filename=dest_diff4.vdi fromsame=3 tosame=3 - -print msg=Comparing_Disks - comparedisks disk1=source disk2=dest - -printfilesize disk=source image=0 -printfilesize disk=source image=1 -printfilesize disk=source image=2 -printfilesize disk=source image=3 -printfilesize disk=source image=4 - -printfilesize disk=dest image=0 -printfilesize disk=dest image=1 -printfilesize disk=dest image=2 -printfilesize disk=dest image=3 -printfilesize disk=dest image=4 - -# Cleanup -print msg=Cleaning_up - close disk=dest mode=single delete=yes - close disk=dest mode=single delete=yes - close disk=dest mode=single delete=yes - - close disk=source mode=single delete=yes - close disk=source mode=single delete=yes - close disk=source mode=single delete=yes - destroydisk name=source - destroydisk name=dest - -iorngdestroy - +/* $Id: tstVDCopy.vd $ */ +/** + * Storage: Testcase for VDCopy with snapshots and optimizations. + */ + +/* + * Copyright (C) 2011 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +void main() +{ + /* Init I/O RNG for generating random data for writes. */ + iorngcreate(10M, "manual", 1234567890); + + /* Create source disk and fill data. */ + print("Creating Source Disk"); + createdisk("source", false); + create("source", "base", "source_base.vdi", "dynamic", "VDI", 1G, false); + io("source", false, 1, "rnd", 64K, 0, 512M, 256M, 100, "none"); + + print("Creating first diff"); + create("source", "diff", "source_diff1.vdi", "dynamic", "VDI", 1G, false); + io("source", false, 1, "rnd", 64K, 512M, 1G, 256M, 50, "none"); + + print("Creating second diff"); + create("source", "diff", "source_diff2.vdi", "dynamic", "VDI", 1G, false); + io("source", false, 1, "rnd", 1M, 0, 1G, 45M, 100, "none"); + + print("Creating third diff"); + create("source", "diff", "source_diff3.vdi", "dynamic", "VDI", 1G, false); + io("source", false, 1, "rnd", 1M, 0, 1G, 45M, 100, "none"); + + print("Creating fourth diff"); + create("source", "diff", "source_diff4.vdi", "dynamic", "VDI", 1G, false); + io("source", false, 1, "rnd", 1M, 0, 1G, 45M, 100, "none"); + + print("Creating destination disk"); + createdisk("dest", false); + + print("Copying base image"); + copy("source", "dest", 0, "VDI", "dest_base.vdi", false, 0, 0xffffffff, 0xffffffff); /* Image content unknown */ + + print("Copying first diff optimized"); + copy("source", "dest", 1, "VDI", "dest_diff1.vdi", false, 0, 0, 0); + + print("Copying other diffs optimized"); + copy("source", "dest", 2, "VDI", "dest_diff1.vdi", false, 0, 1, 1); + copy("source", "dest", 3, "VDI", "dest_diff1.vdi", false, 0, 2, 2); + copy("source", "dest", 4, "VDI", "dest_diff1.vdi", false, 0, 3, 3); + + print("Comparing_Disks"); + comparedisks("source", "dest"); + + printfilesize("source", 0); + printfilesize("source", 1); + printfilesize("source", 2); + printfilesize("source", 3); + printfilesize("source", 4); + + printfilesize("dest", 0); + printfilesize("dest", 1); + printfilesize("dest", 2); + printfilesize("dest", 3); + printfilesize("dest", 4); + + print("Cleaning up"); + close("dest", "single", true); + close("dest", "single", true); + close("dest", "single", true); + + close("source", "single", true); + close("source", "single", true); + close("source", "single", true); + destroydisk("source"); + destroydisk("dest"); + + iorngdestroy(); +} diff --git a/src/VBox/Storage/testcase/tstVDDiscard.vd b/src/VBox/Storage/testcase/tstVDDiscard.vd index 7ac1a0ad..56fadf58 100644 --- a/src/VBox/Storage/testcase/tstVDDiscard.vd +++ b/src/VBox/Storage/testcase/tstVDDiscard.vd @@ -1,55 +1,59 @@ -# $Id: tstVDDiscard.vd $ -# -# Storage: Testcase for discarding data in a disk. -# - -# -# Copyright (C) 2011 Oracle Corporation -# -# This file is part of VirtualBox Open Source Edition (OSE), as -# available from http://www.virtualbox.org. This file is free software; -# you can redistribute it and/or modify it under the terms of the GNU -# General Public License (GPL) as published by the Free Software -# Foundation, in version 2 as it comes in the "COPYING" file of the -# VirtualBox OSE distribution. VirtualBox OSE is distributed in the -# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. -# - -# Init I/O RNG for generating random data for writes -iorngcreate size=10M mode=manual seed=1234567890 - -print msg=Testing_VDI -# Create disk containers, read verification is on. -createdisk name=disk verify=yes -# Create the disk. -create disk=disk mode=base name=tstCompact.vdi type=dynamic backend=VDI size=2G -# Fill the disk with random data -io disk=disk async=no mode=seq blocksize=64k off=0-2G size=2G writes=100 -# Read the data to verify it once. -io disk=disk async=no mode=seq blocksize=64k off=0-2G size=2G writes=0 -close disk=disk mode=single delete=no - -open disk=disk name=tstCompact.vdi backend=VDI async=yes discard=yes -printfilesize disk=disk image=0 -discard async=yes disk=disk ranges=6,0M,512k,1M,512k,2M,512k,3M,512k,4M,512k,5M,512k -discard async=yes disk=disk ranges=6,6M,512k,7M,512k,8M,512k,9M,512k,10M,512k,11M,512k -discard async=yes disk=disk ranges=1,512k,512k -printfilesize disk=disk image=0 - -print msg=Discard_whole_block -discard async=yes disk=disk ranges=1,20M,1M -printfilesize disk=disk image=0 - -print msg=Split_Discard -discard async=yes disk=disk ranges=1,21M,512k -printfilesize disk=disk image=0 -discard async=yes disk=disk ranges=1,22016k,512k -printfilesize disk=disk image=0 - -# Cleanup -close disk=disk mode=single delete=yes -destroydisk name=disk - -# Destroy RNG and pattern -iorngdestroy - +/* $Id: tstVDDiscard.vd $ */ +/** + * Storage: Testcase for discarding data in a disk. + */ + +/* + * Copyright (C) 2011 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +void main() +{ + /* Init I/O RNG for generating random data for writes. */ + iorngcreate(10M, "manual", 1234567890); + + print("Testing VDI"); + + /* Create disk containers, read verification is on. */ + createdisk("disk", true /* fVerify */); + /* Create the disk. */ + create("disk", "base", "tstCompact.vdi", "dynamic", "VDI", 2G, false /* fIgnoreFlush */); + /* Fill the disk with random data */ + io("disk", false, 1, "seq", 64K, 0, 2G, 2G, 100, "none"); + /* Read the data to verify it once. */ + io("disk", false, 1, "seq", 64K, 0, 2G, 2G, 0, "none"); + close("disk", "single", false); + + open("disk", "tstCompact.vdi", "VDI", true, false, false, true, false); + printfilesize("disk", 0); + discard("disk", true, "6,0M,512K,1M,512K,2M,512K,3M,512K,4M,512K,5M,512K"); + discard("disk", true, "6,6M,512K,7M,512K,8M,512K,9M,512K,10M,512K,11M,512K"); + discard("disk", true, "1,512K,512K"); + discard("disk", false, "1,1024K,64K"); + printfilesize("disk", 0); + + print("Discard whole block"); + discard("disk", true, "1,20M,1M"); + printfilesize("disk", 0); + + print("Split Discard"); + discard("disk", true, "1,21M,512K"); + printfilesize("disk", 0); + discard("disk", true, "1,22016K,512K"); + printfilesize("disk", 0); + + /* Cleanup */ + close("disk", "single", true); + destroydisk("disk"); + + /* Destroy RNG and pattern */ + iorngdestroy(); +} diff --git a/src/VBox/Storage/testcase/tstVDIo.cpp b/src/VBox/Storage/testcase/tstVDIo.cpp index d492c50a..9422f477 100644 --- a/src/VBox/Storage/testcase/tstVDIo.cpp +++ b/src/VBox/Storage/testcase/tstVDIo.cpp @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2011 Oracle Corporation + * Copyright (C) 2011-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -34,9 +34,11 @@ #include <iprt/critsect.h> #include "VDMemDisk.h" -#include "VDIoBackendMem.h" +#include "VDIoBackend.h" #include "VDIoRnd.h" +#include "VDScript.h" + /** * A virtual file backed by memory. */ @@ -46,8 +48,8 @@ typedef struct VDFILE RTLISTNODE Node; /** Name of the file. */ char *pszName; - /** Memory file baking the file. */ - PVDMEMDISK pMemDisk; + /** Storage backing the file. */ + PVDIOSTORAGE pIoStorage; /** Flag whether the file is read locked. */ bool fReadLock; /** Flag whether the file is write locked. */ @@ -124,8 +126,8 @@ typedef struct VDTESTGLOB RTLISTNODE ListFiles; /** Head of the pattern list. */ RTLISTNODE ListPatterns; - /** Memory I/O backend. */ - PVDIOBACKENDMEM pIoBackend; + /** I/O backend, common data. */ + PVDIOBACKEND pIoBackend; /** Error interface. */ VDINTERFACEERROR VDIfError; /** Pointer to the per disk interface list. */ @@ -136,6 +138,8 @@ typedef struct VDTESTGLOB PVDINTERFACE pInterfacesImages; /** I/O RNG handle. */ PVDIORND pIoRnd; + /** Current storage backend to use. */ + char *pszIoBackend; } VDTESTGLOB, *PVDTESTGLOB; /** @@ -213,369 +217,279 @@ typedef struct VDIOTEST } u; } VDIOTEST, *PVDIOTEST; -/** - * Argument types. - */ -typedef enum VDSCRIPTARGTYPE -{ - /** Argument is a string. */ - VDSCRIPTARGTYPE_STRING = 0, - /** Argument is a 64bit unsigned number. */ - VDSCRIPTARGTYPE_UNSIGNED_NUMBER, - /** Argument is a 64bit signed number. */ - VDSCRIPTARGTYPE_SIGNED_NUMBER, - /** Arugment is a unsigned 64bit range */ - VDSCRIPTARGTYPE_UNSIGNED_RANGE, - /** Arugment is a boolean. */ - VDSCRIPTARGTYPE_BOOL -} VDSCRIPTARGTYPE; - -/** - * Script argument. - */ -typedef struct VDSCRIPTARG -{ - /** Argument identifier. */ - char chId; - /** Type of the argument. */ - VDSCRIPTARGTYPE enmType; - /** Type depndent data. */ - union - { - /** String. */ - const char *pcszString; - /** Bool. */ - bool fFlag; - /** unsigned number. */ - uint64_t u64; - /** Signed number. */ - int64_t i64; - /** Unsigned range. */ - struct - { - uint64_t Start; - uint64_t End; - } Range; - } u; -} VDSCRIPTARG, *PVDSCRIPTARG; - -/** Script action handler. */ -typedef DECLCALLBACK(int) FNVDSCRIPTACTION(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs); -/** Pointer to a script action handler. */ -typedef FNVDSCRIPTACTION *PFNVDSCRIPTACTION; - -/** - * Script argument descriptor. - */ -typedef struct VDSCRIPTARGDESC -{ - /** Name of the arugment. */ - const char *pcszName; - /** Identifier for the argument. */ - char chId; - /** Type of the argument. */ - VDSCRIPTARGTYPE enmType; - /** Flags */ - uint32_t fFlags; -} VDSCRIPTARGDESC, *PVDSCRIPTARGDESC; -/** Pointer to a const script argument descriptor. */ -typedef const VDSCRIPTARGDESC *PCVDSCRIPTARGDESC; - -/** Flag whether the argument is mandatory. */ -#define VDSCRIPTARGDESC_FLAG_MANDATORY RT_BIT(0) -/** Flag whether the number can have a size suffix (K|M|G) */ -#define VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX RT_BIT(1) - -/** - * Script action. - */ -typedef struct VDSCRIPTACTION -{ - /** Action name. */ - const char *pcszAction; - /** Pointer to the arguments. */ - const PCVDSCRIPTARGDESC paArgDesc; - /** Number of arugments in the array. */ - unsigned cArgDescs; - /** Pointer to the action handler. */ - PFNVDSCRIPTACTION pfnHandler; -} VDSCRIPTACTION, *PVDSCRIPTACTION; - -typedef const VDSCRIPTACTION *PCVDSCRIPTACTION; - -static DECLCALLBACK(int) vdScriptHandlerCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs); -static DECLCALLBACK(int) vdScriptHandlerOpen(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs); -static DECLCALLBACK(int) vdScriptHandlerIo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs); -static DECLCALLBACK(int) vdScriptHandlerFlush(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs); -static DECLCALLBACK(int) vdScriptHandlerMerge(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs); -static DECLCALLBACK(int) vdScriptHandlerCompact(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs); -static DECLCALLBACK(int) vdScriptHandlerDiscard(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs); -static DECLCALLBACK(int) vdScriptHandlerCopy(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs); -static DECLCALLBACK(int) vdScriptHandlerClose(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs); -static DECLCALLBACK(int) vdScriptHandlerPrintFileSize(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs); -static DECLCALLBACK(int) vdScriptHandlerIoLogReplay(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs); -static DECLCALLBACK(int) vdScriptHandlerIoRngCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs); -static DECLCALLBACK(int) vdScriptHandlerIoRngDestroy(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs); -static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromNumber(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs); -static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromFile(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs); -static DECLCALLBACK(int) vdScriptHandlerIoPatternDestroy(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs); -static DECLCALLBACK(int) vdScriptHandlerSleep(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs); -static DECLCALLBACK(int) vdScriptHandlerDumpFile(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs); -static DECLCALLBACK(int) vdScriptHandlerCreateDisk(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs); -static DECLCALLBACK(int) vdScriptHandlerDestroyDisk(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs); -static DECLCALLBACK(int) vdScriptHandlerCompareDisks(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs); -static DECLCALLBACK(int) vdScriptHandlerDumpDiskInfo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs); -static DECLCALLBACK(int) vdScriptHandlerPrintMsg(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs); -static DECLCALLBACK(int) vdScriptHandlerShowStatistics(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs); -static DECLCALLBACK(int) vdScriptHandlerResetStatistics(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs); +static DECLCALLBACK(int) vdScriptHandlerCreate(PVDSCRIPTARG paScriptArgs, void *pvUser); +static DECLCALLBACK(int) vdScriptHandlerOpen(PVDSCRIPTARG paScriptArgs, void *pvUser); +static DECLCALLBACK(int) vdScriptHandlerIo(PVDSCRIPTARG paScriptArgs, void *pvUser); +static DECLCALLBACK(int) vdScriptHandlerFlush(PVDSCRIPTARG paScriptArgs, void *pvUser); +static DECLCALLBACK(int) vdScriptHandlerMerge(PVDSCRIPTARG paScriptArgs, void *pvUser); +static DECLCALLBACK(int) vdScriptHandlerCompact(PVDSCRIPTARG paScriptArgs, void *pvUser); +static DECLCALLBACK(int) vdScriptHandlerDiscard(PVDSCRIPTARG paScriptArgs, void *pvUser); +static DECLCALLBACK(int) vdScriptHandlerCopy(PVDSCRIPTARG paScriptArgs, void *pvUser); +static DECLCALLBACK(int) vdScriptHandlerClose(PVDSCRIPTARG paScriptArgs, void *pvUser); +static DECLCALLBACK(int) vdScriptHandlerPrintFileSize(PVDSCRIPTARG paScriptArgs, void *pvUser); +static DECLCALLBACK(int) vdScriptHandlerIoLogReplay(PVDSCRIPTARG paScriptArgs, void *pvUser); +static DECLCALLBACK(int) vdScriptHandlerIoRngCreate(PVDSCRIPTARG paScriptArgs, void *pvUser); +static DECLCALLBACK(int) vdScriptHandlerIoRngDestroy(PVDSCRIPTARG paScriptArgs, void *pvUser); +static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromNumber(PVDSCRIPTARG paScriptArgs, void *pvUser); +static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromFile(PVDSCRIPTARG paScriptArgs, void *pvUser); +static DECLCALLBACK(int) vdScriptHandlerIoPatternDestroy(PVDSCRIPTARG paScriptArgs, void *pvUser); +static DECLCALLBACK(int) vdScriptHandlerSleep(PVDSCRIPTARG paScriptArgs, void *pvUser); +static DECLCALLBACK(int) vdScriptHandlerDumpFile(PVDSCRIPTARG paScriptArgs, void *pvUser); +static DECLCALLBACK(int) vdScriptHandlerCreateDisk(PVDSCRIPTARG paScriptArgs, void *pvUser); +static DECLCALLBACK(int) vdScriptHandlerDestroyDisk(PVDSCRIPTARG paScriptArgs, void *pvUser); +static DECLCALLBACK(int) vdScriptHandlerCompareDisks(PVDSCRIPTARG paScriptArgs, void *pvUser); +static DECLCALLBACK(int) vdScriptHandlerDumpDiskInfo(PVDSCRIPTARG paScriptArgs, void *pvUser); +static DECLCALLBACK(int) vdScriptHandlerPrintMsg(PVDSCRIPTARG paScriptArgs, void *pvUser); +static DECLCALLBACK(int) vdScriptHandlerShowStatistics(PVDSCRIPTARG paScriptArgs, void *pvUser); +static DECLCALLBACK(int) vdScriptHandlerResetStatistics(PVDSCRIPTARG paScriptArgs, void *pvUser); +static DECLCALLBACK(int) vdScriptHandlerResize(PVDSCRIPTARG paScriptArgs, void *pvUser); +static DECLCALLBACK(int) vdScriptHandlerSetFileBackend(PVDSCRIPTARG paScriptArgs, void *pvUser); /* create action */ -const VDSCRIPTARGDESC g_aArgCreate[] = -{ - /* pcszName chId enmType fFlags */ - {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}, - {"mode", 'm', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}, - {"name", 'n', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}, - {"type", 't', VDSCRIPTARGTYPE_STRING, 0}, - {"backend", 'b', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}, - {"size", 's', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY | VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX}, - {"ignoreflush", 'f', VDSCRIPTARGTYPE_BOOL, 0} +const VDSCRIPTTYPE g_aArgCreate[] = +{ + VDSCRIPTTYPE_STRING, + VDSCRIPTTYPE_STRING, + VDSCRIPTTYPE_STRING, + VDSCRIPTTYPE_STRING, + VDSCRIPTTYPE_STRING, + VDSCRIPTTYPE_UINT64, + VDSCRIPTTYPE_BOOL }; /* open action */ -const VDSCRIPTARGDESC g_aArgOpen[] = -{ - /* pcszName chId enmType fFlags */ - {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}, - {"name", 'n', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}, - {"backend", 'b', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}, - {"async", 'a', VDSCRIPTARGTYPE_BOOL, 0}, - {"shareable", 's', VDSCRIPTARGTYPE_BOOL, 0}, - {"readonly", 'r', VDSCRIPTARGTYPE_BOOL, 0}, - {"discard", 'i', VDSCRIPTARGTYPE_BOOL, 0}, - {"ignoreflush", 'f', VDSCRIPTARGTYPE_BOOL, 0}, +const VDSCRIPTTYPE g_aArgOpen[] = +{ + VDSCRIPTTYPE_STRING, /* disk */ + VDSCRIPTTYPE_STRING, /* name */ + VDSCRIPTTYPE_STRING, /* backend */ + VDSCRIPTTYPE_BOOL, /* async */ + VDSCRIPTTYPE_BOOL, /* shareable */ + VDSCRIPTTYPE_BOOL, /* readonly */ + VDSCRIPTTYPE_BOOL, /* discard */ + VDSCRIPTTYPE_BOOL /* ignoreflush */ }; /* I/O action */ -const VDSCRIPTARGDESC g_aArgIo[] = -{ - /* pcszName chId enmType fFlags */ - {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}, - {"async", 'a', VDSCRIPTARGTYPE_BOOL, 0}, - {"max-reqs", 'l', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, 0}, - {"mode", 'm', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}, - {"size", 's', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX}, - {"blocksize", 'b', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY | VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX}, - {"off", 'o', VDSCRIPTARGTYPE_UNSIGNED_RANGE, VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX}, - {"writes", 'w', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY}, - {"pattern", 'p', VDSCRIPTARGTYPE_STRING, 0}, +const VDSCRIPTTYPE g_aArgIo[] = +{ + VDSCRIPTTYPE_STRING, /* disk */ + VDSCRIPTTYPE_BOOL, /* async */ + VDSCRIPTTYPE_UINT32, /* max-reqs */ + VDSCRIPTTYPE_STRING, /* mode */ + VDSCRIPTTYPE_UINT64, /* size */ + VDSCRIPTTYPE_UINT64, /* blocksize */ + VDSCRIPTTYPE_UINT64, /* offStart */ + VDSCRIPTTYPE_UINT64, /* offEnd */ + VDSCRIPTTYPE_UINT32, /* writes */ + VDSCRIPTTYPE_STRING /* pattern */ }; /* flush action */ -const VDSCRIPTARGDESC g_aArgFlush[] = +const VDSCRIPTTYPE g_aArgFlush[] = { - /* pcszName chId enmType fFlags */ - {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}, - {"async", 'a', VDSCRIPTARGTYPE_BOOL, 0} + VDSCRIPTTYPE_STRING, /* disk */ + VDSCRIPTTYPE_BOOL /* async */ }; /* merge action */ -const VDSCRIPTARGDESC g_aArgMerge[] = +const VDSCRIPTTYPE g_aArgMerge[] = { - /* pcszName chId enmType fFlags */ - {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}, - {"from", 'f', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY}, - {"to", 't', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY}, + VDSCRIPTTYPE_STRING, /* disk */ + VDSCRIPTTYPE_UINT32, /* from */ + VDSCRIPTTYPE_UINT32 /* to */ }; /* Compact a disk */ -const VDSCRIPTARGDESC g_aArgCompact[] = +const VDSCRIPTTYPE g_aArgCompact[] = { - /* pcszName chId enmType fFlags */ - {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}, - {"image", 'i', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY}, + VDSCRIPTTYPE_STRING, /* disk */ + VDSCRIPTTYPE_UINT32 /* image */ }; /* Discard a part of a disk */ -const VDSCRIPTARGDESC g_aArgDiscard[] = +const VDSCRIPTTYPE g_aArgDiscard[] = { - /* pcszName chId enmType fFlags */ - {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}, - {"async", 'a', VDSCRIPTARGTYPE_BOOL, 0}, - {"ranges", 'r', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY} + VDSCRIPTTYPE_STRING, /* disk */ + VDSCRIPTTYPE_BOOL, /* async */ + VDSCRIPTTYPE_STRING /* ranges */ }; /* Compact a disk */ -const VDSCRIPTARGDESC g_aArgCopy[] = -{ - /* pcszName chId enmType fFlags */ - {"diskfrom", 's', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}, - {"diskto", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}, - {"imagefrom", 'i', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY}, - {"backend", 'b', VDSCRIPTARGTYPE_STRING, 0}, - {"filename", 'f', VDSCRIPTARGTYPE_STRING, 0}, - {"movebyrename", 'm', VDSCRIPTARGTYPE_BOOL, 0}, - {"size", 'z', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, 0}, - {"fromsame", 'o', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, 0}, - {"tosame", 't', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, 0} +const VDSCRIPTTYPE g_aArgCopy[] = +{ + VDSCRIPTTYPE_STRING, /* diskfrom */ + VDSCRIPTTYPE_STRING, /* diskto */ + VDSCRIPTTYPE_UINT32, /* imagefrom */ + VDSCRIPTTYPE_STRING, /* backend */ + VDSCRIPTTYPE_STRING, /* filename */ + VDSCRIPTTYPE_BOOL, /* movebyrename */ + VDSCRIPTTYPE_UINT64, /* size */ + VDSCRIPTTYPE_UINT32, /* fromsame */ + VDSCRIPTTYPE_UINT32 /* tosame */ }; /* close action */ -const VDSCRIPTARGDESC g_aArgClose[] = +const VDSCRIPTTYPE g_aArgClose[] = { - /* pcszName chId enmType fFlags */ - {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}, - {"mode", 'm', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}, - {"delete", 'r', VDSCRIPTARGTYPE_BOOL, VDSCRIPTARGDESC_FLAG_MANDATORY} + VDSCRIPTTYPE_STRING, /* disk */ + VDSCRIPTTYPE_STRING, /* mode */ + VDSCRIPTTYPE_BOOL /* delete */ }; /* print file size action */ -const VDSCRIPTARGDESC g_aArgPrintFileSize[] = +const VDSCRIPTTYPE g_aArgPrintFileSize[] = { - /* pcszName chId enmType fFlags */ - {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}, - {"image", 'i', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY} + VDSCRIPTTYPE_STRING, /* disk */ + VDSCRIPTTYPE_UINT32 /* image */ }; /* print file size action */ -const VDSCRIPTARGDESC g_aArgIoLogReplay[] = +const VDSCRIPTTYPE g_aArgIoLogReplay[] = { - /* pcszName chId enmType fFlags */ - {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}, - {"iolog", 'i', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY} + VDSCRIPTTYPE_STRING, /* disk */ + VDSCRIPTTYPE_STRING /* iolog */ }; /* I/O RNG create action */ -const VDSCRIPTARGDESC g_aArgIoRngCreate[] = +const VDSCRIPTTYPE g_aArgIoRngCreate[] = { - /* pcszName chId enmType fFlags */ - {"size", 'd', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY | VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX}, - {"mode", 'm', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}, - {"seed", 's', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, 0} + VDSCRIPTTYPE_UINT32, /* size */ + VDSCRIPTTYPE_STRING, /* mode */ + VDSCRIPTTYPE_UINT32, /* seed */ }; /* I/O pattern create action */ -const VDSCRIPTARGDESC g_aArgIoPatternCreateFromNumber[] = +const VDSCRIPTTYPE g_aArgIoPatternCreateFromNumber[] = { - /* pcszName chId enmType fFlags */ - {"name", 'n', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}, - {"size", 's', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY | VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX}, - {"pattern", 'p', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY}, + VDSCRIPTTYPE_STRING, /* name */ + VDSCRIPTTYPE_UINT32, /* size */ + VDSCRIPTTYPE_UINT32 /* pattern */ }; /* I/O pattern create action */ -const VDSCRIPTARGDESC g_aArgIoPatternCreateFromFile[] = +const VDSCRIPTTYPE g_aArgIoPatternCreateFromFile[] = { - /* pcszName chId enmType fFlags */ - {"name", 'n', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}, - {"file", 'f', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}, + VDSCRIPTTYPE_STRING, /* name */ + VDSCRIPTTYPE_STRING /* file */ }; /* I/O pattern destroy action */ -const VDSCRIPTARGDESC g_aArgIoPatternDestroy[] = +const VDSCRIPTTYPE g_aArgIoPatternDestroy[] = { - /* pcszName chId enmType fFlags */ - {"name", 'n', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY} + VDSCRIPTTYPE_STRING /* name */ }; /* Sleep */ -const VDSCRIPTARGDESC g_aArgSleep[] = +const VDSCRIPTTYPE g_aArgSleep[] = { - /* pcszName chId enmType fFlags */ - {"time", 't', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY} + VDSCRIPTTYPE_UINT32 /* time */ }; /* Dump memory file */ -const VDSCRIPTARGDESC g_aArgDumpFile[] = +const VDSCRIPTTYPE g_aArgDumpFile[] = { - /* pcszName chId enmType fFlags */ - {"file", 'f', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}, - {"path", 'p', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY} + VDSCRIPTTYPE_STRING, /* file */ + VDSCRIPTTYPE_STRING /* path */ }; /* Create virtual disk handle */ -const VDSCRIPTARGDESC g_aArgCreateDisk[] = +const VDSCRIPTTYPE g_aArgCreateDisk[] = { - /* pcszName chId enmType fFlags */ - {"name", 'n', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}, - {"verify", 'v', VDSCRIPTARGTYPE_BOOL, 0} + VDSCRIPTTYPE_STRING, /* name */ + VDSCRIPTTYPE_BOOL /* verify */ }; /* Create virtual disk handle */ -const VDSCRIPTARGDESC g_aArgDestroyDisk[] = +const VDSCRIPTTYPE g_aArgDestroyDisk[] = { - /* pcszName chId enmType fFlags */ - {"name", 'n', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY} + VDSCRIPTTYPE_STRING /* name */ }; /* Compare virtual disks */ -const VDSCRIPTARGDESC g_aArgCompareDisks[] = +const VDSCRIPTTYPE g_aArgCompareDisks[] = { - /* pcszName chId enmType fFlags */ - {"disk1", '1', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}, - {"disk2", '2', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY} + VDSCRIPTTYPE_STRING, /* disk1 */ + VDSCRIPTTYPE_STRING /* disk2 */ }; /* Dump disk info */ -const VDSCRIPTARGDESC g_aArgDumpDiskInfo[] = +const VDSCRIPTTYPE g_aArgDumpDiskInfo[] = { - /* pcszName chId enmType fFlags */ - {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}, + VDSCRIPTTYPE_STRING /* disk */ }; /* Print message */ -const VDSCRIPTARGDESC g_aArgPrintMsg[] = +const VDSCRIPTTYPE g_aArgPrintMsg[] = { - /* pcszName chId enmType fFlags */ - {"msg", 'm', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}, + VDSCRIPTTYPE_STRING /* msg */ }; /* Show statistics */ -const VDSCRIPTARGDESC g_aArgShowStatistics[] = +const VDSCRIPTTYPE g_aArgShowStatistics[] = { - /* pcszName chId enmType fFlags */ - {"file", 'f', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}, + VDSCRIPTTYPE_STRING /* file */ }; /* Reset statistics */ -const VDSCRIPTARGDESC g_aArgResetStatistics[] = +const VDSCRIPTTYPE g_aArgResetStatistics[] = { - /* pcszName chId enmType fFlags */ - {"file", 'f', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}, + VDSCRIPTTYPE_STRING /* file */ }; -const VDSCRIPTACTION g_aScriptActions[] = -{ - /* pcszAction paArgDesc cArgDescs pfnHandler */ - {"create", g_aArgCreate, RT_ELEMENTS(g_aArgCreate), vdScriptHandlerCreate}, - {"open", g_aArgOpen, RT_ELEMENTS(g_aArgOpen), vdScriptHandlerOpen}, - {"io", g_aArgIo, RT_ELEMENTS(g_aArgIo), vdScriptHandlerIo}, - {"flush", g_aArgFlush, RT_ELEMENTS(g_aArgFlush), vdScriptHandlerFlush}, - {"close", g_aArgClose, RT_ELEMENTS(g_aArgClose), vdScriptHandlerClose}, - {"printfilesize", g_aArgPrintFileSize, RT_ELEMENTS(g_aArgPrintFileSize), vdScriptHandlerPrintFileSize}, - {"ioreplay", g_aArgIoLogReplay, RT_ELEMENTS(g_aArgIoLogReplay), vdScriptHandlerIoLogReplay}, - {"merge", g_aArgMerge, RT_ELEMENTS(g_aArgMerge), vdScriptHandlerMerge}, - {"compact", g_aArgCompact, RT_ELEMENTS(g_aArgCompact), vdScriptHandlerCompact}, - {"discard", g_aArgDiscard, RT_ELEMENTS(g_aArgDiscard), vdScriptHandlerDiscard}, - {"copy", g_aArgCopy, RT_ELEMENTS(g_aArgCopy), vdScriptHandlerCopy}, - {"iorngcreate", g_aArgIoRngCreate, RT_ELEMENTS(g_aArgIoRngCreate), vdScriptHandlerIoRngCreate}, - {"iorngdestroy", NULL, 0, vdScriptHandlerIoRngDestroy}, - {"iopatterncreatefromnumber", g_aArgIoPatternCreateFromNumber, RT_ELEMENTS(g_aArgIoPatternCreateFromNumber), vdScriptHandlerIoPatternCreateFromNumber}, - {"iopatterncreatefromfile", g_aArgIoPatternCreateFromFile, RT_ELEMENTS(g_aArgIoPatternCreateFromFile), vdScriptHandlerIoPatternCreateFromFile}, - {"iopatterndestroy", g_aArgIoPatternDestroy, RT_ELEMENTS(g_aArgIoPatternDestroy), vdScriptHandlerIoPatternDestroy}, - {"sleep", g_aArgSleep, RT_ELEMENTS(g_aArgSleep), vdScriptHandlerSleep}, - {"dumpfile", g_aArgDumpFile, RT_ELEMENTS(g_aArgDumpFile), vdScriptHandlerDumpFile}, - {"createdisk", g_aArgCreateDisk, RT_ELEMENTS(g_aArgCreateDisk), vdScriptHandlerCreateDisk}, - {"destroydisk", g_aArgDestroyDisk, RT_ELEMENTS(g_aArgDestroyDisk), vdScriptHandlerDestroyDisk}, - {"comparedisks", g_aArgCompareDisks, RT_ELEMENTS(g_aArgCompareDisks), vdScriptHandlerCompareDisks}, - {"dumpdiskinfo", g_aArgDumpDiskInfo, RT_ELEMENTS(g_aArgDumpDiskInfo), vdScriptHandlerDumpDiskInfo}, - {"print", g_aArgPrintMsg, RT_ELEMENTS(g_aArgPrintMsg), vdScriptHandlerPrintMsg}, - {"showstatistics", g_aArgShowStatistics, RT_ELEMENTS(g_aArgShowStatistics), vdScriptHandlerShowStatistics}, - {"resetstatistics", g_aArgResetStatistics, RT_ELEMENTS(g_aArgResetStatistics), vdScriptHandlerResetStatistics} +/* Resize disk. */ +const VDSCRIPTTYPE g_aArgResize[] = +{ + VDSCRIPTTYPE_STRING, /* disk */ + VDSCRIPTTYPE_UINT64 /* size */ +}; + +/* Set file backend. */ +const VDSCRIPTTYPE g_aArgSetFileBackend[] = +{ + VDSCRIPTTYPE_STRING /* new file backend */ +}; + +const VDSCRIPTCALLBACK g_aScriptActions[] = +{ + /* pcszFnName enmTypeReturn paArgDesc cArgDescs pfnHandler */ + {"create", VDSCRIPTTYPE_VOID, g_aArgCreate, RT_ELEMENTS(g_aArgCreate), vdScriptHandlerCreate}, + {"open", VDSCRIPTTYPE_VOID, g_aArgOpen, RT_ELEMENTS(g_aArgOpen), vdScriptHandlerOpen}, + {"io", VDSCRIPTTYPE_VOID, g_aArgIo, RT_ELEMENTS(g_aArgIo), vdScriptHandlerIo}, + {"flush", VDSCRIPTTYPE_VOID, g_aArgFlush, RT_ELEMENTS(g_aArgFlush), vdScriptHandlerFlush}, + {"close", VDSCRIPTTYPE_VOID, g_aArgClose, RT_ELEMENTS(g_aArgClose), vdScriptHandlerClose}, + {"printfilesize", VDSCRIPTTYPE_VOID, g_aArgPrintFileSize, RT_ELEMENTS(g_aArgPrintFileSize), vdScriptHandlerPrintFileSize}, + {"ioreplay", VDSCRIPTTYPE_VOID, g_aArgIoLogReplay, RT_ELEMENTS(g_aArgIoLogReplay), vdScriptHandlerIoLogReplay}, + {"merge", VDSCRIPTTYPE_VOID, g_aArgMerge, RT_ELEMENTS(g_aArgMerge), vdScriptHandlerMerge}, + {"compact", VDSCRIPTTYPE_VOID, g_aArgCompact, RT_ELEMENTS(g_aArgCompact), vdScriptHandlerCompact}, + {"discard", VDSCRIPTTYPE_VOID, g_aArgDiscard, RT_ELEMENTS(g_aArgDiscard), vdScriptHandlerDiscard}, + {"copy", VDSCRIPTTYPE_VOID, g_aArgCopy, RT_ELEMENTS(g_aArgCopy), vdScriptHandlerCopy}, + {"iorngcreate", VDSCRIPTTYPE_VOID, g_aArgIoRngCreate, RT_ELEMENTS(g_aArgIoRngCreate), vdScriptHandlerIoRngCreate}, + {"iorngdestroy", VDSCRIPTTYPE_VOID, NULL, 0, vdScriptHandlerIoRngDestroy}, + {"iopatterncreatefromnumber", VDSCRIPTTYPE_VOID, g_aArgIoPatternCreateFromNumber, RT_ELEMENTS(g_aArgIoPatternCreateFromNumber), vdScriptHandlerIoPatternCreateFromNumber}, + {"iopatterncreatefromfile", VDSCRIPTTYPE_VOID, g_aArgIoPatternCreateFromFile, RT_ELEMENTS(g_aArgIoPatternCreateFromFile), vdScriptHandlerIoPatternCreateFromFile}, + {"iopatterndestroy", VDSCRIPTTYPE_VOID, g_aArgIoPatternDestroy, RT_ELEMENTS(g_aArgIoPatternDestroy), vdScriptHandlerIoPatternDestroy}, + {"sleep", VDSCRIPTTYPE_VOID, g_aArgSleep, RT_ELEMENTS(g_aArgSleep), vdScriptHandlerSleep}, + {"dumpfile", VDSCRIPTTYPE_VOID, g_aArgDumpFile, RT_ELEMENTS(g_aArgDumpFile), vdScriptHandlerDumpFile}, + {"createdisk", VDSCRIPTTYPE_VOID, g_aArgCreateDisk, RT_ELEMENTS(g_aArgCreateDisk), vdScriptHandlerCreateDisk}, + {"destroydisk", VDSCRIPTTYPE_VOID, g_aArgDestroyDisk, RT_ELEMENTS(g_aArgDestroyDisk), vdScriptHandlerDestroyDisk}, + {"comparedisks", VDSCRIPTTYPE_VOID, g_aArgCompareDisks, RT_ELEMENTS(g_aArgCompareDisks), vdScriptHandlerCompareDisks}, + {"dumpdiskinfo", VDSCRIPTTYPE_VOID, g_aArgDumpDiskInfo, RT_ELEMENTS(g_aArgDumpDiskInfo), vdScriptHandlerDumpDiskInfo}, + {"print", VDSCRIPTTYPE_VOID, g_aArgPrintMsg, RT_ELEMENTS(g_aArgPrintMsg), vdScriptHandlerPrintMsg}, + {"showstatistics", VDSCRIPTTYPE_VOID, g_aArgShowStatistics, RT_ELEMENTS(g_aArgShowStatistics), vdScriptHandlerShowStatistics}, + {"resetstatistics", VDSCRIPTTYPE_VOID, g_aArgResetStatistics, RT_ELEMENTS(g_aArgResetStatistics), vdScriptHandlerResetStatistics}, + {"resize", VDSCRIPTTYPE_VOID, g_aArgResize, RT_ELEMENTS(g_aArgResize), vdScriptHandlerResize}, + {"setfilebackend", VDSCRIPTTYPE_VOID, g_aArgSetFileBackend, RT_ELEMENTS(g_aArgSetFileBackend), vdScriptHandlerSetFileBackend}, }; const unsigned g_cScriptActions = RT_ELEMENTS(g_aScriptActions); +static DECLCALLBACK(int) vdScriptCallbackPrint(PVDSCRIPTARG paScriptArgs, void *pvUser) +{ + RTPrintf(paScriptArgs[0].psz); + return VINF_SUCCESS; +} + static void tstVDError(void *pvUser, int rc, RT_SRC_POS_DECL, const char *pszFormat, va_list va) { @@ -605,9 +519,10 @@ static PVDPATTERN tstVDIoGetPatternByName(PVDTESTGLOB pGlob, const char *pcszNam static PVDPATTERN tstVDIoPatternCreate(const char *pcszName, size_t cbPattern); static int tstVDIoPatternGetBuffer(PVDPATTERN pPattern, void **ppv, size_t cb); -static DECLCALLBACK(int) vdScriptHandlerCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs) +static DECLCALLBACK(int) vdScriptHandlerCreate(PVDSCRIPTARG paScriptArgs, void *pvUser) { int rc = VINF_SUCCESS; + PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser; uint64_t cbSize = 0; const char *pcszBackend = NULL; const char *pcszImage = NULL; @@ -616,69 +531,31 @@ static DECLCALLBACK(int) vdScriptHandlerCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG p bool fBase = false; bool fDynamic = true; bool fIgnoreFlush = false; + PVDIOBACKEND pIoBackend = NULL; - for (unsigned i = 0; i < cScriptArgs; i++) + pcszDisk = paScriptArgs[0].psz; + if (!RTStrICmp(paScriptArgs[1].psz, "base")) + fBase = true; + else if (!RTStrICmp(paScriptArgs[1].psz, "diff")) + fBase = false; + else { - switch (paScriptArgs[i].chId) - { - case 'd': - { - pcszDisk = paScriptArgs[i].u.pcszString; - break; - } - case 'm': - { - if (!RTStrICmp(paScriptArgs[i].u.pcszString, "base")) - fBase = true; - else if (!RTStrICmp(paScriptArgs[i].u.pcszString, "diff")) - fBase = false; - else - { - RTPrintf("Invalid image mode '%s' given\n", paScriptArgs[i].u.pcszString); - rc = VERR_INVALID_PARAMETER; - } - break; - } - case 'n': - { - pcszImage = paScriptArgs[i].u.pcszString; - break; - } - case 'b': - { - pcszBackend = paScriptArgs[i].u.pcszString; - break; - } - case 's': - { - cbSize = paScriptArgs[i].u.u64; - break; - } - case 't': - { - if (!RTStrICmp(paScriptArgs[i].u.pcszString, "fixed")) - fDynamic = false; - else if (!RTStrICmp(paScriptArgs[i].u.pcszString, "dynamic")) - fDynamic = true; - else - { - RTPrintf("Invalid image type '%s' given\n", paScriptArgs[i].u.pcszString); - rc = VERR_INVALID_PARAMETER; - } - break; - } - case 'f': - { - fIgnoreFlush = paScriptArgs[i].u.fFlag; - break; - } - default: - AssertMsgFailed(("Invalid argument given!\n")); - } - - if (RT_FAILURE(rc)) - break; + RTPrintf("Invalid image mode '%s' given\n", paScriptArgs[1].psz); + rc = VERR_INVALID_PARAMETER; } + pcszImage = paScriptArgs[2].psz; + if (!RTStrICmp(paScriptArgs[3].psz, "fixed")) + fDynamic = false; + else if (!RTStrICmp(paScriptArgs[3].psz, "dynamic")) + fDynamic = true; + else + { + RTPrintf("Invalid image type '%s' given\n", paScriptArgs[3].psz); + rc = VERR_INVALID_PARAMETER; + } + pcszBackend = paScriptArgs[4].psz; + cbSize = paScriptArgs[5].u64; + fIgnoreFlush = paScriptArgs[6].f; if (RT_SUCCESS(rc)) { @@ -709,9 +586,10 @@ static DECLCALLBACK(int) vdScriptHandlerCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG p return rc; } -static DECLCALLBACK(int) vdScriptHandlerOpen(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs) +static DECLCALLBACK(int) vdScriptHandlerOpen(PVDSCRIPTARG paScriptArgs, void *pvUser) { int rc = VINF_SUCCESS; + PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser; const char *pcszBackend = NULL; const char *pcszImage = NULL; const char *pcszDisk = NULL; @@ -722,52 +600,13 @@ static DECLCALLBACK(int) vdScriptHandlerOpen(PVDTESTGLOB pGlob, PVDSCRIPTARG paS bool fDiscard = false; bool fIgnoreFlush = false; - for (unsigned i = 0; i < cScriptArgs; i++) - { - switch (paScriptArgs[i].chId) - { - case 'd': - { - pcszDisk = paScriptArgs[i].u.pcszString; - break; - } - case 'n': - { - pcszImage = paScriptArgs[i].u.pcszString; - break; - } - case 'b': - { - pcszBackend = paScriptArgs[i].u.pcszString; - break; - } - case 's': - { - fShareable = paScriptArgs[i].u.fFlag; - break; - } - case 'r': - { - fReadonly = paScriptArgs[i].u.fFlag; - break; - } - case 'a': - { - fAsyncIo = paScriptArgs[i].u.fFlag; - break; - } - case 'i': - { - fDiscard = paScriptArgs[i].u.fFlag; - break; - } - default: - AssertMsgFailed(("Invalid argument given!\n")); - } - - if (RT_FAILURE(rc)) - break; - } + pcszDisk = paScriptArgs[0].psz; + pcszImage = paScriptArgs[1].psz; + pcszBackend = paScriptArgs[2].psz; + fShareable = paScriptArgs[3].f; + fReadonly = paScriptArgs[4].f; + fAsyncIo = paScriptArgs[5].f; + fDiscard = paScriptArgs[6].f; if (RT_SUCCESS(rc)) { @@ -796,9 +635,10 @@ static DECLCALLBACK(int) vdScriptHandlerOpen(PVDTESTGLOB pGlob, PVDSCRIPTARG paS return rc; } -static DECLCALLBACK(int) vdScriptHandlerIo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs) +static DECLCALLBACK(int) vdScriptHandlerIo(PVDSCRIPTARG paScriptArgs, void *pvUser) { int rc = VINF_SUCCESS; + PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser; bool fAsync = false; bool fRandomAcc = false; uint64_t cbIo = 0; @@ -814,71 +654,24 @@ static DECLCALLBACK(int) vdScriptHandlerIo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScr PVDDISK pDisk = NULL; PVDPATTERN pPattern = NULL; - for (unsigned i = 0; i < cScriptArgs; i++) + pcszDisk = paScriptArgs[0].psz; + fAsync = paScriptArgs[1].f; + cMaxReqs = paScriptArgs[2].u64; + if (!RTStrICmp(paScriptArgs[3].psz, "seq")) + fRandomAcc = false; + else if (!RTStrICmp(paScriptArgs[3].psz, "rnd")) + fRandomAcc = true; + else { - switch (paScriptArgs[i].chId) - { - case 'd': - { - pcszDisk = paScriptArgs[i].u.pcszString; - break; - } - case 'a': - { - fAsync = paScriptArgs[i].u.fFlag; - break; - } - case 'l': - { - cMaxReqs = paScriptArgs[i].u.u64; - break; - } - case 'm': - { - if (!RTStrICmp(paScriptArgs[i].u.pcszString, "seq")) - fRandomAcc = false; - else if (!RTStrICmp(paScriptArgs[i].u.pcszString, "rnd")) - fRandomAcc = true; - else - { - RTPrintf("Invalid access mode '%s'\n", paScriptArgs[i].u.pcszString); - rc = VERR_INVALID_PARAMETER; - } - break; - } - case 's': - { - cbIo = paScriptArgs[i].u.u64; - break; - } - case 'b': - { - cbBlkSize = paScriptArgs[i].u.u64; - break; - } - case 'o': - { - offStart = paScriptArgs[i].u.Range.Start; - offEnd = paScriptArgs[i].u.Range.End; - break; - } - case 'w': - { - uWriteChance = (uint8_t)paScriptArgs[i].u.u64; - break; - } - case 'p': - { - pcszPattern = paScriptArgs[i].u.pcszString; - break; - } - default: - AssertMsgFailed(("Invalid argument given!\n")); - } - - if (RT_FAILURE(rc)) - break; + RTPrintf("Invalid access mode '%s'\n", paScriptArgs[3].psz); + rc = VERR_INVALID_PARAMETER; } + cbBlkSize = paScriptArgs[4].u64; + offStart = paScriptArgs[5].u64; + offEnd = paScriptArgs[6].u64; + cbIo = paScriptArgs[7].u64; + uWriteChance = (uint8_t)paScriptArgs[8].u64; + pcszPattern = paScriptArgs[9].psz; if ( RT_SUCCESS(rc) && fAsync @@ -907,7 +700,7 @@ static DECLCALLBACK(int) vdScriptHandlerIo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScr } if ( RT_SUCCESS(rc) - && pcszPattern) + && RTStrCmp(pcszPattern, "none")) { pPattern = tstVDIoGetPatternByName(pGlob, pcszPattern); if (!pPattern) @@ -1144,35 +937,16 @@ static DECLCALLBACK(int) vdScriptHandlerIo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScr return rc; } -static DECLCALLBACK(int) vdScriptHandlerFlush(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs) +static DECLCALLBACK(int) vdScriptHandlerFlush(PVDSCRIPTARG paScriptArgs, void *pvUser) { int rc = VINF_SUCCESS; + PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser; bool fAsync = false; const char *pcszDisk = NULL; PVDDISK pDisk = NULL; - for (unsigned i = 0; i < cScriptArgs; i++) - { - switch (paScriptArgs[i].chId) - { - case 'd': - { - pcszDisk = paScriptArgs[i].u.pcszString; - break; - } - case 'a': - { - fAsync = paScriptArgs[i].u.fFlag; - break; - } - - default: - AssertMsgFailed(("Invalid argument given!\n")); - } - - if (RT_FAILURE(rc)) - break; - } + pcszDisk = paScriptArgs[0].psz; + fAsync = paScriptArgs[1].f; if (RT_SUCCESS(rc)) { @@ -1210,137 +984,70 @@ static DECLCALLBACK(int) vdScriptHandlerFlush(PVDTESTGLOB pGlob, PVDSCRIPTARG pa return rc; } -static DECLCALLBACK(int) vdScriptHandlerMerge(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs) +static DECLCALLBACK(int) vdScriptHandlerMerge(PVDSCRIPTARG paScriptArgs, void *pvUser) { int rc = VINF_SUCCESS; + PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser; const char *pcszDisk = NULL; PVDDISK pDisk = NULL; unsigned nImageFrom = 0; unsigned nImageTo = 0; - for (unsigned i = 0; i < cScriptArgs; i++) - { - switch (paScriptArgs[i].chId) - { - case 'd': - { - pcszDisk = paScriptArgs[i].u.pcszString; - break; - } - case 'f': - { - nImageFrom = (unsigned)paScriptArgs[i].u.u64; - break; - } - case 't': - { - nImageTo = (unsigned)paScriptArgs[i].u.u64; - break; - } - - default: - AssertMsgFailed(("Invalid argument given!\n")); - } + pcszDisk = paScriptArgs[0].psz; + nImageFrom = paScriptArgs[1].u32; + nImageTo = paScriptArgs[2].u32; - if (RT_FAILURE(rc)) - break; - } - - if (RT_SUCCESS(rc)) + pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk); + if (!pDisk) + rc = VERR_NOT_FOUND; + else { - pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk); - if (!pDisk) - rc = VERR_NOT_FOUND; - else - { - /** @todo: Provide progress interface to test that cancelation - * doesn't corrupt the data. - */ - rc = VDMerge(pDisk->pVD, nImageFrom, nImageTo, NULL); - } + /** @todo: Provide progress interface to test that cancelation + * doesn't corrupt the data. + */ + rc = VDMerge(pDisk->pVD, nImageFrom, nImageTo, NULL); } return rc; } -static DECLCALLBACK(int) vdScriptHandlerCompact(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs) +static DECLCALLBACK(int) vdScriptHandlerCompact(PVDSCRIPTARG paScriptArgs, void *pvUser) { int rc = VINF_SUCCESS; + PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser; const char *pcszDisk = NULL; PVDDISK pDisk = NULL; unsigned nImage = 0; - for (unsigned i = 0; i < cScriptArgs; i++) - { - switch (paScriptArgs[i].chId) - { - case 'd': - { - pcszDisk = paScriptArgs[i].u.pcszString; - break; - } - case 'i': - { - nImage = (unsigned)paScriptArgs[i].u.u64; - break; - } - - default: - AssertMsgFailed(("Invalid argument given!\n")); - } - - if (RT_FAILURE(rc)) - break; - } + pcszDisk = paScriptArgs[0].psz; + nImage = paScriptArgs[1].u32; - if (RT_SUCCESS(rc)) + pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk); + if (!pDisk) + rc = VERR_NOT_FOUND; + else { - pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk); - if (!pDisk) - rc = VERR_NOT_FOUND; - else - { - /** @todo: Provide progress interface to test that cancelation - * doesn't corrupt the data. - */ - rc = VDCompact(pDisk->pVD, nImage, NULL); - } + /** @todo: Provide progress interface to test that cancelation + * doesn't corrupt the data. + */ + rc = VDCompact(pDisk->pVD, nImage, NULL); } return rc; } -static DECLCALLBACK(int) vdScriptHandlerDiscard(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs) +static DECLCALLBACK(int) vdScriptHandlerDiscard(PVDSCRIPTARG paScriptArgs, void *pvUser) { int rc = VINF_SUCCESS; + PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser; const char *pcszDisk = NULL; PVDDISK pDisk = NULL; bool fAsync = false; const char *pcszRanges = NULL; - for (unsigned i = 0; i < cScriptArgs; i++) - { - switch (paScriptArgs[i].chId) - { - case 'd': - { - pcszDisk = paScriptArgs[i].u.pcszString; - break; - } - case 'a': - { - fAsync = paScriptArgs[i].u.fFlag; - break; - } - case 'r': - { - pcszRanges = paScriptArgs[i].u.pcszString; - break; - } - default: - AssertMsgFailed(("Invalid argument given!\n")); - } - } + pcszDisk = paScriptArgs[0].psz; + fAsync = paScriptArgs[1].f; + pcszRanges = paScriptArgs[2].psz; pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk); if (!pDisk) @@ -1546,9 +1253,10 @@ static DECLCALLBACK(int) vdScriptHandlerDiscard(PVDTESTGLOB pGlob, PVDSCRIPTARG return rc; } -static DECLCALLBACK(int) vdScriptHandlerCopy(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs) +static DECLCALLBACK(int) vdScriptHandlerCopy(PVDSCRIPTARG paScriptArgs, void *pvUser) { int rc = VINF_SUCCESS; + PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser; const char *pcszDiskFrom = NULL; const char *pcszDiskTo = NULL; PVDDISK pDiskFrom = NULL; @@ -1561,130 +1269,56 @@ static DECLCALLBACK(int) vdScriptHandlerCopy(PVDTESTGLOB pGlob, PVDSCRIPTARG paS unsigned nImageFromSame = VD_IMAGE_CONTENT_UNKNOWN; unsigned nImageToSame = VD_IMAGE_CONTENT_UNKNOWN; - for (unsigned i = 0; i < cScriptArgs; i++) - { - switch (paScriptArgs[i].chId) - { - case 's': - { - pcszDiskFrom = paScriptArgs[i].u.pcszString; - break; - } - case 'd': - { - pcszDiskTo = paScriptArgs[i].u.pcszString; - break; - } - case 'i': - { - nImageFrom = (unsigned)paScriptArgs[i].u.u64; - break; - } - case 'b': - { - pcszBackend = paScriptArgs[i].u.pcszString; - break; - } - case 'f': - { - pcszFilename = paScriptArgs[i].u.pcszString; - break; - } - case 'm': - { - fMoveByRename = paScriptArgs[i].u.fFlag; - break; - } - case 'z': - { - cbSize = paScriptArgs[i].u.u64; - break; - } - case 'o': - { - nImageFromSame = (unsigned)paScriptArgs[i].u.u64; - break; - } - case 't': - { - nImageToSame = (unsigned)paScriptArgs[i].u.u64; - break; - } - - default: - AssertMsgFailed(("Invalid argument given!\n")); - } - - if (RT_FAILURE(rc)) - break; - } - - if (RT_SUCCESS(rc)) + pcszDiskFrom = paScriptArgs[0].psz; + pcszDiskTo = paScriptArgs[1].psz; + nImageFrom = paScriptArgs[2].u32; + pcszBackend = paScriptArgs[3].psz; + pcszFilename = paScriptArgs[4].psz; + fMoveByRename = paScriptArgs[5].f; + cbSize = paScriptArgs[6].u64; + nImageFromSame = paScriptArgs[7].u32; + nImageToSame = paScriptArgs[8].u32; + + pDiskFrom = tstVDIoGetDiskByName(pGlob, pcszDiskFrom); + pDiskTo = tstVDIoGetDiskByName(pGlob, pcszDiskTo); + if (!pDiskFrom || !pDiskTo) + rc = VERR_NOT_FOUND; + else { - pDiskFrom = tstVDIoGetDiskByName(pGlob, pcszDiskFrom); - pDiskTo = tstVDIoGetDiskByName(pGlob, pcszDiskTo); - if (!pDiskFrom || !pDiskTo) - rc = VERR_NOT_FOUND; - else - { - /** @todo: Provide progress interface to test that cancelation - * works as intended. - */ - rc = VDCopyEx(pDiskFrom->pVD, nImageFrom, pDiskTo->pVD, pcszBackend, pcszFilename, - fMoveByRename, cbSize, nImageFromSame, nImageToSame, - VD_IMAGE_FLAGS_NONE, NULL, VD_OPEN_FLAGS_ASYNC_IO, - NULL, pGlob->pInterfacesImages, NULL); - } + /** @todo: Provide progress interface to test that cancelation + * works as intended. + */ + rc = VDCopyEx(pDiskFrom->pVD, nImageFrom, pDiskTo->pVD, pcszBackend, pcszFilename, + fMoveByRename, cbSize, nImageFromSame, nImageToSame, + VD_IMAGE_FLAGS_NONE, NULL, VD_OPEN_FLAGS_ASYNC_IO, + NULL, pGlob->pInterfacesImages, NULL); } return rc; } -static DECLCALLBACK(int) vdScriptHandlerClose(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs) +static DECLCALLBACK(int) vdScriptHandlerClose(PVDSCRIPTARG paScriptArgs, void *pvUser) { int rc = VINF_SUCCESS; + PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser; bool fAll = false; bool fDelete = false; const char *pcszDisk = NULL; PVDDISK pDisk = NULL; - for (unsigned i = 0; i < cScriptArgs; i++) + pcszDisk = paScriptArgs[0].psz; + if (!RTStrICmp(paScriptArgs[1].psz, "all")) + fAll = true; + else if (!RTStrICmp(paScriptArgs[1].psz, "single")) + fAll = false; + else { - switch (paScriptArgs[i].chId) - { - case 'd': - { - pcszDisk = paScriptArgs[i].u.pcszString; - break; - } - case 'm': - { - if (!RTStrICmp(paScriptArgs[i].u.pcszString, "all")) - fAll = true; - else if (!RTStrICmp(paScriptArgs[i].u.pcszString, "single")) - fAll = false; - else - { - RTPrintf("Invalid mode '%s' given\n", paScriptArgs[i].u.pcszString); - rc = VERR_INVALID_PARAMETER; - } - break; - } - case 'r': - { - fDelete = paScriptArgs[i].u.fFlag; - break; - } - default: - AssertMsgFailed(("Invalid argument given!\n")); - } - - if (RT_FAILURE(rc)) - break; + RTPrintf("Invalid mode '%s' given\n", paScriptArgs[1].psz); + rc = VERR_INVALID_PARAMETER; } + fDelete = paScriptArgs[2].f; - if ( RT_SUCCESS(rc) - && fAll + if ( fAll && fDelete) { RTPrintf("mode=all doesn't work with delete=yes\n"); @@ -1708,31 +1342,16 @@ static DECLCALLBACK(int) vdScriptHandlerClose(PVDTESTGLOB pGlob, PVDSCRIPTARG pa } -static DECLCALLBACK(int) vdScriptHandlerPrintFileSize(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs) +static DECLCALLBACK(int) vdScriptHandlerPrintFileSize(PVDSCRIPTARG paScriptArgs, void *pvUser) { int rc = VINF_SUCCESS; + PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser; const char *pcszDisk = NULL; PVDDISK pDisk = NULL; unsigned nImage = 0; - for (unsigned i = 0; i < cScriptArgs; i++) - { - switch (paScriptArgs[i].chId) - { - case 'd': - { - pcszDisk = paScriptArgs[i].u.pcszString; - break; - } - case 'i': - { - nImage = (unsigned)paScriptArgs[i].u.u64; - break; - } - default: - AssertMsgFailed(("Invalid argument given!\n")); - } - } + pcszDisk = paScriptArgs[0].psz; + nImage = paScriptArgs[1].u32; pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk); if (pDisk) @@ -1744,31 +1363,16 @@ static DECLCALLBACK(int) vdScriptHandlerPrintFileSize(PVDTESTGLOB pGlob, PVDSCRI } -static DECLCALLBACK(int) vdScriptHandlerIoLogReplay(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs) +static DECLCALLBACK(int) vdScriptHandlerIoLogReplay(PVDSCRIPTARG paScriptArgs, void *pvUser) { int rc = VINF_SUCCESS; + PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser; const char *pcszDisk = NULL; PVDDISK pDisk = NULL; const char *pcszIoLog = NULL; - for (unsigned i = 0; i < cScriptArgs; i++) - { - switch (paScriptArgs[i].chId) - { - case 'd': - { - pcszDisk = paScriptArgs[i].u.pcszString; - break; - } - case 'i': - { - pcszIoLog = paScriptArgs[i].u.pcszString; - break; - } - default: - AssertMsgFailed(("Invalid argument given!\n")); - } - } + pcszDisk = paScriptArgs[0].psz; + pcszIoLog = paScriptArgs[1].psz; pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk); if (pDisk) @@ -1911,36 +1515,17 @@ static DECLCALLBACK(int) vdScriptHandlerIoLogReplay(PVDTESTGLOB pGlob, PVDSCRIPT } -static DECLCALLBACK(int) vdScriptHandlerIoRngCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs) +static DECLCALLBACK(int) vdScriptHandlerIoRngCreate(PVDSCRIPTARG paScriptArgs, void *pvUser) { int rc = VINF_SUCCESS; + PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser; size_t cbPattern = 0; uint64_t uSeed = 0; const char *pcszSeeder = NULL; - for (unsigned i = 0; i < cScriptArgs; i++) - { - switch (paScriptArgs[i].chId) - { - case 'd': - { - cbPattern = paScriptArgs[i].u.u64; - break; - } - case 's': - { - uSeed = paScriptArgs[i].u.u64; - break; - } - case 'm': - { - pcszSeeder = paScriptArgs[i].u.pcszString; - break; - } - default: - AssertMsgFailed(("Invalid argument given!\n")); - } - } + cbPattern = paScriptArgs[0].u64; + pcszSeeder = paScriptArgs[1].psz; + uSeed = paScriptArgs[2].u64; if (pGlob->pIoRnd) { @@ -1973,8 +1558,10 @@ static DECLCALLBACK(int) vdScriptHandlerIoRngCreate(PVDTESTGLOB pGlob, PVDSCRIPT return rc; } -static DECLCALLBACK(int) vdScriptHandlerIoRngDestroy(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs) +static DECLCALLBACK(int) vdScriptHandlerIoRngDestroy(PVDSCRIPTARG paScriptArgs, void *pvUser) { + PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser; + if (pGlob->pIoRnd) { VDIoRndDestroy(pGlob->pIoRnd); @@ -1986,36 +1573,17 @@ static DECLCALLBACK(int) vdScriptHandlerIoRngDestroy(PVDTESTGLOB pGlob, PVDSCRIP return VINF_SUCCESS; } -static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromNumber(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs) +static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromNumber(PVDSCRIPTARG paScriptArgs, void *pvUser) { int rc = VINF_SUCCESS; + PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser; size_t cbPattern = 0; const char *pcszName = NULL; uint64_t u64Pattern = 0; - for (unsigned i = 0; i < cScriptArgs; i++) - { - switch (paScriptArgs[i].chId) - { - case 'n': - { - pcszName = paScriptArgs[i].u.pcszString; - break; - } - case 's': - { - cbPattern = paScriptArgs[i].u.u64; - break; - } - case 'p': - { - u64Pattern = paScriptArgs[i].u.u64; - break; - } - default: - AssertMsgFailed(("Invalid argument given!\n")); - } - } + pcszName = paScriptArgs[0].psz; + cbPattern = paScriptArgs[1].u64; + u64Pattern = paScriptArgs[2].u64; PVDPATTERN pPattern = tstVDIoGetPatternByName(pGlob, pcszName); if (!pPattern) @@ -2045,30 +1613,15 @@ static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromNumber(PVDTESTGLOB pG return rc; } -static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromFile(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs) +static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromFile(PVDSCRIPTARG paScriptArgs, void *pvUser) { int rc = VINF_SUCCESS; + PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser; const char *pcszName = NULL; const char *pcszFile = NULL; - for (unsigned i = 0; i < cScriptArgs; i++) - { - switch (paScriptArgs[i].chId) - { - case 'n': - { - pcszName = paScriptArgs[i].u.pcszString; - break; - } - case 'f': - { - pcszFile = paScriptArgs[i].u.pcszString; - break; - } - default: - AssertMsgFailed(("Invalid argument given!\n")); - } - } + pcszName = paScriptArgs[0].psz; + pcszFile = paScriptArgs[1].psz; PVDPATTERN pPattern = tstVDIoGetPatternByName(pGlob, pcszName); if (!pPattern) @@ -2107,24 +1660,13 @@ static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromFile(PVDTESTGLOB pGlo return rc; } -static DECLCALLBACK(int) vdScriptHandlerIoPatternDestroy(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs) +static DECLCALLBACK(int) vdScriptHandlerIoPatternDestroy(PVDSCRIPTARG paScriptArgs, void *pvUser) { int rc = VINF_SUCCESS; + PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser; const char *pcszName = NULL; - for (unsigned i = 0; i < cScriptArgs; i++) - { - switch (paScriptArgs[i].chId) - { - case 'n': - { - pcszName = paScriptArgs[i].u.pcszString; - break; - } - default: - AssertMsgFailed(("Invalid argument given!\n")); - } - } + pcszName = paScriptArgs[0].psz; PVDPATTERN pPattern = tstVDIoGetPatternByName(pGlob, pcszName); if (pPattern) @@ -2140,53 +1682,24 @@ static DECLCALLBACK(int) vdScriptHandlerIoPatternDestroy(PVDTESTGLOB pGlob, PVDS return rc; } -static DECLCALLBACK(int) vdScriptHandlerSleep(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs) +static DECLCALLBACK(int) vdScriptHandlerSleep(PVDSCRIPTARG paScriptArgs, void *pvUser) { int rc = VINF_SUCCESS; - uint64_t cMillies = 0; - - for (unsigned i = 0; i < cScriptArgs; i++) - { - switch (paScriptArgs[i].chId) - { - case 't': - { - cMillies = paScriptArgs[i].u.u64; - break; - } - default: - AssertMsgFailed(("Invalid argument given!\n")); - } - } + uint64_t cMillies = paScriptArgs[0].u64; rc = RTThreadSleep(cMillies); return rc; } -static DECLCALLBACK(int) vdScriptHandlerDumpFile(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs) +static DECLCALLBACK(int) vdScriptHandlerDumpFile(PVDSCRIPTARG paScriptArgs, void *pvUser) { int rc = VINF_SUCCESS; + PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser; const char *pcszFile = NULL; const char *pcszPathToDump = NULL; - for (unsigned i = 0; i < cScriptArgs; i++) - { - switch (paScriptArgs[i].chId) - { - case 'f': - { - pcszFile = paScriptArgs[i].u.pcszString; - break; - } - case 'p': - { - pcszPathToDump = paScriptArgs[i].u.pcszString; - break; - } - default: - AssertMsgFailed(("Invalid argument given!\n")); - } - } + pcszFile = paScriptArgs[0].psz; + pcszPathToDump = paScriptArgs[1].psz; /* Check for the file. */ PVDFILE pIt = NULL; @@ -2203,7 +1716,8 @@ static DECLCALLBACK(int) vdScriptHandlerDumpFile(PVDTESTGLOB pGlob, PVDSCRIPTARG if (fFound) { RTPrintf("Dumping memory file %s to %s, this might take some time\n", pcszFile, pcszPathToDump); - rc = VDMemDiskWriteToFile(pIt->pMemDisk, pcszPathToDump); + //rc = VDMemDiskWriteToFile(pIt->pIo, pcszPathToDump); + rc = VERR_NOT_IMPLEMENTED; } else rc = VERR_FILE_NOT_FOUND; @@ -2211,31 +1725,16 @@ static DECLCALLBACK(int) vdScriptHandlerDumpFile(PVDTESTGLOB pGlob, PVDSCRIPTARG return rc; } -static DECLCALLBACK(int) vdScriptHandlerCreateDisk(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs) +static DECLCALLBACK(int) vdScriptHandlerCreateDisk(PVDSCRIPTARG paScriptArgs, void *pvUser) { int rc = VINF_SUCCESS; + PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser; const char *pcszDisk = NULL; PVDDISK pDisk = NULL; bool fVerify = false; - for (unsigned i = 0; i < cScriptArgs; i++) - { - switch (paScriptArgs[i].chId) - { - case 'n': - { - pcszDisk = paScriptArgs[i].u.pcszString; - break; - } - case 'v': - { - fVerify = paScriptArgs[i].u.fFlag; - break; - } - default: - AssertMsgFailed(("Invalid argument given!\n")); - } - } + pcszDisk = paScriptArgs[0].psz; + fVerify = paScriptArgs[1].f; pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk); if (pDisk) @@ -2290,25 +1789,14 @@ static DECLCALLBACK(int) vdScriptHandlerCreateDisk(PVDTESTGLOB pGlob, PVDSCRIPTA return rc; } -static DECLCALLBACK(int) vdScriptHandlerDestroyDisk(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs) +static DECLCALLBACK(int) vdScriptHandlerDestroyDisk(PVDSCRIPTARG paScriptArgs, void *pvUser) { int rc = VINF_SUCCESS; + PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser; const char *pcszDisk = NULL; PVDDISK pDisk = NULL; - for (unsigned i = 0; i < cScriptArgs; i++) - { - switch (paScriptArgs[i].chId) - { - case 'n': - { - pcszDisk = paScriptArgs[i].u.pcszString; - break; - } - default: - AssertMsgFailed(("Invalid argument given!\n")); - } - } + pcszDisk = paScriptArgs[0].psz; pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk); if (pDisk) @@ -2329,32 +1817,17 @@ static DECLCALLBACK(int) vdScriptHandlerDestroyDisk(PVDTESTGLOB pGlob, PVDSCRIPT return rc; } -static DECLCALLBACK(int) vdScriptHandlerCompareDisks(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs) +static DECLCALLBACK(int) vdScriptHandlerCompareDisks(PVDSCRIPTARG paScriptArgs, void *pvUser) { int rc = VINF_SUCCESS; + PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser; const char *pcszDisk1 = NULL; PVDDISK pDisk1 = NULL; const char *pcszDisk2 = NULL; PVDDISK pDisk2 = NULL; - for (unsigned i = 0; i < cScriptArgs; i++) - { - switch (paScriptArgs[i].chId) - { - case '1': - { - pcszDisk1 = paScriptArgs[i].u.pcszString; - break; - } - case '2': - { - pcszDisk2 = paScriptArgs[i].u.pcszString; - break; - } - default: - AssertMsgFailed(("Invalid argument given!\n")); - } - } + pcszDisk1 = paScriptArgs[0].psz; + pcszDisk2 = paScriptArgs[1].psz; pDisk1 = tstVDIoGetDiskByName(pGlob, pcszDisk1); pDisk2 = tstVDIoGetDiskByName(pGlob, pcszDisk2); @@ -2420,25 +1893,14 @@ static DECLCALLBACK(int) vdScriptHandlerCompareDisks(PVDTESTGLOB pGlob, PVDSCRIP return rc; } -static DECLCALLBACK(int) vdScriptHandlerDumpDiskInfo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs) +static DECLCALLBACK(int) vdScriptHandlerDumpDiskInfo(PVDSCRIPTARG paScriptArgs, void *pvUser) { int rc = VINF_SUCCESS; + PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser; const char *pcszDisk = NULL; PVDDISK pDisk = NULL; - for (unsigned i = 0; i < cScriptArgs; i++) - { - switch (paScriptArgs[i].chId) - { - case 'd': - { - pcszDisk = paScriptArgs[i].u.pcszString; - break; - } - default: - AssertMsgFailed(("Invalid argument given!\n")); - } - } + pcszDisk = paScriptArgs[0].psz; pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk); @@ -2450,30 +1912,17 @@ static DECLCALLBACK(int) vdScriptHandlerDumpDiskInfo(PVDTESTGLOB pGlob, PVDSCRIP return rc; } -static DECLCALLBACK(int) vdScriptHandlerPrintMsg(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs) +static DECLCALLBACK(int) vdScriptHandlerPrintMsg(PVDSCRIPTARG paScriptArgs, void *pvUser) { - RTPrintf("%s\n", paScriptArgs[0].u.pcszString); + RTPrintf("%s\n", paScriptArgs[0].psz); return VINF_SUCCESS; } -static DECLCALLBACK(int) vdScriptHandlerShowStatistics(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs) +static DECLCALLBACK(int) vdScriptHandlerShowStatistics(PVDSCRIPTARG paScriptArgs, void *pvUser) { int rc = VINF_SUCCESS; - const char *pcszFile = NULL; - - for (unsigned i = 0; i < cScriptArgs; i++) - { - switch (paScriptArgs[i].chId) - { - case 'f': - { - pcszFile = paScriptArgs[i].u.pcszString; - break; - } - default: - AssertMsgFailed(("Invalid argument given!\n")); - } - } + PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser; + const char *pcszFile = paScriptArgs[0].psz; /* Check for the file. */ PVDFILE pIt = NULL; @@ -2502,24 +1951,11 @@ static DECLCALLBACK(int) vdScriptHandlerShowStatistics(PVDTESTGLOB pGlob, PVDSCR return rc; } -static DECLCALLBACK(int) vdScriptHandlerResetStatistics(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs) +static DECLCALLBACK(int) vdScriptHandlerResetStatistics(PVDSCRIPTARG paScriptArgs, void *pvUser) { int rc = VINF_SUCCESS; - const char *pcszFile = NULL; - - for (unsigned i = 0; i < cScriptArgs; i++) - { - switch (paScriptArgs[i].chId) - { - case 'f': - { - pcszFile = paScriptArgs[i].u.pcszString; - break; - } - default: - AssertMsgFailed(("Invalid argument given!\n")); - } - } + PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser; + const char *pcszFile = paScriptArgs[0].psz; /* Check for the file. */ PVDFILE pIt = NULL; @@ -2549,6 +1985,39 @@ static DECLCALLBACK(int) vdScriptHandlerResetStatistics(PVDTESTGLOB pGlob, PVDSC return rc; } +static DECLCALLBACK(int) vdScriptHandlerResize(PVDSCRIPTARG paScriptArgs, void *pvUser) +{ + int rc = VINF_SUCCESS; + PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser; + const char *pcszDisk = paScriptArgs[0].psz; + uint64_t cbDiskNew = 0; + PVDDISK pDisk = NULL; + + pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk); + if (pDisk) + { + rc = VDResize(pDisk->pVD, cbDiskNew, &pDisk->PhysGeom, &pDisk->LogicalGeom, NULL); + } + else + rc = VERR_NOT_FOUND; + + return rc; +} + +static DECLCALLBACK(int) vdScriptHandlerSetFileBackend(PVDSCRIPTARG paScriptArgs, void *pvUser) +{ + int rc = VINF_SUCCESS; + PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser; + const char *pcszBackend = paScriptArgs[0].psz; + + RTStrFree(pGlob->pszIoBackend); + pGlob->pszIoBackend = RTStrDup(pcszBackend); + if (!pGlob->pszIoBackend) + rc = VERR_NO_MEMORY; + + return rc; +} + static DECLCALLBACK(int) tstVDIoFileOpen(void *pvUser, const char *pszLocation, uint32_t fOpen, PFNVDCOMPLETED pfnCompleted, @@ -2583,7 +2052,7 @@ static DECLCALLBACK(int) tstVDIoFileOpen(void *pvUser, const char *pszLocation, { /* If the file exists delete the memory disk. */ if (fFound) - rc = VDMemDiskSetSize(pIt->pMemDisk, 0); + rc = VDIoBackendStorageSetSize(pIt->pIoStorage, 0); else { /* Create completey new. */ @@ -2594,7 +2063,8 @@ static DECLCALLBACK(int) tstVDIoFileOpen(void *pvUser, const char *pszLocation, if (pIt->pszName) { - rc = VDMemDiskCreate(&pIt->pMemDisk, 0); + rc = VDIoBackendStorageCreate(pGlob->pIoBackend, pGlob->pszIoBackend, + pszLocation, pfnCompleted, &pIt->pIoStorage); } else rc = VERR_NO_MEMORY; @@ -2605,11 +2075,11 @@ static DECLCALLBACK(int) tstVDIoFileOpen(void *pvUser, const char *pszLocation, RTStrFree(pIt->pszName); RTMemFree(pIt); } + else + RTListAppend(&pGlob->ListFiles, &pIt->Node); } else rc = VERR_NO_MEMORY; - - RTListAppend(&pGlob->ListFiles, &pIt->Node); } } else if ((fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN) @@ -2673,7 +2143,7 @@ static DECLCALLBACK(int) tstVDIoFileDelete(void *pvUser, const char *pcszFilenam if (fFound) { RTListNodeRemove(&pIt->Node); - VDMemDiskDestroy(pIt->pMemDisk); + VDIoBackendStorageDestroy(pIt->pIoStorage); RTStrFree(pIt->pszName); RTMemFree(pIt); } @@ -2737,14 +2207,14 @@ static DECLCALLBACK(int) tstVDIoFileGetSize(void *pvUser, void *pStorage, uint64 { PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage; - return VDMemDiskGetSize(pIoStorage->pFile->pMemDisk, pcbSize); + return VDIoBackendStorageGetSize(pIoStorage->pFile->pIoStorage, pcbSize); } static DECLCALLBACK(int) tstVDIoFileSetSize(void *pvUser, void *pStorage, uint64_t cbSize) { PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage; - return VDMemDiskSetSize(pIoStorage->pFile->pMemDisk, cbSize); + return VDIoBackendStorageSetSize(pIoStorage->pFile->pIoStorage, cbSize); } static DECLCALLBACK(int) tstVDIoFileWriteSync(void *pvUser, void *pStorage, uint64_t uOffset, @@ -2759,7 +2229,8 @@ static DECLCALLBACK(int) tstVDIoFileWriteSync(void *pvUser, void *pStorage, uint Seg.pvSeg = (void *)pvBuffer; Seg.cbSeg = cbBuffer; RTSgBufInit(&SgBuf, &Seg, 1); - rc = VDMemDiskWrite(pIoStorage->pFile->pMemDisk, uOffset, cbBuffer, &SgBuf); + rc = VDIoBackendTransfer(pIoStorage->pFile->pIoStorage, VDIOTXDIR_WRITE, uOffset, + cbBuffer, &SgBuf, NULL, true /* fSync */); if (RT_SUCCESS(rc)) { pIoStorage->pFile->cWrites++; @@ -2782,7 +2253,8 @@ static DECLCALLBACK(int) tstVDIoFileReadSync(void *pvUser, void *pStorage, uint6 Seg.pvSeg = pvBuffer; Seg.cbSeg = cbBuffer; RTSgBufInit(&SgBuf, &Seg, 1); - rc = VDMemDiskRead(pIoStorage->pFile->pMemDisk, uOffset, cbBuffer, &SgBuf); + rc = VDIoBackendTransfer(pIoStorage->pFile->pIoStorage, VDIOTXDIR_READ, uOffset, + cbBuffer, &SgBuf, NULL, true /* fSync */); if (RT_SUCCESS(rc)) { pIoStorage->pFile->cReads++; @@ -2796,8 +2268,10 @@ static DECLCALLBACK(int) tstVDIoFileReadSync(void *pvUser, void *pStorage, uint6 static DECLCALLBACK(int) tstVDIoFileFlushSync(void *pvUser, void *pStorage) { PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage; + int rc = VDIoBackendTransfer(pIoStorage->pFile->pIoStorage, VDIOTXDIR_FLUSH, 0, + 0, NULL, NULL, true /* fSync */); pIoStorage->pFile->cFlushes++; - return VINF_SUCCESS; + return rc; } static DECLCALLBACK(int) tstVDIoFileReadAsync(void *pvUser, void *pStorage, uint64_t uOffset, @@ -2808,9 +2282,11 @@ static DECLCALLBACK(int) tstVDIoFileReadAsync(void *pvUser, void *pStorage, uint int rc = VINF_SUCCESS; PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser; PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage; + RTSGBUF SgBuf; - rc = VDIoBackendMemTransfer(pGlob->pIoBackend, pIoStorage->pFile->pMemDisk, VDIOTXDIR_READ, uOffset, - cbRead, paSegments, cSegments, pIoStorage->pfnComplete, pvCompletion); + RTSgBufInit(&SgBuf, paSegments, cSegments); + rc = VDIoBackendTransfer(pIoStorage->pFile->pIoStorage, VDIOTXDIR_READ, uOffset, + cbRead, &SgBuf, pvCompletion, false /* fSync */); if (RT_SUCCESS(rc)) { pIoStorage->pFile->cAsyncReads++; @@ -2828,9 +2304,11 @@ static DECLCALLBACK(int) tstVDIoFileWriteAsync(void *pvUser, void *pStorage, uin int rc = VINF_SUCCESS; PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser; PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage; + RTSGBUF SgBuf; - rc = VDIoBackendMemTransfer(pGlob->pIoBackend, pIoStorage->pFile->pMemDisk, VDIOTXDIR_WRITE, uOffset, - cbWrite, paSegments, cSegments, pIoStorage->pfnComplete, pvCompletion); + RTSgBufInit(&SgBuf, paSegments, cSegments); + rc = VDIoBackendTransfer(pIoStorage->pFile->pIoStorage, VDIOTXDIR_WRITE, uOffset, + cbWrite, &SgBuf, pvCompletion, false /* fSync */); if (RT_SUCCESS(rc)) { pIoStorage->pFile->cAsyncWrites++; @@ -2847,8 +2325,8 @@ static DECLCALLBACK(int) tstVDIoFileFlushAsync(void *pvUser, void *pStorage, voi PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser; PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage; - rc = VDIoBackendMemTransfer(pGlob->pIoBackend, pIoStorage->pFile->pMemDisk, VDIOTXDIR_FLUSH, 0, - 0, NULL, 0, pIoStorage->pfnComplete, pvCompletion); + rc = VDIoBackendTransfer(pIoStorage->pFile->pIoStorage, VDIOTXDIR_FLUSH, 0, + 0, NULL, pvCompletion, false /* fSync */); if (RT_SUCCESS(rc)) { pIoStorage->pFile->cAsyncFlushes++; @@ -3169,454 +2647,7 @@ static int tstVDIoPatternGetBuffer(PVDPATTERN pPattern, void **ppv, size_t cb) } /** - * Skips the characters until the given character is reached. - * - * @returns Start of the string with the given character - * or NULL if the string ended before. - * - * @param psz The string to skip. - * @param ch The character. - */ -static char *tstVDIoScriptSkipUntil(char *psz, char ch) -{ - while ( *psz != '\0' - && *psz != ch) - psz++; - - return psz; -} - -/** - * Skips the spaces of the current string. - * - * @returns Start of the string with a non space character - * or NULL if the string ended before. - * - * @param psz The string to skip. - */ -static char *tstVDIoScriptSkipSpace(char *psz) -{ - while ( *psz != '\0' - && RT_C_IS_SPACE(*psz)) - psz++; - - return psz; -} - -/** - * Skips all characters until a space is reached of the current - * string. - * - * @returns Start of the string with a space character - * or NULL if the string ended before. - * - * @param psz The string to skip. - */ -static char *tstVDIoScriptSkipNonSpace(char *psz) -{ - while ( *psz != '\0' - && !RT_C_IS_SPACE(*psz)) - psz++; - - return psz; -} - -/** - * Returns true if the first character of the given string - * contains a character marking a line end (comment or \0 - * terminator). - * - * @returns true if the line contains no more characters of - * interest and false otherwise. - * - * @param psz The string to check for. - */ -static bool tstVDIoIsLineEnd(const char *psz) -{ - return *psz == '\0' || *psz == '#'; -} - -/** - * Parses one argument name, value pair. - * - * @returns IPRT status code. - * - * @param pVDScriptAction Script action. - * @param pcszName Argument name. - * @param pcszValue Argument value. - * @param pScriptArg Where to fill in the parsed - * argument. - * @param pfMandatory Where to store whether the argument - * is mandatory. - */ -static int tstVDIoScriptArgumentParse(PCVDSCRIPTACTION pVDScriptAction, const char *pcszName, - const char *pcszValue, PVDSCRIPTARG pScriptArg, bool *pfMandatory) -{ - int rc = VERR_NOT_FOUND; - - for (unsigned i = 0; i < pVDScriptAction->cArgDescs; i++) - { - if (!RTStrCmp(pVDScriptAction->paArgDesc[i].pcszName, pcszName)) - { - rc = VINF_SUCCESS; - - switch (pVDScriptAction->paArgDesc[i].enmType) - { - case VDSCRIPTARGTYPE_BOOL: - { - pScriptArg->enmType = VDSCRIPTARGTYPE_BOOL; - if (!RTStrICmp(pcszValue, "yes") || !RTStrICmp(pcszValue, "on")) - pScriptArg->u.fFlag = true; - else if (!RTStrICmp(pcszValue, "no") || !RTStrICmp(pcszValue, "off")) - pScriptArg->u.fFlag = false; - else - { - RTPrintf("Boolean argument malformed '%s'\n", pcszValue); - rc = VERR_INVALID_PARAMETER; - } - break; - } - case VDSCRIPTARGTYPE_SIGNED_NUMBER: - { - pScriptArg->enmType = VDSCRIPTARGTYPE_SIGNED_NUMBER; - AssertMsgFailed(("todo\n")); - break; - } - case VDSCRIPTARGTYPE_STRING: - { - pScriptArg->enmType = VDSCRIPTARGTYPE_STRING; - pScriptArg->u.pcszString = pcszValue; - break; - } - case VDSCRIPTARGTYPE_UNSIGNED_NUMBER: - { - char *pszSuffix = NULL; - - pScriptArg->enmType = VDSCRIPTARGTYPE_UNSIGNED_NUMBER; - rc = RTStrToUInt64Ex(pcszValue, &pszSuffix, 10, &pScriptArg->u.u64); - if (rc == VWRN_TRAILING_CHARS) - { - switch (*pszSuffix) - { - case 'k': - case 'K': - { - pScriptArg->u.u64 *= _1K; - break; - } - case 'm': - case 'M': - { - pScriptArg->u.u64 *= _1M; - break; - } - case 'g': - case 'G': - { - pScriptArg->u.u64 *= _1G; - break; - } - default: - { - RTPrintf("Invalid size suffix '%s'\n", pszSuffix); - rc = VERR_INVALID_PARAMETER; - } - } - if (rc != VERR_INVALID_PARAMETER) - rc = VINF_SUCCESS; - } - - break; - } - case VDSCRIPTARGTYPE_UNSIGNED_RANGE: - { - char *pszSuffix = NULL; - - pScriptArg->enmType = VDSCRIPTARGTYPE_UNSIGNED_RANGE; - rc = RTStrToUInt64Ex(pcszValue, &pszSuffix, 10, &pScriptArg->u.Range.Start); - if (rc == VWRN_TRAILING_CHARS) - { - if (*pszSuffix != '-') - { - switch (*pszSuffix) - { - case 'k': - case 'K': - { - pScriptArg->u.u64 *= _1K; - break; - } - case 'm': - case 'M': - { - pScriptArg->u.u64 *= _1M; - break; - } - case 'g': - case 'G': - { - pScriptArg->u.u64 *= _1G; - break; - } - default: - { - RTPrintf("Invalid size suffix '%s'\n", pszSuffix); - rc = VERR_INVALID_PARAMETER; - } - } - if (RT_SUCCESS(rc)) - pszSuffix++; - } - - if (*pszSuffix == '-') - { - pszSuffix++; - rc = RTStrToUInt64Ex(pszSuffix, &pszSuffix, 10, &pScriptArg->u.Range.End); - if (rc == VWRN_TRAILING_CHARS) - { - switch (*pszSuffix) - { - case 'k': - case 'K': - { - pScriptArg->u.Range.End *= _1K; - break; - } - case 'm': - case 'M': - { - pScriptArg->u.Range.End *= _1M; - break; - } - case 'g': - case 'G': - { - pScriptArg->u.Range.End *= _1G; - break; - } - default: - { - RTPrintf("Invalid size suffix '%s'\n", pszSuffix); - rc = VERR_INVALID_PARAMETER; - } - } - } - } - else - rc = VERR_INVALID_PARAMETER; - } - else - rc = VERR_INVALID_PARAMETER; - - if (rc == VERR_INVALID_PARAMETER) - RTPrintf("Invalid range format\n"); - break; - } - default: - AssertMsgFailed(("Invalid script argument type\n")); - } - - if (RT_SUCCESS(rc)) - { - pScriptArg->chId = pVDScriptAction->paArgDesc[i].chId; - *pfMandatory = !!(pVDScriptAction->paArgDesc[i].fFlags & VDSCRIPTARGDESC_FLAG_MANDATORY); - } - break; - } - } - - if (rc == VERR_NOT_FOUND) - RTPrintf("Argument '%s' not found\n", pcszName); - - return rc; -} - -/** - * Parses the arguments of a action in the script. - * - * @returns IPRT status code. - * - * @param psz Argument string. - * @param pVDScriptAction The script action to parses - * arguments for. - * @param paScriptArgs Where to store the arguments. - * @param pcScriptArgs Where to store the actual number of - * arguments parsed. - */ -static int tstVDIoScriptArgumentListParse(char *psz, PCVDSCRIPTACTION pVDScriptAction, PVDSCRIPTARG paScriptArgs, unsigned *pcScriptArgs) -{ - int rc = VINF_SUCCESS; - unsigned cMandatoryArgsReq = 0; - unsigned cScriptArgs = 0; - - /* Count the number of mandatory arguments first. */ - for (unsigned i = 0; i < pVDScriptAction->cArgDescs; i++) - if (pVDScriptAction->paArgDesc[i].fFlags & VDSCRIPTARGDESC_FLAG_MANDATORY) - cMandatoryArgsReq++; - - /* One argument is given in the form name=value. */ - *pcScriptArgs = 0; - - while ( psz - && !tstVDIoIsLineEnd(psz)) - { - const char *pcszName = psz; - - psz = tstVDIoScriptSkipUntil(psz, '='); - if (!tstVDIoIsLineEnd(psz)) - { - *psz = '\0'; /* Overwrite */ - psz++; - const char *pcszValue = psz; - - psz = tstVDIoScriptSkipNonSpace(psz); - if (!tstVDIoIsLineEnd(psz)) - { - *psz = '\0'; /* Overwrite */ - psz++; - psz = tstVDIoScriptSkipSpace(psz); - } - - pcszValue = tstVDIoScriptSkipSpace((char *)pcszValue); - if (*pcszValue == '\0') - { - RTPrintf("Value missing for argument '%s'\n", pcszName); - rc = VERR_INVALID_STATE; - break; - } - - /* We have the name and value pair now. */ - bool fMandatory = false; /* Shut up gcc */ - rc = tstVDIoScriptArgumentParse(pVDScriptAction, pcszName, pcszValue, &paScriptArgs[cScriptArgs], &fMandatory); - if (RT_SUCCESS(rc)) - { - if (fMandatory) - cMandatoryArgsReq--; - cScriptArgs++; - } - } - else - { - RTPrintf("Argument in invalid form\n"); - rc = VERR_INVALID_STATE; - break; - } - } - - if ( RT_SUCCESS(rc) - && cMandatoryArgsReq) - { - /* No arguments anymore but there are still mandatory arguments left. */ - RTPrintf("There are %u arguments missing for script action '%s\n", pVDScriptAction->pcszAction); - rc = VERR_INVALID_STATE; - } - - if (RT_SUCCESS(rc)) - *pcScriptArgs = cScriptArgs; - - return rc; -} - -/** - * Executes the script pointed to by the given stream. - * - * @returns IPRT status code. - * - * @param pStrm The stream handle of the script. - * @param pGlob Global test data. - */ -static int tstVDIoScriptExecute(PRTSTREAM pStrm, PVDTESTGLOB pGlob) -{ - int rc = VINF_SUCCESS; - char abBuffer[0x1000]; /* Current assumption that a line is never longer than 4096 bytes. */ - PVDSCRIPTARG paScriptArgs = NULL; - unsigned cScriptArgsMax = 0; - - do - { - memset(abBuffer, 0, sizeof(abBuffer)); - rc = RTStrmGetLine(pStrm, abBuffer, sizeof(abBuffer)); - if (RT_SUCCESS(rc)) - { - const char *pcszAction = NULL; - char *psz = abBuffer; - - /* Skip space */ - psz = tstVDIoScriptSkipSpace(psz); - if (!tstVDIoIsLineEnd(psz)) - { - PCVDSCRIPTACTION pVDScriptAction = NULL; - - /* Get the action name. */ - pcszAction = psz; - - psz = tstVDIoScriptSkipNonSpace(psz); - if (!tstVDIoIsLineEnd(psz)) - { - Assert(RT_C_IS_SPACE(*psz)); - *psz++ = '\0'; - } - - /* Find the action. */ - for (unsigned i = 0; i < g_cScriptActions; i++) - { - if (!RTStrCmp(pcszAction, g_aScriptActions[i].pcszAction)) - { - pVDScriptAction = &g_aScriptActions[i]; - break; - } - } - - if (pVDScriptAction) - { - /* Parse arguments. */ - if (cScriptArgsMax < pVDScriptAction->cArgDescs) - { - /* Increase arguments array. */ - if (paScriptArgs) - RTMemFree(paScriptArgs); - - cScriptArgsMax = pVDScriptAction->cArgDescs; - paScriptArgs = (PVDSCRIPTARG)RTMemAllocZ(cScriptArgsMax * sizeof(VDSCRIPTARG)); - } - - if (paScriptArgs) - { - unsigned cScriptArgs; - - rc = tstVDIoScriptArgumentListParse(psz, pVDScriptAction, paScriptArgs, &cScriptArgs); - if (RT_SUCCESS(rc)) - { - /* Execute the handler. */ - rc = pVDScriptAction->pfnHandler(pGlob, paScriptArgs, cScriptArgs); - } - } - else - { - RTPrintf("Out of memory while allocating argument array for script action %s\n", pcszAction); - rc = VERR_NO_MEMORY; - } - } - else - { - RTPrintf("Script action %s is not known\n", pcszAction); - rc = VERR_NOT_FOUND; - } - } - /* else empty line, just continue */ - } - } while(RT_SUCCESS(rc)); - - if (rc == VERR_EOF) - { - RTPrintf("Successfully executed I/O script\n"); - rc = VINF_SUCCESS; - } - return rc; -} - -/** - * Executes the given I/O script. + * Executes the given I/O script using the new scripting engine. * * @returns nothing. * @@ -3625,17 +2656,28 @@ static int tstVDIoScriptExecute(PRTSTREAM pStrm, PVDTESTGLOB pGlob) static void tstVDIoScriptRun(const char *pcszFilename) { int rc = VINF_SUCCESS; - PRTSTREAM pScriptStrm; /**< Stream of the script file. */ VDTESTGLOB GlobTest; /**< Global test data. */ + void *pvFile = NULL; + size_t cbFile = 0; memset(&GlobTest, 0, sizeof(VDTESTGLOB)); RTListInit(&GlobTest.ListFiles); RTListInit(&GlobTest.ListDisks); RTListInit(&GlobTest.ListPatterns); + GlobTest.pszIoBackend = RTStrDup("memory"); + if (!GlobTest.pszIoBackend) + { + RTPrintf("Out of memory allocating I/O backend string\n"); + return; + } - rc = RTStrmOpen(pcszFilename, "r", &pScriptStrm); + rc = RTFileReadAll(pcszFilename, &pvFile, &cbFile); if (RT_SUCCESS(rc)) { + char *pszScript = RTStrDupN((char *)pvFile, cbFile); + RTFileReadAllFree(pvFile, cbFile); + + AssertPtr(pszScript); /* Init global test data. */ GlobTest.VDIfError.pfnError = tstVDError; GlobTest.VDIfError.pfnMessage = tstVDMessage; @@ -3664,24 +2706,34 @@ static void tstVDIoScriptRun(const char *pcszFilename) AssertRC(rc); /* Init I/O backend. */ - rc = VDIoBackendMemCreate(&GlobTest.pIoBackend); + rc = VDIoBackendCreate(&GlobTest.pIoBackend); if (RT_SUCCESS(rc)) { - /* Execute the script. */ - rc = tstVDIoScriptExecute(pScriptStrm, &GlobTest); - if (RT_FAILURE(rc)) + VDSCRIPTCTX hScriptCtx = NULL; + rc = VDScriptCtxCreate(&hScriptCtx); + if (RT_SUCCESS(rc)) { - RTPrintf("Executing the script stream failed rc=%Rrc\n", rc); + rc = VDScriptCtxCallbacksRegister(hScriptCtx, g_aScriptActions, g_cScriptActions, &GlobTest); + AssertRC(rc); + + rc = VDScriptCtxLoadScript(hScriptCtx, pszScript); + if (RT_FAILURE(rc)) + { + RTPrintf("Loading the script failed rc=%Rrc\n", rc); + } + else + rc = VDScriptCtxCallFn(hScriptCtx, "main", NULL, 0); + VDScriptCtxDestroy(hScriptCtx); } - VDIoBackendMemDestroy(GlobTest.pIoBackend); + VDIoBackendDestroy(GlobTest.pIoBackend); } else RTPrintf("Creating the I/O backend failed rc=%Rrc\n"); - - RTStrmClose(pScriptStrm); } else RTPrintf("Opening script failed rc=%Rrc\n", rc); + + RTStrFree(GlobTest.pszIoBackend); } /** diff --git a/src/VBox/Storage/testcase/tstVDIo.vd b/src/VBox/Storage/testcase/tstVDIo.vd index b808ffa3..0074be68 100644 --- a/src/VBox/Storage/testcase/tstVDIo.vd +++ b/src/VBox/Storage/testcase/tstVDIo.vd @@ -1,108 +1,53 @@ -# $Id: tstVDIo.vd $ -# -# Storage: Simple I/O testing for most backends. -# - -# -# Copyright (C) 2011 Oracle Corporation -# -# This file is part of VirtualBox Open Source Edition (OSE), as -# available from http://www.virtualbox.org. This file is free software; -# you can redistribute it and/or modify it under the terms of the GNU -# General Public License (GPL) as published by the Free Software -# Foundation, in version 2 as it comes in the "COPYING" file of the -# VirtualBox OSE distribution. VirtualBox OSE is distributed in the -# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. -# - -# Init I/O RNG for generating random data for writes -iorngcreate size=10M mode=manual seed=1234567890 - -# VMDK disk -print msg=Testing_VMDK -createdisk name=test verify=yes -create disk=test mode=base name=tstShared.vmdk type=dynamic backend=VMDK size=200M -io disk=test async=yes max-reqs=32 mode=seq blocksize=64k off=0-200M size=200M writes=100 -io disk=test async=yes max-reqs=32 mode=seq blocksize=64k off=0-200M size=200M writes=0 -create disk=test mode=diff name=tstShared2.vmdk type=dynamic backend=VMDK size=200M -io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=50 -create disk=test mode=diff name=tstShared3.vmdk type=dynamic backend=VMDK size=200M -io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=50 -close disk=test mode=single delete=yes -close disk=test mode=single delete=yes -close disk=test mode=single delete=yes -destroydisk name=test - -# VDI disk -print msg=Testing_VDI -createdisk name=test verify=yes -create disk=test mode=base name=tstShared.vdi type=dynamic backend=VDI size=200M -io disk=test async=yes max-reqs=32 mode=seq blocksize=64k off=0-200M size=200M writes=100 -io disk=test async=yes max-reqs=32 mode=seq blocksize=64k off=0-200M size=200M writes=0 -create disk=test mode=diff name=tstShared2.vdi type=dynamic backend=VDI size=200M -io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=50 -create disk=test mode=diff name=tstShared3.vdi type=dynamic backend=VDI size=200M -io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=50 -close disk=test mode=single delete=yes -close disk=test mode=single delete=yes -close disk=test mode=single delete=yes -destroydisk name=test - -# VHD disk -print msg=Testing_VHD -createdisk name=test verify=yes -create disk=test mode=base name=tstShared.vhd type=dynamic backend=VHD size=200M -io disk=test async=yes max-reqs=32 mode=seq blocksize=64k off=0-200M size=200M writes=100 -io disk=test async=yes max-reqs=32 mode=seq blocksize=64k off=0-200M size=200M writes=0 -create disk=test mode=diff name=tstShared2.vhd type=dynamic backend=VHD size=200M -io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=50 -create disk=test mode=diff name=tstShared3.vhd type=dynamic backend=VHD size=200M -io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=50 -close disk=test mode=single delete=yes -close disk=test mode=single delete=yes -close disk=test mode=single delete=yes -destroydisk name=test - -# Parallels disk -print msg=Testing_Parallels -createdisk name=test verify=yes -create disk=test mode=base name=tstShared.hdd type=dynamic backend=Parallels size=200M -io disk=test async=yes max-reqs=32 mode=seq blocksize=64k off=0-200M size=200M writes=100 -io disk=test async=yes max-reqs=32 mode=seq blocksize=64k off=0-200M size=200M writes=0 -create disk=test mode=diff name=tstShared2.hdd type=dynamic backend=Parallels size=200M -io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=50 -create disk=test mode=diff name=tstShared3.hdd type=dynamic backend=Parallels size=200M -io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=50 -close disk=test mode=single delete=yes -close disk=test mode=single delete=yes -close disk=test mode=single delete=yes -destroydisk name=test - -# QED disk -print msg=Testing_QED -createdisk name=test verify=yes -create disk=test mode=base name=tstShared.qed type=dynamic backend=QED size=200M -io disk=test async=yes max-reqs=32 mode=seq blocksize=64k off=0-200M size=200M writes=100 -io disk=test async=yes max-reqs=32 mode=seq blocksize=64k off=0-200M size=200M writes=0 -io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=50 -io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=0 -close disk=test mode=single delete=no -open disk=test name=tstShared.qed backend=QED async=yes -io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=0 -destroydisk name=test - -# QCOW disk -print msg=Testing_QCOW -createdisk name=test verify=yes -create disk=test mode=base name=tstShared.qed type=dynamic backend=QCOW size=200M -io disk=test async=yes max-reqs=32 mode=seq blocksize=64k off=0-200M size=200M writes=100 -io disk=test async=yes max-reqs=32 mode=seq blocksize=64k off=0-200M size=200M writes=0 -io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=50 -io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=0 -close disk=test mode=single delete=no -open disk=test name=tstShared.qed backend=QCOW async=yes -io disk=test async=yes max-reqs=32 mode=rnd blocksize=64k off=0-200M size=200M writes=0 -destroydisk name=test - -iorngdestroy +/* $Id: tstVDIo.vd $ */ +/** + * Storage: Simple I/O testing for most backends. + */ + +/* + * Copyright (C) 2011-2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +void tstIo(string strMessage, string strBackend) +{ + print(strMessage); + createdisk("test", true /* fVerify */); + create("test", "base", "tst.disk", "dynamic", strBackend, 200M, false /* fIgnoreFlush */); + io("test", true, 32, "seq", 64K, 0, 200M, 200M, 100, "none"); + io("test", false, 1, "seq", 64K, 0, 200M, 200M, 100, "none"); + io("test", true, 32, "seq", 64K, 0, 200M, 200M, 0, "none"); + io("test", false, 1, "seq", 64K, 0, 200M, 200M, 0, "none"); + create("test", "diff", "tst2.disk", "dynamic", strBackend, 200M, false /* fIgnoreFlush */); + io("test", true, 32, "rnd", 64K, 0, 200M, 200M, 50, "none"); + io("test", false, 1, "rnd", 64K, 0, 200M, 200M, 50, "none"); + create("test", "diff", "tst3.disk", "dynamic", strBackend, 200M, false /* fIgnoreFlush */); + io("test", true, 32, "rnd", 64K, 0, 200M, 200M, 50, "none"); + io("test", false, 1, "rnd", 64K, 0, 200M, 200M, 50, "none"); + close("test", "single", true /* fDelete */); + close("test", "single", true /* fDelete */); + close("test", "single", true /* fDelete */); + destroydisk("test"); +} + +void main() +{ + /* Init I/O RNG for generating random data for writes */ + iorngcreate(10M, "manual", 1234567890); + + tstIo("Testing VDI", "VDI"); + tstIo("Testing VMDK", "VMDK"); + tstIo("Testing VHD", "VHD"); + tstIo("Testing Parallels", "Parallels"); + tstIo("Testing QED", "QED"); + tstIo("Testing QCOW", "QCOW"); + + iorngdestroy(); +} diff --git a/src/VBox/Storage/testcase/tstVDResize.vd b/src/VBox/Storage/testcase/tstVDResize.vd new file mode 100644 index 00000000..0a3dcf40 --- /dev/null +++ b/src/VBox/Storage/testcase/tstVDResize.vd @@ -0,0 +1,32 @@ +/* $Id: tstVDResize.vd $ */ +/** + * Storage: Resize testing for VDI. + */ + +/* + * Copyright (C) 2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +void main() +{ + /* Init I/O RNG for generating random data for writes. */ + iorngcreate(10M, "manual", 1234567890); + + print("Testing VDI"); + createdisk("test", true); + create("test", "base", "tst.vdi", "dynamic", "VDI", 1T, false); + io("test", false, 1, "seq", 64K, 255G, 257G, 2G, 100, "none"); + resize("test", 1331200M); + io("test", false, 1, "seq", 64K, 255G, 257G, 2G, 0, "none"); + destroydisk("test"); + + iorngdestroy(); +} diff --git a/src/VBox/Storage/testcase/tstVDShareable.cpp b/src/VBox/Storage/testcase/tstVDShareable.cpp index 98957fb7..834f6a96 100644 --- a/src/VBox/Storage/testcase/tstVDShareable.cpp +++ b/src/VBox/Storage/testcase/tstVDShareable.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2010 Oracle Corporation + * Copyright (C) 2010-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Storage/testcase/tstVDShareable.vd b/src/VBox/Storage/testcase/tstVDShareable.vd index b2f5cd0e..7cbe4e2b 100644 --- a/src/VBox/Storage/testcase/tstVDShareable.vd +++ b/src/VBox/Storage/testcase/tstVDShareable.vd @@ -1,53 +1,55 @@ -# $Id: tstVDShareable.vd $ -# -# Storage: Testcase for shareable disks. -# - -# -# Copyright (C) 2011 Oracle Corporation -# -# This file is part of VirtualBox Open Source Edition (OSE), as -# available from http://www.virtualbox.org. This file is free software; -# you can redistribute it and/or modify it under the terms of the GNU -# General Public License (GPL) as published by the Free Software -# Foundation, in version 2 as it comes in the "COPYING" file of the -# VirtualBox OSE distribution. VirtualBox OSE is distributed in the -# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. -# - -# Init I/O RNG for generating random data for writes -iorngcreate size=10M mode=manual seed=1234567890 - -# Create disk containers. -createdisk name=shared1 -createdisk name=shared2 - -# Create the disk and close it. -create disk=shared1 mode=base name=tstShared.vdi type=fixed backend=VDI size=20M -close disk=shared1 mode=all delete=no - -# Open the disk with sharing enabled. -open disk=shared1 name=tstShared.vdi backend=VDI shareable=yes -open disk=shared2 name=tstShared.vdi backend=VDI shareable=yes - -# Write to one disk and verify that the other disk can see the content. -io disk=shared1 async=yes max-reqs=32 mode=seq blocksize=64k off=0-20M size=20M writes=100 -comparedisks disk1=shared1 disk2=shared2 - -# Write to the second disk and verify that the first can see the content. -io disk=shared2 async=yes max-reqs=64 mode=seq blocksize=8k off=0-20M size=20M writes=50 -comparedisks disk1=shared1 disk2=shared2 - -# Close but don't delete yet. -close disk=shared1 mode=all delete=no -close disk=shared2 mode=all delete=no - -# Open and delete -open disk=shared1 name=tstShared.vdi backend=VDI shareable=no -close disk=shared1 mode=single delete=yes - -# Cleanup -destroydisk name=shared1 -destroydisk name=shared2 -iorngdestroy - +/* $Id: tstVDShareable.vd $ */ +/** + * Storage: Testcase for shareable disks. + */ + +/* + * Copyright (C) 2011 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +void main() +{ + /* Init I/O RNG for generating random data for writes. */ + iorngcreate(10M, "manual", 1234567890); + + /* Create disk containers. */ + createdisk("shared1", false); + createdisk("shared2", false); + + /* Create the disk and close it. */ + create("shared1", "base", "tstShared.vdi", "fixed", "VDI", 20M, false); + close("shared1", "all", false); + + /* Open the disk with sharing enabled. */ + open("shared1", "tstShared.vdi", "VDI", true /* fAsync */, true /* fShareable */, false, false, false); + open("shared2", "tstShared.vdi", "VDI", true /* fAsync */, true /* fShareable */, false, false, false); + + /* Write to one disk and verify that the other disk can see the content. */ + io("shared1", true, 32, "seq", 64K, 0, 20M, 20M, 100, "none"); + comparedisks("shared1", "shared2"); + + /* Write to the second disk and verify that the first can see the content. */ + io("shared2", true, 64, "seq", 8K, 0, 20M, 20M, 50, "none"); + comparedisks("shared1", "shared2"); + + /* Close but don't delete yet. */ + close("shared1", "all", false); + close("shared2", "all", false); + + /* Open and delete. */ + open("shared1", "tstShared.vdi", "VDI", false /* fAsync */, false /* fShareable */, false, false, false); + close("shared1", "single", true); + + /* Cleanup */ + destroydisk("shared1"); + destroydisk("shared2"); + iorngdestroy(); +} diff --git a/src/VBox/Storage/testcase/tstVDSnap.cpp b/src/VBox/Storage/testcase/tstVDSnap.cpp index 83313e1f..d32d3b55 100644 --- a/src/VBox/Storage/testcase/tstVDSnap.cpp +++ b/src/VBox/Storage/testcase/tstVDSnap.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2010 Oracle Corporation + * Copyright (C) 2010-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -229,6 +229,21 @@ static int tstVDOpenCreateWriteMerge(PVDSNAPTEST pTest) unsigned cDiffs = 0; unsigned idDiff = 0; /* Diff ID counter for the filename */ + /* Delete all images from a previous run. */ + RTFileDelete(pTest->pcszBaseImage); + for (unsigned i = 0; i < pTest->cIterations; i++) + { + char *pszDiffFilename = NULL; + + rc = RTStrAPrintf(&pszDiffFilename, "tstVDSnapDiff%u.%s", i, pTest->pcszDiffSuff); + if (RT_SUCCESS(rc)) + { + if (RTFileExists(pszDiffFilename)) + RTFileDelete(pszDiffFilename); + RTStrFree(pszDiffFilename); + } + } + /* Create the virtual disk test data */ pbTestPattern = (uint8_t *)RTMemAlloc(pTest->cbTestPattern); @@ -260,10 +275,22 @@ static int tstVDOpenCreateWriteMerge(PVDSNAPTEST pTest) if (pbTestPattern) \ RTMemFree(pbTestPattern); \ VDDestroy(pVD); \ + g_cErrors++; \ return rc; \ } \ } while (0) +#define CHECK_BREAK(str) \ + do \ + { \ + RTPrintf("%s rc=%Rrc\n", str, rc); \ + if (RT_FAILURE(rc)) \ + { \ + g_cErrors++; \ + break; \ + } \ + } while (0) + /* Create error interface. */ /* Create error interface. */ VDIfError.pfnError = tstVDError; @@ -292,7 +319,7 @@ static int tstVDOpenCreateWriteMerge(PVDSNAPTEST pTest) { /* Write */ rc = tstVDSnapWrite(pVD, paDiskSeg, cDiskSegments, cbDisk, fInit); - CHECK("tstVDSnapWrite()"); + CHECK_BREAK("tstVDSnapWrite()"); fInit = false; @@ -313,7 +340,7 @@ static int tstVDOpenCreateWriteMerge(PVDSNAPTEST pTest) rc = VDCreateDiff(pVD, pTest->pcszBackend, pszDiffFilename, VD_IMAGE_FLAGS_NONE, "Test diff image", NULL, NULL, VD_OPEN_FLAGS_NORMAL, NULL, NULL); - CHECK("VDCreateDiff()"); + CHECK_BREAK("VDCreateDiff()"); RTStrFree(pszDiffFilename); VDDumpImages(pVD); @@ -333,7 +360,7 @@ static int tstVDOpenCreateWriteMerge(PVDSNAPTEST pTest) rc = VDMerge(pVD, uStartMerge, uEndMerge, NULL); else rc = VDMerge(pVD, uEndMerge, uStartMerge, NULL); - CHECK("VDMerge()"); + CHECK_BREAK("VDMerge()"); cDiffs -= uEndMerge - uStartMerge; @@ -351,7 +378,7 @@ static int tstVDOpenCreateWriteMerge(PVDSNAPTEST pTest) /* Now compare the result with our test pattern */ rc = tstVDSnapReadVerify(pVD, paDiskSeg, cDiskSegments, cbDisk); - CHECK("tstVDSnapReadVerify()"); + CHECK_BREAK("tstVDSnapReadVerify()"); } cIteration++; } @@ -372,7 +399,7 @@ static int tstVDOpenCreateWriteMerge(PVDSNAPTEST pTest) RTStrFree(pszDiffFilename); } #undef CHECK - return 0; + return rc; } int main(int argc, char *argv[]) diff --git a/src/VBox/Storage/testcase/vbox-img.cpp b/src/VBox/Storage/testcase/vbox-img.cpp index 8fb9e0bf..7d3f9a48 100644 --- a/src/VBox/Storage/testcase/vbox-img.cpp +++ b/src/VBox/Storage/testcase/vbox-img.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2010-2011 Oracle Corporation + * Copyright (C) 2010-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -64,10 +64,13 @@ static void printUsage(PRTSTREAM pStrm) " --size <size in bytes>\n" " [--format VDI|VMDK|VHD] (default: VDI)\n" " [--variant Standard,Fixed,Split2G,Stream,ESX]\n" + " [--dataalignment <alignment in bytes>]\n" "\n" " repair --filename <filename>\n" " [--dry-run]\n" - " [--format VDI|VMDK|VHD] (default: autodetect)\n", + " [--format VDI|VMDK|VHD] (default: autodetect)\n" + "\n" + " clearcomment --filename <filename>\n", g_pszProgName); } @@ -260,8 +263,8 @@ int handleSetUUID(HandlerArg *a) if (RT_FAILURE(rc)) return errorRuntime("Cannot create the virtual disk container: %Rrc\n", rc); - - rc = VDOpen(pVD, pszFormat, pszFilename, VD_OPEN_FLAGS_NORMAL, NULL); + /* Open in info mode to be able to open diff images without their parent. */ + rc = VDOpen(pVD, pszFormat, pszFilename, VD_OPEN_FLAGS_INFO, NULL); if (RT_FAILURE(rc)) return errorRuntime("Cannot open the virtual disk image \"%s\": %Rrc\n", pszFilename, rc); @@ -430,21 +433,22 @@ static int convInRead(void *pvUser, void *pStorage, uint64_t uOffset, if (pFS->offBuffer == UINT64_MAX) { /* Repeat reading until buffer is full or EOF. */ - size_t cbSumRead = 0, cbRead; - uint8_t *pTmp = (uint8_t *)&pFS->abBuffer[0]; + size_t cbRead; + size_t cbSumRead = 0; + uint8_t *pbTmp = (uint8_t *)&pFS->abBuffer[0]; size_t cbTmp = sizeof(pFS->abBuffer); do { - rc = RTFileRead(pFS->file, pTmp, cbTmp, &cbRead); + rc = RTFileRead(pFS->file, pbTmp, cbTmp, &cbRead); if (RT_FAILURE(rc)) return rc; - pTmp += cbRead; + pbTmp += cbRead; cbTmp -= cbRead; cbSumRead += cbRead; } while (cbTmp && cbRead); pFS->offBuffer = 0; - pFS->cbBuffer = cbSumRead; + pFS->cbBuffer = (uint32_t)cbSumRead; if (!cbSumRead && !pcbRead) /* Caller can't handle partial reads. */ return VERR_EOF; } @@ -464,25 +468,26 @@ static int convInRead(void *pvUser, void *pStorage, uint64_t uOffset, } /* Repeat reading until buffer is full or EOF. */ - size_t cbSumRead = 0, cbRead; - uint8_t *pTmp = (uint8_t *)&pFS->abBuffer[0]; + size_t cbRead; + size_t cbSumRead = 0; + uint8_t *pbTmp = (uint8_t *)&pFS->abBuffer[0]; size_t cbTmp = sizeof(pFS->abBuffer); do { - rc = RTFileRead(pFS->file, pTmp, cbTmp, &cbRead); + rc = RTFileRead(pFS->file, pbTmp, cbTmp, &cbRead); if (RT_FAILURE(rc)) return rc; - pTmp += cbRead; + pbTmp += cbRead; cbTmp -= cbRead; cbSumRead += cbRead; } while (cbTmp && cbRead); pFS->offBuffer += pFS->cbBuffer; - pFS->cbBuffer = cbSumRead; + pFS->cbBuffer = (uint32_t)cbSumRead; } - uint32_t cbThisRead = RT_MIN(cbBuffer, - pFS->cbBuffer - uOffset % sizeof(pFS->abBuffer)); + uint32_t cbThisRead = (uint32_t)RT_MIN(cbBuffer, + pFS->cbBuffer - uOffset % sizeof(pFS->abBuffer)); memcpy(pvBuffer, &pFS->abBuffer[uOffset % sizeof(pFS->abBuffer)], cbThisRead); uOffset += cbThisRead; @@ -654,8 +659,8 @@ static int convOutWrite(void *pvUser, void *pStorage, uint64_t uOffset, pFS->cbBuffer = 0; } - uint32_t cbThisWrite = RT_MIN(cbBuffer, - sizeof(pFS->abBuffer) - uOffset % sizeof(pFS->abBuffer)); + uint32_t cbThisWrite = (uint32_t)RT_MIN(cbBuffer, + sizeof(pFS->abBuffer) - uOffset % sizeof(pFS->abBuffer)); memcpy(&pFS->abBuffer[uOffset % sizeof(pFS->abBuffer)], pvBuffer, cbThisWrite); uOffset += cbThisWrite; @@ -954,7 +959,7 @@ int handleInfo(HandlerArg *a) return errorRuntime("Error while creating the virtual disk container: %Rrc\n", rc); /* Open the image */ - rc = VDOpen(pDisk, pszFormat, pszFilename, VD_OPEN_FLAGS_INFO, NULL); + rc = VDOpen(pDisk, pszFormat, pszFilename, VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_READONLY, NULL); if (RT_FAILURE(rc)) return errorRuntime("Error while opening the image: %Rrc\n", rc); @@ -1296,6 +1301,42 @@ int handleCreateCache(HandlerArg *a) return rc; } +static DECLCALLBACK(bool) vdIfCfgCreateBaseAreKeysValid(void *pvUser, const char *pszzValid) +{ + return VINF_SUCCESS; /** @todo: Implement. */ +} + +static DECLCALLBACK(int) vdIfCfgCreateBaseQuerySize(void *pvUser, const char *pszName, size_t *pcbValue) +{ + AssertReturn(VALID_PTR(pcbValue), VERR_INVALID_POINTER); + + AssertPtrReturn(pvUser, VERR_GENERAL_FAILURE); + + if (RTStrCmp(pszName, "DataAlignment")) + return VERR_CFGM_VALUE_NOT_FOUND; + + *pcbValue = strlen((const char *)pvUser) + 1 /* include terminator */; + + return VINF_SUCCESS; +} + +static DECLCALLBACK(int) vdIfCfgCreateBaseQuery(void *pvUser, const char *pszName, char *pszValue, size_t cchValue) +{ + AssertReturn(VALID_PTR(pszValue), VERR_INVALID_POINTER); + + AssertPtrReturn(pvUser, VERR_GENERAL_FAILURE); + + if (RTStrCmp(pszName, "DataAlignment")) + return VERR_CFGM_VALUE_NOT_FOUND; + + if (strlen((const char *)pvUser) >= cchValue) + return VERR_CFGM_NOT_ENOUGH_SPACE; + + memcpy(pszValue, pvUser, strlen((const char *)pvUser) + 1); + + return VINF_SUCCESS; + +} int handleCreateBase(HandlerArg *a) { @@ -1306,7 +1347,10 @@ int handleCreateBase(HandlerArg *a) const char *pszVariant = NULL; unsigned uImageFlags = VD_IMAGE_FLAGS_NONE; uint64_t cbSize = 0; + const char *pszDataAlignment = NULL; VDGEOMETRY LCHSGeometry, PCHSGeometry; + PVDINTERFACE pVDIfsOperation = NULL; + VDINTERFACECONFIG vdIfCfg; memset(&LCHSGeometry, 0, sizeof(VDGEOMETRY)); memset(&PCHSGeometry, 0, sizeof(VDGEOMETRY)); @@ -1314,10 +1358,11 @@ int handleCreateBase(HandlerArg *a) /* Parse the command line. */ static const RTGETOPTDEF s_aOptions[] = { - { "--filename", 'f', RTGETOPT_REQ_STRING }, - { "--size", 's', RTGETOPT_REQ_UINT64 }, - { "--format", 'b', RTGETOPT_REQ_STRING }, - { "--variant", 'v', RTGETOPT_REQ_STRING } + { "--filename", 'f', RTGETOPT_REQ_STRING }, + { "--size", 's', RTGETOPT_REQ_UINT64 }, + { "--format", 'b', RTGETOPT_REQ_STRING }, + { "--variant", 'v', RTGETOPT_REQ_STRING }, + { "--dataalignment", 'a', RTGETOPT_REQ_STRING } }; int ch; RTGETOPTUNION ValueUnion; @@ -1343,6 +1388,10 @@ int handleCreateBase(HandlerArg *a) pszVariant = ValueUnion.psz; break; + case 'a': // --dataalignment + pszDataAlignment = ValueUnion.psz; + break; + default: ch = RTGetOptPrintError(ch, &ValueUnion); printUsage(g_pStdErr); @@ -1364,6 +1413,16 @@ int handleCreateBase(HandlerArg *a) return errorSyntax("Invalid variant %s given\n", pszVariant); } + /* Setup the config interface if required. */ + if (pszDataAlignment) + { + vdIfCfg.pfnAreKeysValid = vdIfCfgCreateBaseAreKeysValid; + vdIfCfg.pfnQuerySize = vdIfCfgCreateBaseQuerySize; + vdIfCfg.pfnQuery = vdIfCfgCreateBaseQuery; + VDInterfaceAdd(&vdIfCfg.Core, "Config", VDINTERFACETYPE_CONFIG, (void *)pszDataAlignment, + sizeof(vdIfCfg), &pVDIfsOperation); + } + /* just try it */ rc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk); if (RT_FAILURE(rc)) @@ -1371,7 +1430,7 @@ int handleCreateBase(HandlerArg *a) rc = VDCreateBase(pDisk, pszBackend, pszFilename, cbSize, uImageFlags, NULL, &PCHSGeometry, &LCHSGeometry, NULL, VD_OPEN_FLAGS_NORMAL, - NULL, NULL); + NULL, pVDIfsOperation); if (RT_FAILURE(rc)) return errorRuntime("Error while creating the virtual disk: %Rrc\n", rc); @@ -1448,6 +1507,64 @@ int handleRepair(HandlerArg *a) } +int handleClearComment(HandlerArg *a) +{ + int rc = VINF_SUCCESS; + PVBOXHDD pDisk = NULL; + const char *pszFilename = NULL; + bool fDryRun = false; + + /* Parse the command line. */ + static const RTGETOPTDEF s_aOptions[] = + { + { "--filename", 'f', RTGETOPT_REQ_STRING } + }; + int ch; + RTGETOPTUNION ValueUnion; + RTGETOPTSTATE GetState; + RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */); + while ((ch = RTGetOpt(&GetState, &ValueUnion))) + { + switch (ch) + { + case 'f': // --filename + pszFilename = ValueUnion.psz; + break; + + default: + ch = RTGetOptPrintError(ch, &ValueUnion); + printUsage(g_pStdErr); + return ch; + } + } + + /* Check for mandatory parameters. */ + if (!pszFilename) + return errorSyntax("Mandatory --filename option missing\n"); + + /* just try it */ + char *pszFormat = NULL; + VDTYPE enmType = VDTYPE_INVALID; + rc = VDGetFormat(NULL, NULL, pszFilename, &pszFormat, &enmType); + if (RT_FAILURE(rc)) + return errorSyntax("Format autodetect failed: %Rrc\n", rc); + + rc = VDCreate(pVDIfs, enmType, &pDisk); + if (RT_FAILURE(rc)) + return errorRuntime("Error while creating the virtual disk container: %Rrc\n", rc); + + /* Open the image */ + rc = VDOpen(pDisk, pszFormat, pszFilename, VD_OPEN_FLAGS_INFO, NULL); + if (RT_FAILURE(rc)) + return errorRuntime("Error while opening the image: %Rrc\n", rc); + + VDSetComment(pDisk, 0, NULL); + + VDDestroy(pDisk); + return rc; +} + + int main(int argc, char *argv[]) { int exitcode = 0; @@ -1530,13 +1647,14 @@ int main(int argc, char *argv[]) int (*handler)(HandlerArg *a); } s_commandHandlers[] = { - { "setuuid", handleSetUUID }, - { "convert", handleConvert }, - { "info", handleInfo }, - { "compact", handleCompact }, - { "createcache", handleCreateCache }, - { "createbase", handleCreateBase }, - { "repair", handleRepair }, + { "setuuid", handleSetUUID }, + { "convert", handleConvert }, + { "info", handleInfo }, + { "compact", handleCompact }, + { "createcache", handleCreateCache }, + { "createbase", handleCreateBase }, + { "repair", handleRepair }, + { "clearcomment", handleClearComment }, { NULL, NULL } }; |