VirtualBox

source: vbox/trunk/src/VBox/Storage/testcase/VDScriptInterp.cpp@ 93231

Last change on this file since 93231 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.8 KB
Line 
1/* $Id: VDScriptInterp.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * VBox HDD container test utility - scripting engine, interpreter.
4 */
5
6/*
7 * Copyright (C) 2013-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOGGROUP LOGGROUP_DEFAULT
19#include <iprt/assert.h>
20#include <iprt/err.h>
21#include <iprt/list.h>
22#include <iprt/mem.h>
23#include <iprt/stream.h>
24#include <iprt/string.h>
25
26#include <VBox/log.h>
27
28#include "VDScriptAst.h"
29#include "VDScriptStack.h"
30#include "VDScriptInternal.h"
31
32/**
33 * Interpreter variable.
34 */
35typedef struct VDSCRIPTINTERPVAR
36{
37 /** String space core. */
38 RTSTRSPACECORE Core;
39 /** Value. */
40 VDSCRIPTARG Value;
41} VDSCRIPTINTERPVAR;
42/** Pointer to an interpreter variable. */
43typedef VDSCRIPTINTERPVAR *PVDSCRIPTINTERPVAR;
44
45/**
46 * Block scope.
47 */
48typedef struct VDSCRIPTINTERPSCOPE
49{
50 /** Pointer to the enclosing scope if available. */
51 struct VDSCRIPTINTERPSCOPE *pParent;
52 /** String space of declared variables in this scope. */
53 RTSTRSPACE hStrSpaceVar;
54} VDSCRIPTINTERPSCOPE;
55/** Pointer to a scope block. */
56typedef VDSCRIPTINTERPSCOPE *PVDSCRIPTINTERPSCOPE;
57
58/**
59 * Function call.
60 */
61typedef struct VDSCRIPTINTERPFNCALL
62{
63 /** Pointer to the caller of this function. */
64 struct VDSCRIPTINTERPFNCALL *pCaller;
65 /** Root scope of this function. */
66 VDSCRIPTINTERPSCOPE ScopeRoot;
67 /** Current scope in this function. */
68 PVDSCRIPTINTERPSCOPE pScopeCurr;
69} VDSCRIPTINTERPFNCALL;
70/** Pointer to a function call. */
71typedef VDSCRIPTINTERPFNCALL *PVDSCRIPTINTERPFNCALL;
72
73/**
74 * Interpreter context.
75 */
76typedef struct VDSCRIPTINTERPCTX
77{
78 /** Pointer to the script context. */
79 PVDSCRIPTCTXINT pScriptCtx;
80 /** Current function call entry. */
81 PVDSCRIPTINTERPFNCALL pFnCallCurr;
82 /** Stack of calculated values. */
83 VDSCRIPTSTACK StackValues;
84 /** Evaluation control stack. */
85 VDSCRIPTSTACK StackCtrl;
86} VDSCRIPTINTERPCTX;
87/** Pointer to an interpreter context. */
88typedef VDSCRIPTINTERPCTX *PVDSCRIPTINTERPCTX;
89
90/**
91 * Interpreter control type.
92 */
93typedef enum VDSCRIPTINTERPCTRLTYPE
94{
95 VDSCRIPTINTERPCTRLTYPE_INVALID = 0,
96 /** Function call to evaluate now, all values are computed
97 * and are stored on the value stack.
98 */
99 VDSCRIPTINTERPCTRLTYPE_FN_CALL,
100 /** Cleanup the function call, deleting the scope and restoring the previous one. */
101 VDSCRIPTINTERPCTRLTYPE_FN_CALL_CLEANUP,
102 /** If statement to evaluate now, the guard is on the stack. */
103 VDSCRIPTINTERPCTRLTYPE_IF,
104 /** While or for statement. */
105 VDSCRIPTINTERPCTRLTYPE_LOOP,
106 /** switch statement. */
107 VDSCRIPTINTERPCTRLTYPE_SWITCH,
108 /** Compound statement. */
109 VDSCRIPTINTERPCTRLTYPE_COMPOUND,
110 /** 32bit blowup. */
111 VDSCRIPTINTERPCTRLTYPE_32BIT_HACK = 0x7fffffff
112} VDSCRIPTINTERPCTRLTYPE;
113/** Pointer to a control type. */
114typedef VDSCRIPTINTERPCTRLTYPE *PVDSCRIPTINTERPCTRLTYPE;
115
116/**
117 * Interpreter stack control entry.
118 */
119typedef struct VDSCRIPTINTERPCTRL
120{
121 /** Flag whether this entry points to an AST node to evaluate. */
122 bool fEvalAst;
123 /** Flag dependent data. */
124 union
125 {
126 /** Pointer to the AST node to interprete. */
127 PVDSCRIPTASTCORE pAstNode;
128 /** Interpreter control structure. */
129 struct
130 {
131 /** Type of control. */
132 VDSCRIPTINTERPCTRLTYPE enmCtrlType;
133 /** Function call data. */
134 struct
135 {
136 /** Function to call. */
137 PVDSCRIPTFN pFn;
138 } FnCall;
139 /** Compound statement. */
140 struct
141 {
142 /** The compound statement node. */
143 PVDSCRIPTASTSTMT pStmtCompound;
144 /** Current statement evaluated. */
145 PVDSCRIPTASTSTMT pStmtCurr;
146 } Compound;
147 /** Pointer to an AST node. */
148 PVDSCRIPTASTCORE pAstNode;
149 } Ctrl;
150 };
151} VDSCRIPTINTERPCTRL;
152/** Pointer to an exec stack control entry. */
153typedef VDSCRIPTINTERPCTRL *PVDSCRIPTINTERPCTRL;
154
155/**
156 * Record an error while interpreting.
157 *
158 * @returns VBox status code passed.
159 * @param pThis The interpreter context.
160 * @param rc The status code to record.
161 * @param RT_SRC_POS Position in the source code.
162 * @param pszFmt Format string.
163 */
164static int vdScriptInterpreterError(PVDSCRIPTINTERPCTX pThis, int rc, RT_SRC_POS_DECL, const char *pszFmt, ...)
165{
166 RT_NOREF1(pThis); RT_SRC_POS_NOREF();
167 va_list va;
168 va_start(va, pszFmt);
169 RTPrintfV(pszFmt, va);
170 va_end(va);
171 return rc;
172}
173
174/**
175 * Pops the topmost value from the value stack.
176 *
177 * @returns nothing.
178 * @param pThis The interpreter context.
179 * @param pVal Where to store the value.
180 */
181DECLINLINE(void) vdScriptInterpreterPopValue(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTARG pVal)
182{
183 PVDSCRIPTARG pValStack = (PVDSCRIPTARG)vdScriptStackGetUsed(&pThis->StackValues);
184 if (!pValStack)
185 {
186 RT_ZERO(*pVal);
187 AssertPtrReturnVoid(pValStack);
188 }
189
190 *pVal = *pValStack;
191 vdScriptStackPop(&pThis->StackValues);
192}
193
194/**
195 * Pushes a given value onto the value stack.
196 */
197DECLINLINE(int) vdScriptInterpreterPushValue(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTARG pVal)
198{
199 PVDSCRIPTARG pValStack = (PVDSCRIPTARG)vdScriptStackGetUnused(&pThis->StackValues);
200 if (!pValStack)
201 return vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory pushing a value on the value stack");
202
203 *pValStack = *pVal;
204 vdScriptStackPush(&pThis->StackValues);
205 return VINF_SUCCESS;
206}
207
208/**
209 * Pushes an AST node onto the control stack.
210 *
211 * @returns VBox status code.
212 * @param pThis The interpreter context.
213 * @param enmCtrlType The control entry type.
214 */
215DECLINLINE(int) vdScriptInterpreterPushAstEntry(PVDSCRIPTINTERPCTX pThis,
216 PVDSCRIPTASTCORE pAstNode)
217{
218 PVDSCRIPTINTERPCTRL pCtrl = NULL;
219
220 pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUnused(&pThis->StackCtrl);
221
222 if (pCtrl)
223 {
224 pCtrl->fEvalAst = true;
225 pCtrl->pAstNode = pAstNode;
226 vdScriptStackPush(&pThis->StackCtrl);
227 return VINF_SUCCESS;
228 }
229
230 return vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory adding an entry on the control stack");
231}
232
233/**
234 * Pushes a control entry without additional data onto the stack.
235 *
236 * @returns VBox status code.
237 * @param pThis The interpreter context.
238 * @param enmCtrlType The control entry type.
239 */
240DECLINLINE(int) vdScriptInterpreterPushNonDataCtrlEntry(PVDSCRIPTINTERPCTX pThis,
241 VDSCRIPTINTERPCTRLTYPE enmCtrlType)
242{
243 PVDSCRIPTINTERPCTRL pCtrl = NULL;
244
245 pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUnused(&pThis->StackCtrl);
246 if (pCtrl)
247 {
248 pCtrl->fEvalAst = false;
249 pCtrl->Ctrl.enmCtrlType = enmCtrlType;
250 vdScriptStackPush(&pThis->StackCtrl);
251 return VINF_SUCCESS;
252 }
253
254 return vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory adding an entry on the control stack");
255}
256
257/**
258 * Pushes a compound statement control entry onto the stack.
259 *
260 * @returns VBox status code.
261 * @param pThis The interpreter context.
262 * @param pStmtFirst The first statement of the compound statement
263 */
264DECLINLINE(int) vdScriptInterpreterPushCompoundCtrlEntry(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTASTSTMT pStmt)
265{
266 PVDSCRIPTINTERPCTRL pCtrl = NULL;
267
268 pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUnused(&pThis->StackCtrl);
269 if (pCtrl)
270 {
271 pCtrl->fEvalAst = false;
272 pCtrl->Ctrl.enmCtrlType = VDSCRIPTINTERPCTRLTYPE_COMPOUND;
273 pCtrl->Ctrl.Compound.pStmtCompound = pStmt;
274 pCtrl->Ctrl.Compound.pStmtCurr = RTListGetFirst(&pStmt->Compound.ListStmts, VDSCRIPTASTSTMT, Core.ListNode);
275 vdScriptStackPush(&pThis->StackCtrl);
276 return VINF_SUCCESS;
277 }
278
279 return vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory adding an entry on the control stack");
280}
281
282/**
283 * Pushes a while statement control entry onto the stack.
284 *
285 * @returns VBox status code.
286 * @param pThis The interpreter context.
287 * @param pStmt The while statement.
288 */
289DECLINLINE(int) vdScriptInterpreterPushWhileCtrlEntry(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTASTSTMT pStmt)
290{
291 int rc = VINF_SUCCESS;
292 PVDSCRIPTINTERPCTRL pCtrl = NULL;
293
294 pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUnused(&pThis->StackCtrl);
295 if (pCtrl)
296 {
297 pCtrl->fEvalAst = false;
298 pCtrl->Ctrl.enmCtrlType = VDSCRIPTINTERPCTRLTYPE_LOOP;
299 pCtrl->Ctrl.pAstNode = &pStmt->Core;
300 vdScriptStackPush(&pThis->StackCtrl);
301
302 rc = vdScriptInterpreterPushAstEntry(pThis, &pStmt->While.pCond->Core);
303 if ( RT_SUCCESS(rc)
304 && pStmt->While.fDoWhile)
305 {
306 /* Push the statement to execute for do ... while loops because they run at least once. */
307 rc = vdScriptInterpreterPushAstEntry(pThis, &pStmt->While.pStmt->Core);
308 if (RT_FAILURE(rc))
309 {
310 /* Cleanup while control statement and AST node. */
311 vdScriptStackPop(&pThis->StackCtrl);
312 vdScriptStackPop(&pThis->StackCtrl);
313 }
314 }
315 else if (RT_FAILURE(rc))
316 vdScriptStackPop(&pThis->StackCtrl); /* Cleanup the while control statement. */
317 }
318 else
319 rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory adding an entry on the control stack");
320
321 return rc;
322}
323
324/**
325 * Pushes an if statement control entry onto the stack.
326 *
327 * @returns VBox status code.
328 * @param pThis The interpreter context.
329 * @param pStmt The if statement.
330 */
331static int vdScriptInterpreterPushIfCtrlEntry(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTASTSTMT pStmt)
332{
333 int rc = VINF_SUCCESS;
334 PVDSCRIPTINTERPCTRL pCtrl = NULL;
335
336 pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUnused(&pThis->StackCtrl);
337 if (pCtrl)
338 {
339 pCtrl->fEvalAst = false;
340 pCtrl->Ctrl.enmCtrlType = VDSCRIPTINTERPCTRLTYPE_IF;
341 pCtrl->Ctrl.pAstNode = &pStmt->Core;
342 vdScriptStackPush(&pThis->StackCtrl);
343
344 rc = vdScriptInterpreterPushAstEntry(pThis, &pStmt->If.pCond->Core);
345 }
346 else
347 rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory adding an entry on the control stack");
348
349 return rc;
350}
351
352/**
353 * Pushes a for statement control entry onto the stack.
354 *
355 * @returns VBox status code.
356 * @param pThis The interpreter context.
357 * @param pStmt The while statement.
358 */
359DECLINLINE(int) vdScriptInterpreterPushForCtrlEntry(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTASTSTMT pStmt)
360{
361 int rc = VINF_SUCCESS;
362 PVDSCRIPTINTERPCTRL pCtrl = NULL;
363
364 pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUnused(&pThis->StackCtrl);
365 if (pCtrl)
366 {
367 pCtrl->fEvalAst = false;
368 pCtrl->Ctrl.enmCtrlType = VDSCRIPTINTERPCTRLTYPE_LOOP;
369 pCtrl->Ctrl.pAstNode = &pStmt->Core;
370 vdScriptStackPush(&pThis->StackCtrl);
371
372 /* Push the conditional first and the initializer .*/
373 rc = vdScriptInterpreterPushAstEntry(pThis, &pStmt->For.pExprCond->Core);
374 if (RT_SUCCESS(rc))
375 {
376 rc = vdScriptInterpreterPushAstEntry(pThis, &pStmt->For.pExprStart->Core);
377 if (RT_FAILURE(rc))
378 vdScriptStackPop(&pThis->StackCtrl);
379 }
380 }
381 else
382 rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory adding an entry on the control stack");
383
384 return rc;
385}
386
387/**
388 * Destroy variable string space callback.
389 */
390static DECLCALLBACK(int) vdScriptInterpreterVarSpaceDestroy(PRTSTRSPACECORE pStr, void *pvUser)
391{
392 RT_NOREF1(pvUser);
393 RTMemFree(pStr);
394 return VINF_SUCCESS;
395}
396
397/**
398 * Setsup a new scope in the current function call.
399 *
400 * @returns VBox status code.
401 * @param pThis The interpreter context.
402 */
403static int vdScriptInterpreterScopeCreate(PVDSCRIPTINTERPCTX pThis)
404{
405 int rc = VINF_SUCCESS;
406 PVDSCRIPTINTERPSCOPE pScope = (PVDSCRIPTINTERPSCOPE)RTMemAllocZ(sizeof(VDSCRIPTINTERPSCOPE));
407 if (pScope)
408 {
409 pScope->pParent = pThis->pFnCallCurr->pScopeCurr;
410 pScope->hStrSpaceVar = NULL;
411 pThis->pFnCallCurr->pScopeCurr = pScope;
412 }
413 else
414 rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory allocating new scope");
415
416 return rc;
417}
418
419/**
420 * Destroys the current scope.
421 *
422 * @returns nothing.
423 * @param pThis The interpreter context.
424 */
425static void vdScriptInterpreterScopeDestroyCurr(PVDSCRIPTINTERPCTX pThis)
426{
427 AssertMsgReturnVoid(pThis->pFnCallCurr->pScopeCurr != &pThis->pFnCallCurr->ScopeRoot,
428 ("Current scope is root scope of function call\n"));
429
430 PVDSCRIPTINTERPSCOPE pScope = pThis->pFnCallCurr->pScopeCurr;
431 pThis->pFnCallCurr->pScopeCurr = pScope->pParent;
432 RTStrSpaceDestroy(&pScope->hStrSpaceVar, vdScriptInterpreterVarSpaceDestroy, NULL);
433 RTMemFree(pScope);
434}
435
436/**
437 * Get the content of the given variable identifier from the current or parent scope.
438 */
439static PVDSCRIPTINTERPVAR vdScriptInterpreterGetVar(PVDSCRIPTINTERPCTX pThis, const char *pszVar)
440{
441 PVDSCRIPTINTERPSCOPE pScopeCurr = pThis->pFnCallCurr->pScopeCurr;
442 PVDSCRIPTINTERPVAR pVar = NULL;
443
444 while ( !pVar
445 && pScopeCurr)
446 {
447 pVar = (PVDSCRIPTINTERPVAR)RTStrSpaceGet(&pScopeCurr->hStrSpaceVar, pszVar);
448 if (pVar)
449 break;
450 pScopeCurr = pScopeCurr->pParent;
451 }
452
453
454 return pVar;
455}
456
457/**
458 * Evaluate an expression.
459 *
460 * @returns VBox status code.
461 * @param pThis The interpreter context.
462 * @param pExpr The expression to evaluate.
463 */
464static int vdScriptInterpreterEvaluateExpression(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTASTEXPR pExpr)
465{
466 int rc = VINF_SUCCESS;
467
468 switch (pExpr->enmType)
469 {
470 case VDSCRIPTEXPRTYPE_PRIMARY_NUMCONST:
471 {
472 /* Push the numerical constant on the value stack. */
473 VDSCRIPTARG NumConst;
474 NumConst.enmType = VDSCRIPTTYPE_UINT64;
475 NumConst.u64 = pExpr->u64;
476 rc = vdScriptInterpreterPushValue(pThis, &NumConst);
477 break;
478 }
479 case VDSCRIPTEXPRTYPE_PRIMARY_STRINGCONST:
480 {
481 /* Push the string literal on the value stack. */
482 VDSCRIPTARG StringConst;
483 StringConst.enmType = VDSCRIPTTYPE_STRING;
484 StringConst.psz = pExpr->pszStr;
485 rc = vdScriptInterpreterPushValue(pThis, &StringConst);
486 break;
487 }
488 case VDSCRIPTEXPRTYPE_PRIMARY_BOOLEAN:
489 {
490 VDSCRIPTARG BoolConst;
491 BoolConst.enmType = VDSCRIPTTYPE_BOOL;
492 BoolConst.f = pExpr->f;
493 rc = vdScriptInterpreterPushValue(pThis, &BoolConst);
494 break;
495 }
496 case VDSCRIPTEXPRTYPE_PRIMARY_IDENTIFIER:
497 {
498 /* Look it up and push the value onto the value stack. */
499 PVDSCRIPTINTERPVAR pVar = vdScriptInterpreterGetVar(pThis, pExpr->pIde->aszIde);
500
501 AssertPtrReturn(pVar, VERR_IPE_UNINITIALIZED_STATUS);
502 rc = vdScriptInterpreterPushValue(pThis, &pVar->Value);
503 break;
504 }
505 case VDSCRIPTEXPRTYPE_POSTFIX_INCREMENT:
506 case VDSCRIPTEXPRTYPE_POSTFIX_DECREMENT:
507 AssertMsgFailed(("TODO\n"));
508 RT_FALL_THRU();
509 case VDSCRIPTEXPRTYPE_POSTFIX_FNCALL:
510 {
511 PVDSCRIPTFN pFn = (PVDSCRIPTFN)RTStrSpaceGet(&pThis->pScriptCtx->hStrSpaceFn, pExpr->FnCall.pFnIde->pIde->aszIde);
512 if (pFn)
513 {
514 /* Push a function call control entry on the stack. */
515 PVDSCRIPTINTERPCTRL pCtrlFn = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUnused(&pThis->StackCtrl);
516 if (pCtrlFn)
517 {
518 pCtrlFn->fEvalAst = false;
519 pCtrlFn->Ctrl.enmCtrlType = VDSCRIPTINTERPCTRLTYPE_FN_CALL;
520 pCtrlFn->Ctrl.FnCall.pFn = pFn;
521 vdScriptStackPush(&pThis->StackCtrl);
522
523 /* Push parameter expressions on the stack. */
524 PVDSCRIPTASTEXPR pArg = RTListGetFirst(&pExpr->FnCall.ListArgs, VDSCRIPTASTEXPR, Core.ListNode);
525 while (pArg)
526 {
527 rc = vdScriptInterpreterPushAstEntry(pThis, &pArg->Core);
528 if (RT_FAILURE(rc))
529 break;
530 pArg = RTListGetNext(&pExpr->FnCall.ListArgs, pArg, VDSCRIPTASTEXPR, Core.ListNode);
531 }
532 }
533 }
534 else
535 AssertMsgFailed(("Invalid program given, unknown function: %s\n", pExpr->FnCall.pFnIde->pIde->aszIde));
536 break;
537 }
538 case VDSCRIPTEXPRTYPE_UNARY_INCREMENT:
539 case VDSCRIPTEXPRTYPE_UNARY_DECREMENT:
540 case VDSCRIPTEXPRTYPE_UNARY_POSSIGN:
541 case VDSCRIPTEXPRTYPE_UNARY_NEGSIGN:
542 case VDSCRIPTEXPRTYPE_UNARY_INVERT:
543 case VDSCRIPTEXPRTYPE_UNARY_NEGATE:
544 case VDSCRIPTEXPRTYPE_MULTIPLICATION:
545 case VDSCRIPTEXPRTYPE_DIVISION:
546 case VDSCRIPTEXPRTYPE_MODULUS:
547 case VDSCRIPTEXPRTYPE_ADDITION:
548 case VDSCRIPTEXPRTYPE_SUBTRACTION:
549 case VDSCRIPTEXPRTYPE_LSR:
550 case VDSCRIPTEXPRTYPE_LSL:
551 case VDSCRIPTEXPRTYPE_LOWER:
552 case VDSCRIPTEXPRTYPE_HIGHER:
553 case VDSCRIPTEXPRTYPE_LOWEREQUAL:
554 case VDSCRIPTEXPRTYPE_HIGHEREQUAL:
555 case VDSCRIPTEXPRTYPE_EQUAL:
556 case VDSCRIPTEXPRTYPE_NOTEQUAL:
557 case VDSCRIPTEXPRTYPE_BITWISE_AND:
558 case VDSCRIPTEXPRTYPE_BITWISE_XOR:
559 case VDSCRIPTEXPRTYPE_BITWISE_OR:
560 case VDSCRIPTEXPRTYPE_LOGICAL_AND:
561 case VDSCRIPTEXPRTYPE_LOGICAL_OR:
562 case VDSCRIPTEXPRTYPE_ASSIGN:
563 case VDSCRIPTEXPRTYPE_ASSIGN_MULT:
564 case VDSCRIPTEXPRTYPE_ASSIGN_DIV:
565 case VDSCRIPTEXPRTYPE_ASSIGN_MOD:
566 case VDSCRIPTEXPRTYPE_ASSIGN_ADD:
567 case VDSCRIPTEXPRTYPE_ASSIGN_SUB:
568 case VDSCRIPTEXPRTYPE_ASSIGN_LSL:
569 case VDSCRIPTEXPRTYPE_ASSIGN_LSR:
570 case VDSCRIPTEXPRTYPE_ASSIGN_AND:
571 case VDSCRIPTEXPRTYPE_ASSIGN_XOR:
572 case VDSCRIPTEXPRTYPE_ASSIGN_OR:
573 case VDSCRIPTEXPRTYPE_ASSIGNMENT_LIST:
574 AssertMsgFailed(("TODO\n"));
575 RT_FALL_THRU();
576 default:
577 AssertMsgFailed(("Invalid expression type: %d\n", pExpr->enmType));
578 }
579 return rc;
580}
581
582/**
583 * Evaluate a statement.
584 *
585 * @returns VBox status code.
586 * @param pThis The interpreter context.
587 * @param pStmt The statement to evaluate.
588 */
589static int vdScriptInterpreterEvaluateStatement(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTASTSTMT pStmt)
590{
591 int rc = VINF_SUCCESS;
592
593 switch (pStmt->enmStmtType)
594 {
595 case VDSCRIPTSTMTTYPE_COMPOUND:
596 {
597 /* Setup new scope. */
598 rc = vdScriptInterpreterScopeCreate(pThis);
599 if (RT_SUCCESS(rc))
600 {
601 /** @todo Declarations */
602 rc = vdScriptInterpreterPushCompoundCtrlEntry(pThis, pStmt);
603 }
604 break;
605 }
606 case VDSCRIPTSTMTTYPE_EXPRESSION:
607 {
608 rc = vdScriptInterpreterPushAstEntry(pThis, &pStmt->pExpr->Core);
609 break;
610 }
611 case VDSCRIPTSTMTTYPE_IF:
612 {
613 rc = vdScriptInterpreterPushIfCtrlEntry(pThis, pStmt);
614 break;
615 }
616 case VDSCRIPTSTMTTYPE_SWITCH:
617 AssertMsgFailed(("TODO\n"));
618 break;
619 case VDSCRIPTSTMTTYPE_WHILE:
620 {
621 rc = vdScriptInterpreterPushWhileCtrlEntry(pThis, pStmt);
622 break;
623 }
624 case VDSCRIPTSTMTTYPE_RETURN:
625 {
626 /* Walk up the control stack until we reach a function cleanup entry. */
627 PVDSCRIPTINTERPCTRL pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl);
628 while ( pCtrl
629 && ( pCtrl->fEvalAst
630 || pCtrl->Ctrl.enmCtrlType != VDSCRIPTINTERPCTRLTYPE_FN_CALL_CLEANUP))
631 {
632 /* Cleanup up any compound statement scope. */
633 if ( !pCtrl->fEvalAst
634 && pCtrl->Ctrl.enmCtrlType == VDSCRIPTINTERPCTRLTYPE_COMPOUND)
635 vdScriptInterpreterScopeDestroyCurr(pThis);
636
637 vdScriptStackPop(&pThis->StackCtrl);
638 pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl);
639 }
640 AssertMsg(RT_VALID_PTR(pCtrl), ("Incorrect program, return outside of function\n"));
641 break;
642 }
643 case VDSCRIPTSTMTTYPE_FOR:
644 {
645 rc = vdScriptInterpreterPushForCtrlEntry(pThis, pStmt);
646 break;
647 }
648 case VDSCRIPTSTMTTYPE_CONTINUE:
649 {
650 /* Remove everything up to a loop control entry. */
651 PVDSCRIPTINTERPCTRL pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl);
652 while ( pCtrl
653 && ( pCtrl->fEvalAst
654 || pCtrl->Ctrl.enmCtrlType != VDSCRIPTINTERPCTRLTYPE_LOOP))
655 {
656 /* Cleanup up any compound statement scope. */
657 if ( !pCtrl->fEvalAst
658 && pCtrl->Ctrl.enmCtrlType == VDSCRIPTINTERPCTRLTYPE_COMPOUND)
659 vdScriptInterpreterScopeDestroyCurr(pThis);
660
661 vdScriptStackPop(&pThis->StackCtrl);
662 pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl);
663 }
664 AssertMsg(RT_VALID_PTR(pCtrl), ("Incorrect program, continue outside of loop\n"));
665
666 /* Put the conditionals for while and for loops onto the control stack again. */
667 PVDSCRIPTASTSTMT pLoopStmt = (PVDSCRIPTASTSTMT)pCtrl->Ctrl.pAstNode;
668
669 AssertMsg( pLoopStmt->enmStmtType == VDSCRIPTSTMTTYPE_WHILE
670 || pLoopStmt->enmStmtType == VDSCRIPTSTMTTYPE_FOR,
671 ("Invalid statement type, must be for or while loop\n"));
672
673 if (pLoopStmt->enmStmtType == VDSCRIPTSTMTTYPE_FOR)
674 rc = vdScriptInterpreterPushAstEntry(pThis, &pLoopStmt->For.pExprCond->Core);
675 else if (!pLoopStmt->While.fDoWhile)
676 rc = vdScriptInterpreterPushAstEntry(pThis, &pLoopStmt->While.pCond->Core);
677 break;
678 }
679 case VDSCRIPTSTMTTYPE_BREAK:
680 {
681 /* Remove everything including the loop control statement. */
682 PVDSCRIPTINTERPCTRL pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl);
683 while ( pCtrl
684 && ( pCtrl->fEvalAst
685 || pCtrl->Ctrl.enmCtrlType != VDSCRIPTINTERPCTRLTYPE_LOOP))
686 {
687 /* Cleanup up any compound statement scope. */
688 if ( !pCtrl->fEvalAst
689 && pCtrl->Ctrl.enmCtrlType == VDSCRIPTINTERPCTRLTYPE_COMPOUND)
690 vdScriptInterpreterScopeDestroyCurr(pThis);
691
692 vdScriptStackPop(&pThis->StackCtrl);
693 pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl);
694 }
695 AssertMsg(RT_VALID_PTR(pCtrl), ("Incorrect program, break outside of loop\n"));
696 vdScriptStackPop(&pThis->StackCtrl); /* Remove loop control statement. */
697 break;
698 }
699 case VDSCRIPTSTMTTYPE_CASE:
700 case VDSCRIPTSTMTTYPE_DEFAULT:
701 AssertMsgFailed(("TODO\n"));
702 break;
703 default:
704 AssertMsgFailed(("Invalid statement type: %d\n", pStmt->enmStmtType));
705 }
706
707 return rc;
708}
709
710/**
711 * Evaluates the given AST node.
712 *
713 * @returns VBox statuse code.
714 * @param pThis The interpreter context.
715 * @param pAstNode The AST node to interpret.
716 */
717static int vdScriptInterpreterEvaluateAst(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTASTCORE pAstNode)
718{
719 int rc = VERR_NOT_IMPLEMENTED;
720
721 switch (pAstNode->enmClass)
722 {
723 case VDSCRIPTASTCLASS_DECLARATION:
724 {
725 AssertMsgFailed(("TODO\n"));
726 break;
727 }
728 case VDSCRIPTASTCLASS_STATEMENT:
729 {
730 rc = vdScriptInterpreterEvaluateStatement(pThis, (PVDSCRIPTASTSTMT)pAstNode);
731 break;
732 }
733 case VDSCRIPTASTCLASS_EXPRESSION:
734 {
735 rc = vdScriptInterpreterEvaluateExpression(pThis, (PVDSCRIPTASTEXPR)pAstNode);
736 break;
737 }
738 /* These should never ever appear here. */
739 case VDSCRIPTASTCLASS_IDENTIFIER:
740 case VDSCRIPTASTCLASS_FUNCTION:
741 case VDSCRIPTASTCLASS_FUNCTIONARG:
742 case VDSCRIPTASTCLASS_INVALID:
743 default:
744 AssertMsgFailed(("Invalid AST node class: %d\n", pAstNode->enmClass));
745 }
746
747 return rc;
748}
749
750/**
751 * Evaluate a function call.
752 *
753 * @returns VBox status code.
754 * @param pThis The interpreter context.
755 * @param pFn The function execute.
756 */
757static int vdScriptInterpreterFnCall(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTFN pFn)
758{
759 int rc = VINF_SUCCESS;
760
761 if (!pFn->fExternal)
762 {
763 PVDSCRIPTASTFN pAstFn = pFn->Type.Internal.pAstFn;
764
765 /* Add function call cleanup marker on the stack first. */
766 rc = vdScriptInterpreterPushNonDataCtrlEntry(pThis, VDSCRIPTINTERPCTRLTYPE_FN_CALL_CLEANUP);
767 if (RT_SUCCESS(rc))
768 {
769 /* Create function call frame and set it up. */
770 PVDSCRIPTINTERPFNCALL pFnCall = (PVDSCRIPTINTERPFNCALL)RTMemAllocZ(sizeof(VDSCRIPTINTERPFNCALL));
771 if (pFnCall)
772 {
773 pFnCall->pCaller = pThis->pFnCallCurr;
774 pFnCall->ScopeRoot.pParent = NULL;
775 pFnCall->ScopeRoot.hStrSpaceVar = NULL;
776 pFnCall->pScopeCurr = &pFnCall->ScopeRoot;
777
778 /* Add the variables, remember order. The first variable in the argument has the value at the top of the value stack. */
779 PVDSCRIPTASTFNARG pArg = RTListGetFirst(&pAstFn->ListArgs, VDSCRIPTASTFNARG, Core.ListNode);
780 for (unsigned i = 0; i < pAstFn->cArgs; i++)
781 {
782 PVDSCRIPTINTERPVAR pVar = (PVDSCRIPTINTERPVAR)RTMemAllocZ(sizeof(VDSCRIPTINTERPVAR));
783 if (pVar)
784 {
785 pVar->Core.pszString = pArg->pArgIde->aszIde;
786 pVar->Core.cchString = pArg->pArgIde->cchIde;
787 vdScriptInterpreterPopValue(pThis, &pVar->Value);
788 bool fInserted = RTStrSpaceInsert(&pFnCall->ScopeRoot.hStrSpaceVar, &pVar->Core);
789 Assert(fInserted); RT_NOREF_PV(fInserted);
790 }
791 else
792 {
793 rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory creating a variable");
794 break;
795 }
796 pArg = RTListGetNext(&pAstFn->ListArgs, pArg, VDSCRIPTASTFNARG, Core.ListNode);
797 }
798
799 if (RT_SUCCESS(rc))
800 {
801 /*
802 * Push compount statement on the control stack and make the newly created
803 * call frame the current one.
804 */
805 rc = vdScriptInterpreterPushAstEntry(pThis, &pAstFn->pCompoundStmts->Core);
806 if (RT_SUCCESS(rc))
807 pThis->pFnCallCurr = pFnCall;
808 }
809
810 if (RT_FAILURE(rc))
811 {
812 RTStrSpaceDestroy(&pFnCall->ScopeRoot.hStrSpaceVar, vdScriptInterpreterVarSpaceDestroy, NULL);
813 RTMemFree(pFnCall);
814 }
815 }
816 else
817 rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS, "Out of memory creating a call frame");
818 }
819 }
820 else
821 {
822 /* External function call, build the argument list. */
823 if (pFn->cArgs)
824 {
825 PVDSCRIPTARG paArgs = (PVDSCRIPTARG)RTMemAllocZ(pFn->cArgs * sizeof(VDSCRIPTARG));
826 if (paArgs)
827 {
828 for (unsigned i = 0; i < pFn->cArgs; i++)
829 vdScriptInterpreterPopValue(pThis, &paArgs[i]);
830
831 rc = pFn->Type.External.pfnCallback(paArgs, pFn->Type.External.pvUser);
832 RTMemFree(paArgs);
833 }
834 else
835 rc = vdScriptInterpreterError(pThis, VERR_NO_MEMORY, RT_SRC_POS,
836 "Out of memory creating argument array for external function call");
837 }
838 else
839 rc = pFn->Type.External.pfnCallback(NULL, pFn->Type.External.pvUser);
840 }
841
842 return rc;
843}
844
845/**
846 * Evaluate interpreter control statement.
847 *
848 * @returns VBox status code.
849 * @param pThis The interpreter context.
850 * @param pCtrl The control entry to evaluate.
851 */
852static int vdScriptInterpreterEvaluateCtrlEntry(PVDSCRIPTINTERPCTX pThis, PVDSCRIPTINTERPCTRL pCtrl)
853{
854 int rc = VINF_SUCCESS;
855
856 Assert(!pCtrl->fEvalAst);
857 switch (pCtrl->Ctrl.enmCtrlType)
858 {
859 case VDSCRIPTINTERPCTRLTYPE_FN_CALL:
860 {
861 PVDSCRIPTFN pFn = pCtrl->Ctrl.FnCall.pFn;
862
863 vdScriptStackPop(&pThis->StackCtrl);
864 rc = vdScriptInterpreterFnCall(pThis, pFn);
865 break;
866 }
867 case VDSCRIPTINTERPCTRLTYPE_FN_CALL_CLEANUP:
868 {
869 vdScriptStackPop(&pThis->StackCtrl);
870
871 /* Delete function call entry. */
872 AssertPtr(pThis->pFnCallCurr);
873 PVDSCRIPTINTERPFNCALL pFnCallFree = pThis->pFnCallCurr;
874
875 pThis->pFnCallCurr = pFnCallFree->pCaller;
876 Assert(pFnCallFree->pScopeCurr == &pFnCallFree->ScopeRoot);
877 RTStrSpaceDestroy(&pFnCallFree->ScopeRoot.hStrSpaceVar, vdScriptInterpreterVarSpaceDestroy, NULL);
878 RTMemFree(pFnCallFree);
879 break;
880 }
881 case VDSCRIPTINTERPCTRLTYPE_COMPOUND:
882 {
883 if (!pCtrl->Ctrl.Compound.pStmtCurr)
884 {
885 /* Evaluated last statement, cleanup and remove the control statement from the stack. */
886 vdScriptInterpreterScopeDestroyCurr(pThis);
887 vdScriptStackPop(&pThis->StackCtrl);
888 }
889 else
890 {
891 /* Push the current statement onto the control stack and move on. */
892 rc = vdScriptInterpreterPushAstEntry(pThis, &pCtrl->Ctrl.Compound.pStmtCurr->Core);
893 if (RT_SUCCESS(rc))
894 {
895 pCtrl->Ctrl.Compound.pStmtCurr = RTListGetNext(&pCtrl->Ctrl.Compound.pStmtCompound->Compound.ListStmts,
896 pCtrl->Ctrl.Compound.pStmtCurr, VDSCRIPTASTSTMT, Core.ListNode);
897 }
898 }
899 break;
900 }
901 case VDSCRIPTINTERPCTRLTYPE_LOOP:
902 {
903 PVDSCRIPTASTSTMT pLoopStmt = (PVDSCRIPTASTSTMT)pCtrl->Ctrl.pAstNode;
904
905 /* Check whether the condition passed. */
906 VDSCRIPTARG Cond;
907 vdScriptInterpreterPopValue(pThis, &Cond);
908 AssertMsg(Cond.enmType == VDSCRIPTTYPE_BOOL,
909 ("Value on stack is not of boolean type\n"));
910
911 if (Cond.f)
912 {
913 /* Execute the loop another round. */
914 if (pLoopStmt->enmStmtType == VDSCRIPTSTMTTYPE_WHILE)
915 {
916 rc = vdScriptInterpreterPushAstEntry(pThis, &pLoopStmt->While.pCond->Core);
917 if (RT_SUCCESS(rc))
918 {
919 rc = vdScriptInterpreterPushAstEntry(pThis, &pLoopStmt->While.pStmt->Core);
920 if (RT_FAILURE(rc))
921 vdScriptStackPop(&pThis->StackCtrl);
922 }
923 }
924 else
925 {
926 AssertMsg(pLoopStmt->enmStmtType == VDSCRIPTSTMTTYPE_FOR,
927 ("Not a for statement\n"));
928
929 rc = vdScriptInterpreterPushAstEntry(pThis, &pLoopStmt->For.pExprCond->Core);
930 if (RT_SUCCESS(rc))
931 {
932 rc = vdScriptInterpreterPushAstEntry(pThis, &pLoopStmt->For.pExpr3->Core);
933 if (RT_SUCCESS(rc))
934 {
935 rc = vdScriptInterpreterPushAstEntry(pThis, &pLoopStmt->For.pStmt->Core);
936 if (RT_FAILURE(rc))
937 vdScriptStackPop(&pThis->StackCtrl);
938 }
939
940 if (RT_FAILURE(rc))
941 vdScriptStackPop(&pThis->StackCtrl);
942 }
943 }
944 }
945 else
946 vdScriptStackPop(&pThis->StackCtrl); /* Remove loop control statement. */
947 break;
948 }
949 case VDSCRIPTINTERPCTRLTYPE_IF:
950 {
951 PVDSCRIPTASTSTMT pIfStmt = (PVDSCRIPTASTSTMT)pCtrl->Ctrl.pAstNode;
952
953 vdScriptStackPop(&pThis->StackCtrl); /* Remove if control statement. */
954
955 /* Check whether the condition passed. */
956 VDSCRIPTARG Cond;
957 vdScriptInterpreterPopValue(pThis, &Cond);
958 AssertMsg(Cond.enmType == VDSCRIPTTYPE_BOOL,
959 ("Value on stack is not of boolean type\n"));
960
961 if (Cond.f)
962 {
963 /* Execute the true branch. */
964 rc = vdScriptInterpreterPushAstEntry(pThis, &pIfStmt->If.pTrueStmt->Core);
965 }
966 else if (pIfStmt->If.pElseStmt)
967 rc = vdScriptInterpreterPushAstEntry(pThis, &pIfStmt->If.pElseStmt->Core);
968
969 break;
970 }
971 default:
972 AssertMsgFailed(("Invalid evaluation control type on the stack: %d\n",
973 pCtrl->Ctrl.enmCtrlType));
974 }
975
976 return rc;
977}
978
979/**
980 * The interpreter evaluation core loop.
981 *
982 * @returns VBox status code.
983 * @param pThis The interpreter context.
984 */
985static int vdScriptInterpreterEvaluate(PVDSCRIPTINTERPCTX pThis)
986{
987 int rc = VINF_SUCCESS;
988 PVDSCRIPTINTERPCTRL pCtrl = NULL;
989
990 pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl);
991 while (pCtrl)
992 {
993 if (pCtrl->fEvalAst)
994 {
995 PVDSCRIPTASTCORE pAstNode = pCtrl->pAstNode;
996 vdScriptStackPop(&pThis->StackCtrl);
997
998 rc = vdScriptInterpreterEvaluateAst(pThis, pAstNode);
999 }
1000 else
1001 rc = vdScriptInterpreterEvaluateCtrlEntry(pThis, pCtrl);
1002
1003 pCtrl = (PVDSCRIPTINTERPCTRL)vdScriptStackGetUsed(&pThis->StackCtrl);
1004 }
1005
1006 return rc;
1007}
1008
1009DECLHIDDEN(int) vdScriptCtxInterprete(PVDSCRIPTCTXINT pThis, const char *pszFn,
1010 PVDSCRIPTARG paArgs, unsigned cArgs,
1011 PVDSCRIPTARG pRet)
1012{
1013 RT_NOREF1(pRet);
1014 int rc = VINF_SUCCESS;
1015 VDSCRIPTINTERPCTX InterpCtx;
1016 PVDSCRIPTFN pFn = NULL;
1017
1018 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1019 AssertPtrReturn(pszFn, VERR_INVALID_POINTER);
1020 AssertReturn( (!cArgs && !paArgs)
1021 || (cArgs && paArgs), VERR_INVALID_PARAMETER);
1022
1023 InterpCtx.pScriptCtx = pThis;
1024 InterpCtx.pFnCallCurr = NULL;
1025 vdScriptStackInit(&InterpCtx.StackValues, sizeof(VDSCRIPTARG));
1026 vdScriptStackInit(&InterpCtx.StackCtrl, sizeof(VDSCRIPTINTERPCTRL));
1027
1028 pFn = (PVDSCRIPTFN)RTStrSpaceGet(&pThis->hStrSpaceFn, pszFn);
1029 if (pFn)
1030 {
1031 if (cArgs == pFn->cArgs)
1032 {
1033 /* Push the arguments onto the stack. */
1034 /** @todo Check expected and given argument types. */
1035 for (unsigned i = 0; i < cArgs; i++)
1036 {
1037 PVDSCRIPTARG pArg = (PVDSCRIPTARG)vdScriptStackGetUnused(&InterpCtx.StackValues);
1038 *pArg = paArgs[i];
1039 vdScriptStackPush(&InterpCtx.StackValues);
1040 }
1041
1042 if (RT_SUCCESS(rc))
1043 {
1044 /* Setup function call frame and parameters. */
1045 rc = vdScriptInterpreterFnCall(&InterpCtx, pFn);
1046 if (RT_SUCCESS(rc))
1047 {
1048 /* Run the interpreter. */
1049 rc = vdScriptInterpreterEvaluate(&InterpCtx);
1050 vdScriptStackDestroy(&InterpCtx.StackValues);
1051 vdScriptStackDestroy(&InterpCtx.StackCtrl);
1052 }
1053 }
1054 }
1055 else
1056 rc = vdScriptInterpreterError(&InterpCtx, VERR_INVALID_PARAMETER, RT_SRC_POS, "Invalid number of parameters, expected %d got %d", pFn->cArgs, cArgs);
1057 }
1058 else
1059 rc = vdScriptInterpreterError(&InterpCtx, VERR_NOT_FOUND, RT_SRC_POS, "Function with identifier \"%s\" not found", pszFn);
1060
1061
1062 return rc;
1063}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette