summaryrefslogtreecommitdiffstats
path: root/src/VBox/Storage/testcase/VDScriptInterp.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Storage/testcase/VDScriptInterp.cpp')
-rw-r--r--src/VBox/Storage/testcase/VDScriptInterp.cpp1071
1 files changed, 1071 insertions, 0 deletions
diff --git a/src/VBox/Storage/testcase/VDScriptInterp.cpp b/src/VBox/Storage/testcase/VDScriptInterp.cpp
new file mode 100644
index 00000000..8122c683
--- /dev/null
+++ b/src/VBox/Storage/testcase/VDScriptInterp.cpp
@@ -0,0 +1,1071 @@
+/* $Id: VDScriptInterp.cpp $ */
+/** @file
+ * VBox HDD container test utility - scripting engine, interpreter.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#define LOGGROUP LOGGROUP_DEFAULT
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/list.h>
+#include <iprt/mem.h>
+#include <iprt/stream.h>
+#include <iprt/string.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, ...)
+{
+ RT_NOREF1(pThis); RT_SRC_POS_NOREF();
+ va_list va;
+ va_start(va, pszFmt);
+ RTPrintfV(pszFmt, va);
+ va_end(va);
+ return rc;
+}
+
+/**
+ * Pops the topmost value from the value stack.
+ *
+ * @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 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)
+{
+ RT_NOREF1(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.
+ *
+ * @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"));
+ RT_FALL_THRU();
+ 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"));
+ RT_FALL_THRU();
+ 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(RT_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(RT_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(RT_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); RT_NOREF_PV(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)
+{
+ RT_NOREF1(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;
+}