VirtualBox

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

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