VirtualBox

source: vbox/trunk/src/VBox/Debugger/DBGCEval.cpp@ 35628

Last change on this file since 35628 was 35628, checked in by vboxsync, 14 years ago

Debugger Console: Clean up, split the command evaluation code out of DBGConsole.cpp and into DBGCEval.cpp.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 37.8 KB
Line 
1/* $Id: DBGCEval.cpp 35628 2011-01-19 14:58:26Z vboxsync $ */
2/** @file
3 * DBGC - Debugger Console, command evaluator.
4 */
5
6/*
7 * Copyright (C) 2006-2011 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/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#define LOG_GROUP LOG_GROUP_DBGC
22#include <VBox/dbg.h>
23#include <VBox/err.h>
24#include <VBox/log.h>
25
26#include <iprt/asm.h>
27#include <iprt/assert.h>
28#include <iprt/string.h>
29#include <iprt/ctype.h>
30
31#include "DBGCInternal.h"
32
33
34/*******************************************************************************
35* Global Variables *
36*******************************************************************************/
37/** Bitmap where set bits indicates the characters the may start an operator name. */
38static uint32_t g_bmOperatorChars[256 / (4*8)];
39
40
41
42/**
43 * Initializes g_bmOperatorChars.
44 */
45void dbgcEvalInit(void)
46{
47 memset(g_bmOperatorChars, 0, sizeof(g_bmOperatorChars));
48 for (unsigned iOp = 0; iOp < g_cOps; iOp++)
49 ASMBitSet(&g_bmOperatorChars[0], (uint8_t)g_aOps[iOp].szName[0]);
50}
51
52
53/**
54 * Checks whether the character may be the start of an operator.
55 *
56 * @returns true/false.
57 * @param ch The character.
58 */
59DECLINLINE(bool) dbgcIsOpChar(char ch)
60{
61 return ASMBitTest(&g_bmOperatorChars[0], (uint8_t)ch);
62}
63
64
65static int dbgcEvalSubString(PDBGC pDbgc, char *pszExpr, size_t cchExpr, PDBGCVAR pArg)
66{
67 Log2(("dbgcEvalSubString: cchExpr=%d pszExpr=%s\n", cchExpr, pszExpr));
68
69 /*
70 * Removing any quoting and escapings.
71 */
72 char ch = *pszExpr;
73 if (ch == '"' || ch == '\'' || ch == '`')
74 {
75 if (pszExpr[--cchExpr] != ch)
76 return VERR_PARSE_UNBALANCED_QUOTE;
77 cchExpr--;
78 pszExpr++;
79
80 /** @todo string unescaping. */
81 }
82 pszExpr[cchExpr] = '\0';
83
84 /*
85 * Make the argument.
86 */
87 pArg->pDesc = NULL;
88 pArg->pNext = NULL;
89 pArg->enmType = DBGCVAR_TYPE_STRING;
90 pArg->u.pszString = pszExpr;
91 pArg->enmRangeType = DBGCVAR_RANGE_BYTES;
92 pArg->u64Range = cchExpr;
93
94 NOREF(pDbgc);
95 return 0;
96}
97
98
99static int dbgcEvalSubNum(char *pszExpr, unsigned uBase, PDBGCVAR pArg)
100{
101 Log2(("dbgcEvalSubNum: uBase=%d pszExpr=%s\n", uBase, pszExpr));
102 /*
103 * Convert to number.
104 */
105 uint64_t u64 = 0;
106 char ch;
107 while ((ch = *pszExpr) != '\0')
108 {
109 uint64_t u64Prev = u64;
110 unsigned u = ch - '0';
111 if (u < 10 && u < uBase)
112 u64 = u64 * uBase + u;
113 else if (ch >= 'a' && (u = ch - ('a' - 10)) < uBase)
114 u64 = u64 * uBase + u;
115 else if (ch >= 'A' && (u = ch - ('A' - 10)) < uBase)
116 u64 = u64 * uBase + u;
117 else
118 return VERR_PARSE_INVALID_NUMBER;
119
120 /* check for overflow - ARG!!! How to detect overflow correctly!?!?!? */
121 if (u64Prev != u64 / uBase)
122 return VERR_PARSE_NUMBER_TOO_BIG;
123
124 /* next */
125 pszExpr++;
126 }
127
128 /*
129 * Initialize the argument.
130 */
131 pArg->pDesc = NULL;
132 pArg->pNext = NULL;
133 pArg->enmType = DBGCVAR_TYPE_NUMBER;
134 pArg->u.u64Number = u64;
135 pArg->enmRangeType = DBGCVAR_RANGE_NONE;
136 pArg->u64Range = 0;
137
138 return 0;
139}
140
141
142/**
143 * Match variable and variable descriptor, promoting the variable if necessary.
144 *
145 * @returns VBox status code.
146 * @param pDbgc Debug console instanace.
147 * @param pVar Variable.
148 * @param pVarDesc Variable descriptor.
149 */
150static int dbgcEvalSubMatchVar(PDBGC pDbgc, PDBGCVAR pVar, PCDBGCVARDESC pVarDesc)
151{
152 /*
153 * (If match or promoted to match, return, else break.)
154 */
155 switch (pVarDesc->enmCategory)
156 {
157 /*
158 * Anything goes
159 */
160 case DBGCVAR_CAT_ANY:
161 return VINF_SUCCESS;
162
163 /*
164 * Pointer with and without range.
165 * We can try resolve strings and symbols as symbols and
166 * promote numbers to flat GC pointers.
167 */
168 case DBGCVAR_CAT_POINTER_NO_RANGE:
169 if (pVar->enmRangeType != DBGCVAR_RANGE_NONE)
170 return VERR_PARSE_NO_RANGE_ALLOWED;
171 /* fallthru */
172 case DBGCVAR_CAT_POINTER:
173 switch (pVar->enmType)
174 {
175 case DBGCVAR_TYPE_GC_FLAT:
176 case DBGCVAR_TYPE_GC_FAR:
177 case DBGCVAR_TYPE_GC_PHYS:
178 case DBGCVAR_TYPE_HC_FLAT:
179 case DBGCVAR_TYPE_HC_PHYS:
180 return VINF_SUCCESS;
181
182 case DBGCVAR_TYPE_SYMBOL:
183 case DBGCVAR_TYPE_STRING:
184 {
185 DBGCVAR Var;
186 int rc = dbgcSymbolGet(pDbgc, pVar->u.pszString, DBGCVAR_TYPE_GC_FLAT, &Var);
187 if (RT_SUCCESS(rc))
188 {
189 /* deal with range */
190 if (pVar->enmRangeType != DBGCVAR_RANGE_NONE)
191 {
192 Var.enmRangeType = pVar->enmRangeType;
193 Var.u64Range = pVar->u64Range;
194 }
195 else if (pVarDesc->enmCategory == DBGCVAR_CAT_POINTER_NO_RANGE)
196 Var.enmRangeType = DBGCVAR_RANGE_NONE;
197 *pVar = Var;
198 return rc;
199 }
200 break;
201 }
202
203 case DBGCVAR_TYPE_NUMBER:
204 {
205 RTGCPTR GCPtr = (RTGCPTR)pVar->u.u64Number;
206 pVar->enmType = DBGCVAR_TYPE_GC_FLAT;
207 pVar->u.GCFlat = GCPtr;
208 return VINF_SUCCESS;
209 }
210
211 default:
212 break;
213 }
214 break;
215
216 /*
217 * GC pointer with and without range.
218 * We can try resolve strings and symbols as symbols and
219 * promote numbers to flat GC pointers.
220 */
221 case DBGCVAR_CAT_GC_POINTER_NO_RANGE:
222 if (pVar->enmRangeType != DBGCVAR_RANGE_NONE)
223 return VERR_PARSE_NO_RANGE_ALLOWED;
224 /* fallthru */
225 case DBGCVAR_CAT_GC_POINTER:
226 switch (pVar->enmType)
227 {
228 case DBGCVAR_TYPE_GC_FLAT:
229 case DBGCVAR_TYPE_GC_FAR:
230 case DBGCVAR_TYPE_GC_PHYS:
231 return VINF_SUCCESS;
232
233 case DBGCVAR_TYPE_HC_FLAT:
234 case DBGCVAR_TYPE_HC_PHYS:
235 return VERR_PARSE_CONVERSION_FAILED;
236
237 case DBGCVAR_TYPE_SYMBOL:
238 case DBGCVAR_TYPE_STRING:
239 {
240 DBGCVAR Var;
241 int rc = dbgcSymbolGet(pDbgc, pVar->u.pszString, DBGCVAR_TYPE_GC_FLAT, &Var);
242 if (RT_SUCCESS(rc))
243 {
244 /* deal with range */
245 if (pVar->enmRangeType != DBGCVAR_RANGE_NONE)
246 {
247 Var.enmRangeType = pVar->enmRangeType;
248 Var.u64Range = pVar->u64Range;
249 }
250 else if (pVarDesc->enmCategory == DBGCVAR_CAT_POINTER_NO_RANGE)
251 Var.enmRangeType = DBGCVAR_RANGE_NONE;
252 *pVar = Var;
253 return rc;
254 }
255 break;
256 }
257
258 case DBGCVAR_TYPE_NUMBER:
259 {
260 RTGCPTR GCPtr = (RTGCPTR)pVar->u.u64Number;
261 pVar->enmType = DBGCVAR_TYPE_GC_FLAT;
262 pVar->u.GCFlat = GCPtr;
263 return VINF_SUCCESS;
264 }
265
266 default:
267 break;
268 }
269 break;
270
271 /*
272 * Number with or without a range.
273 * Numbers can be resolved from symbols, but we cannot demote a pointer
274 * to a number.
275 */
276 case DBGCVAR_CAT_NUMBER_NO_RANGE:
277 if (pVar->enmRangeType != DBGCVAR_RANGE_NONE)
278 return VERR_PARSE_NO_RANGE_ALLOWED;
279 /* fallthru */
280 case DBGCVAR_CAT_NUMBER:
281 switch (pVar->enmType)
282 {
283 case DBGCVAR_TYPE_NUMBER:
284 return VINF_SUCCESS;
285
286 case DBGCVAR_TYPE_SYMBOL:
287 case DBGCVAR_TYPE_STRING:
288 {
289 DBGCVAR Var;
290 int rc = dbgcSymbolGet(pDbgc, pVar->u.pszString, DBGCVAR_TYPE_NUMBER, &Var);
291 if (RT_SUCCESS(rc))
292 {
293 *pVar = Var;
294 return rc;
295 }
296 break;
297 }
298 default:
299 break;
300 }
301 break;
302
303 /*
304 * Strings can easily be made from symbols (and of course strings).
305 * We could consider reformatting the addresses and numbers into strings later...
306 */
307 case DBGCVAR_CAT_STRING:
308 switch (pVar->enmType)
309 {
310 case DBGCVAR_TYPE_SYMBOL:
311 pVar->enmType = DBGCVAR_TYPE_STRING;
312 /* fallthru */
313 case DBGCVAR_TYPE_STRING:
314 return VINF_SUCCESS;
315 default:
316 break;
317 }
318 break;
319
320 /*
321 * Symol is pretty much the same thing as a string (at least until we actually implement it).
322 */
323 case DBGCVAR_CAT_SYMBOL:
324 switch (pVar->enmType)
325 {
326 case DBGCVAR_TYPE_STRING:
327 pVar->enmType = DBGCVAR_TYPE_SYMBOL;
328 /* fallthru */
329 case DBGCVAR_TYPE_SYMBOL:
330 return VINF_SUCCESS;
331 default:
332 break;
333 }
334 break;
335
336 /*
337 * Anything else is illegal.
338 */
339 default:
340 AssertMsgFailed(("enmCategory=%d\n", pVar->enmType));
341 break;
342 }
343
344 return VERR_PARSE_NO_ARGUMENT_MATCH;
345}
346
347
348/**
349 * Matches a set of variables with a description set.
350 *
351 * This is typically used for routine arguments before a call. The effects in
352 * addition to the validation, is that some variables might be propagated to
353 * other types in order to match the description. The following transformations
354 * are supported:
355 * - String reinterpreted as a symbol and resolved to a number or pointer.
356 * - Number to a pointer.
357 * - Pointer to a number.
358 * @returns 0 on success with paVars.
359 * @returns VBox error code for match errors.
360 */
361static int dbgcEvalSubMatchVars(PDBGC pDbgc, unsigned cVarsMin, unsigned cVarsMax,
362 PCDBGCVARDESC paVarDescs, unsigned cVarDescs,
363 PDBGCVAR paVars, unsigned cVars)
364{
365 /*
366 * Just do basic min / max checks first.
367 */
368 if (cVars < cVarsMin)
369 return VERR_PARSE_TOO_FEW_ARGUMENTS;
370 if (cVars > cVarsMax)
371 return VERR_PARSE_TOO_MANY_ARGUMENTS;
372
373 /*
374 * Match the descriptors and actual variables.
375 */
376 PCDBGCVARDESC pPrevDesc = NULL;
377 unsigned cCurDesc = 0;
378 unsigned iVar = 0;
379 unsigned iVarDesc = 0;
380 while (iVar < cVars)
381 {
382 /* walk the descriptors */
383 if (iVarDesc >= cVarDescs)
384 return VERR_PARSE_TOO_MANY_ARGUMENTS;
385 if ( ( paVarDescs[iVarDesc].fFlags & DBGCVD_FLAGS_DEP_PREV
386 && &paVarDescs[iVarDesc - 1] != pPrevDesc)
387 || cCurDesc >= paVarDescs[iVarDesc].cTimesMax)
388 {
389 iVarDesc++;
390 if (iVarDesc >= cVarDescs)
391 return VERR_PARSE_TOO_MANY_ARGUMENTS;
392 cCurDesc = 0;
393 }
394
395 /*
396 * Skip thru optional arguments until we find something which matches
397 * or can easily be promoted to what the descriptor want.
398 */
399 for (;;)
400 {
401 int rc = dbgcEvalSubMatchVar(pDbgc, &paVars[iVar], &paVarDescs[iVarDesc]);
402 if (RT_SUCCESS(rc))
403 {
404 paVars[iVar].pDesc = &paVarDescs[iVarDesc];
405 cCurDesc++;
406 break;
407 }
408
409 /* can we advance? */
410 if (paVarDescs[iVarDesc].cTimesMin > cCurDesc)
411 return VERR_PARSE_ARGUMENT_TYPE_MISMATCH;
412 if (++iVarDesc >= cVarDescs)
413 return VERR_PARSE_ARGUMENT_TYPE_MISMATCH;
414 cCurDesc = 0;
415 }
416
417 /* next var */
418 iVar++;
419 }
420
421 /*
422 * Check that the rest of the descriptors are optional.
423 */
424 while (iVarDesc < cVarDescs)
425 {
426 if (paVarDescs[iVarDesc].cTimesMin > cCurDesc)
427 return VERR_PARSE_TOO_FEW_ARGUMENTS;
428 cCurDesc = 0;
429
430 /* next */
431 iVarDesc++;
432 }
433
434 return 0;
435}
436
437
438/**
439 * Evaluates one argument with respect to unary operators.
440 *
441 * @returns 0 on success. pResult contains the result.
442 * @returns VBox error code on parse or other evaluation error.
443 *
444 * @param pDbgc Debugger console instance data.
445 * @param pszExpr The expression string.
446 * @param pResult Where to store the result of the expression evaluation.
447 */
448static int dbgcEvalSubUnary(PDBGC pDbgc, char *pszExpr, size_t cchExpr, PDBGCVAR pResult)
449{
450 Log2(("dbgcEvalSubUnary: cchExpr=%d pszExpr=%s\n", cchExpr, pszExpr));
451
452 /*
453 * The state of the expression is now such that it will start by zero or more
454 * unary operators and being followed by an expression of some kind.
455 * The expression is either plain or in parenthesis.
456 *
457 * Being in a lazy, recursive mode today, the parsing is done as simple as possible. :-)
458 * ASSUME: unary operators are all of equal precedence.
459 */
460 int rc = 0;
461 PCDBGCOP pOp = dbgcOperatorLookup(pDbgc, pszExpr, false, ' ');
462 if (pOp)
463 {
464 /* binary operators means syntax error. */
465 if (pOp->fBinary)
466 return VERR_PARSE_UNEXPECTED_OPERATOR;
467
468 /*
469 * If the next expression (the one following the unary operator) is in a
470 * parenthesis a full eval is needed. If not the unary eval will suffice.
471 */
472 /* calc and strip next expr. */
473 char *pszExpr2 = pszExpr + pOp->cchName;
474 while (RT_C_IS_BLANK(*pszExpr2))
475 pszExpr2++;
476
477 if (!*pszExpr2)
478 rc = VERR_PARSE_EMPTY_ARGUMENT;
479 else
480 {
481 DBGCVAR Arg;
482 if (*pszExpr2 == '(')
483 rc = dbgcEvalSub(pDbgc, pszExpr2, cchExpr - (pszExpr2 - pszExpr), &Arg);
484 else
485 rc = dbgcEvalSubUnary(pDbgc, pszExpr2, cchExpr - (pszExpr2 - pszExpr), &Arg);
486 if (RT_SUCCESS(rc))
487 rc = pOp->pfnHandlerUnary(pDbgc, &Arg, pResult);
488 }
489 }
490 else
491 {
492 /*
493 * Didn't find any operators, so it we have to check if this can be an
494 * function call before assuming numeric or string expression.
495 *
496 * (ASSUMPTIONS:)
497 * A function name only contains alphanumerical chars and it can not start
498 * with a numerical character.
499 * Immediately following the name is a parenthesis which must over
500 * the remaining part of the expression.
501 */
502 bool fExternal = *pszExpr == '.';
503 char *pszFun = fExternal ? pszExpr + 1 : pszExpr;
504 char *pszFunEnd = NULL;
505 if (pszExpr[cchExpr - 1] == ')' && RT_C_IS_ALPHA(*pszFun))
506 {
507 pszFunEnd = pszExpr + 1;
508 while (*pszFunEnd != '(' && RT_C_IS_ALNUM(*pszFunEnd))
509 pszFunEnd++;
510 if (*pszFunEnd != '(')
511 pszFunEnd = NULL;
512 }
513
514 if (pszFunEnd)
515 {
516 /*
517 * Ok, it's a function call.
518 */
519 if (fExternal)
520 pszExpr++, cchExpr--;
521 PCDBGCCMD pFun = dbgcRoutineLookup(pDbgc, pszExpr, pszFunEnd - pszExpr, fExternal);
522 if (!pFun)
523 return VERR_PARSE_FUNCTION_NOT_FOUND;
524 if (!pFun->pResultDesc)
525 return VERR_PARSE_NOT_A_FUNCTION;
526
527 /*
528 * Parse the expression in parenthesis.
529 */
530 cchExpr -= pszFunEnd - pszExpr;
531 pszExpr = pszFunEnd;
532 /** @todo implement multiple arguments. */
533 DBGCVAR Arg;
534 rc = dbgcEvalSub(pDbgc, pszExpr, cchExpr, &Arg);
535 if (!rc)
536 {
537 rc = dbgcEvalSubMatchVars(pDbgc, pFun->cArgsMin, pFun->cArgsMax, pFun->paArgDescs, pFun->cArgDescs, &Arg, 1);
538 if (!rc)
539 rc = pFun->pfnHandler(pFun, &pDbgc->CmdHlp, pDbgc->pVM, &Arg, 1, pResult);
540 }
541 else if (rc == VERR_PARSE_EMPTY_ARGUMENT && pFun->cArgsMin == 0)
542 rc = pFun->pfnHandler(pFun, &pDbgc->CmdHlp, pDbgc->pVM, NULL, 0, pResult);
543 }
544 else
545 {
546 /*
547 * Didn't find any operators, so it must be a plain expression.
548 * This might be numeric or a string expression.
549 */
550 char ch = pszExpr[0];
551 char ch2 = pszExpr[1];
552 if (ch == '0' && (ch2 == 'x' || ch2 == 'X'))
553 rc = dbgcEvalSubNum(pszExpr + 2, 16, pResult);
554 else if (ch == '0' && (ch2 == 'i' || ch2 == 'i'))
555 rc = dbgcEvalSubNum(pszExpr + 2, 10, pResult);
556 else if (ch == '0' && (ch2 == 't' || ch2 == 'T'))
557 rc = dbgcEvalSubNum(pszExpr + 2, 8, pResult);
558 /// @todo 0b doesn't work as a binary prefix, we confuse it with 0bf8:0123 and stuff.
559 //else if (ch == '0' && (ch2 == 'b' || ch2 == 'b'))
560 // rc = dbgcEvalSubNum(pszExpr + 2, 2, pResult);
561 else
562 {
563 /*
564 * Hexadecimal number or a string?
565 */
566 char *psz = pszExpr;
567 while (RT_C_IS_XDIGIT(*psz))
568 psz++;
569 if (!*psz)
570 rc = dbgcEvalSubNum(pszExpr, 16, pResult);
571 else if ((*psz == 'h' || *psz == 'H') && !psz[1])
572 {
573 *psz = '\0';
574 rc = dbgcEvalSubNum(pszExpr, 16, pResult);
575 }
576 else
577 rc = dbgcEvalSubString(pDbgc, pszExpr, cchExpr, pResult);
578 }
579 }
580 }
581
582 return rc;
583}
584
585
586/**
587 * Evaluates one argument.
588 *
589 * @returns 0 on success. pResult contains the result.
590 * @returns VBox error code on parse or other evaluation error.
591 *
592 * @param pDbgc Debugger console instance data.
593 * @param pszExpr The expression string.
594 * @param pResult Where to store the result of the expression evaluation.
595 */
596int dbgcEvalSub(PDBGC pDbgc, char *pszExpr, size_t cchExpr, PDBGCVAR pResult)
597{
598 Log2(("dbgcEvalSub: cchExpr=%d pszExpr=%s\n", cchExpr, pszExpr));
599 /*
600 * First we need to remove blanks in both ends.
601 * ASSUMES: There is no quoting unless the entire expression is a string.
602 */
603
604 /* stripping. */
605 while (cchExpr > 0 && RT_C_IS_BLANK(pszExpr[cchExpr - 1]))
606 pszExpr[--cchExpr] = '\0';
607 while (RT_C_IS_BLANK(*pszExpr))
608 pszExpr++, cchExpr--;
609 if (!*pszExpr)
610 return VERR_PARSE_EMPTY_ARGUMENT;
611
612 /* it there is any kind of quoting in the expression, it's string meat. */
613 if (strpbrk(pszExpr, "\"'`"))
614 return dbgcEvalSubString(pDbgc, pszExpr, cchExpr, pResult);
615
616 /*
617 * Check if there are any parenthesis which needs removing.
618 */
619 if (pszExpr[0] == '(' && pszExpr[cchExpr - 1] == ')')
620 {
621 do
622 {
623 unsigned cPar = 1;
624 char *psz = pszExpr + 1;
625 char ch;
626 while ((ch = *psz) != '\0')
627 {
628 if (ch == '(')
629 cPar++;
630 else if (ch == ')')
631 {
632 if (cPar <= 0)
633 return VERR_PARSE_UNBALANCED_PARENTHESIS;
634 cPar--;
635 if (cPar == 0 && psz[1]) /* If not at end, there's nothing to do. */
636 break;
637 }
638 /* next */
639 psz++;
640 }
641 if (ch)
642 break;
643
644 /* remove the parenthesis. */
645 pszExpr++;
646 cchExpr -= 2;
647 pszExpr[cchExpr] = '\0';
648
649 /* strip blanks. */
650 while (cchExpr > 0 && RT_C_IS_BLANK(pszExpr[cchExpr - 1]))
651 pszExpr[--cchExpr] = '\0';
652 while (RT_C_IS_BLANK(*pszExpr))
653 pszExpr++, cchExpr--;
654 if (!*pszExpr)
655 return VERR_PARSE_EMPTY_ARGUMENT;
656 } while (pszExpr[0] == '(' && pszExpr[cchExpr - 1] == ')');
657 }
658
659 /* tabs to spaces. */
660 char *psz = pszExpr;
661 while ((psz = strchr(psz, '\t')) != NULL)
662 *psz = ' ';
663
664 /*
665 * Now, we need to look for the binary operator with the lowest precedence.
666 *
667 * If there are no operators we're left with a simple expression which we
668 * evaluate with respect to unary operators
669 */
670 char *pszOpSplit = NULL;
671 PCDBGCOP pOpSplit = NULL;
672 unsigned cBinaryOps = 0;
673 unsigned cPar = 0;
674 char ch;
675 char chPrev = ' ';
676 bool fBinary = false;
677 psz = pszExpr;
678
679 while ((ch = *psz) != '\0')
680 {
681 //Log2(("ch=%c cPar=%d fBinary=%d\n", ch, cPar, fBinary));
682 /*
683 * Parenthesis.
684 */
685 if (ch == '(')
686 {
687 cPar++;
688 fBinary = false;
689 }
690 else if (ch == ')')
691 {
692 if (cPar <= 0)
693 return VERR_PARSE_UNBALANCED_PARENTHESIS;
694 cPar--;
695 fBinary = true;
696 }
697 /*
698 * Potential operator.
699 */
700 else if (cPar == 0 && !RT_C_IS_BLANK(ch))
701 {
702 PCDBGCOP pOp = dbgcIsOpChar(ch)
703 ? dbgcOperatorLookup(pDbgc, psz, fBinary, chPrev)
704 : NULL;
705 if (pOp)
706 {
707 /* If not the right kind of operator we've got a syntax error. */
708 if (pOp->fBinary != fBinary)
709 return VERR_PARSE_UNEXPECTED_OPERATOR;
710
711 /*
712 * Update the parse state and skip the operator.
713 */
714 if (!pOpSplit)
715 {
716 pOpSplit = pOp;
717 pszOpSplit = psz;
718 cBinaryOps = fBinary;
719 }
720 else if (fBinary)
721 {
722 cBinaryOps++;
723 if (pOp->iPrecedence >= pOpSplit->iPrecedence)
724 {
725 pOpSplit = pOp;
726 pszOpSplit = psz;
727 }
728 }
729
730 psz += pOp->cchName - 1;
731 fBinary = false;
732 }
733 else
734 fBinary = true;
735 }
736
737 /* next */
738 psz++;
739 chPrev = ch;
740 } /* parse loop. */
741
742
743 /*
744 * Either we found an operator to divide the expression by
745 * or we didn't find any. In the first case it's divide and
746 * conquer. In the latter it's a single expression which
747 * needs dealing with its unary operators if any.
748 */
749 int rc;
750 if ( cBinaryOps
751 && pOpSplit->fBinary)
752 {
753 /* process 1st sub expression. */
754 *pszOpSplit = '\0';
755 DBGCVAR Arg1;
756 rc = dbgcEvalSub(pDbgc, pszExpr, pszOpSplit - pszExpr, &Arg1);
757 if (RT_SUCCESS(rc))
758 {
759 /* process 2nd sub expression. */
760 char *psz2 = pszOpSplit + pOpSplit->cchName;
761 DBGCVAR Arg2;
762 rc = dbgcEvalSub(pDbgc, psz2, cchExpr - (psz2 - pszExpr), &Arg2);
763 if (RT_SUCCESS(rc))
764 /* apply the operator. */
765 rc = pOpSplit->pfnHandlerBinary(pDbgc, &Arg1, &Arg2, pResult);
766 }
767 }
768 else if (cBinaryOps)
769 {
770 /* process sub expression. */
771 pszOpSplit += pOpSplit->cchName;
772 DBGCVAR Arg;
773 rc = dbgcEvalSub(pDbgc, pszOpSplit, cchExpr - (pszOpSplit - pszExpr), &Arg);
774 if (RT_SUCCESS(rc))
775 /* apply the operator. */
776 rc = pOpSplit->pfnHandlerUnary(pDbgc, &Arg, pResult);
777 }
778 else
779 /* plain expression or using unary operators perhaps with parentheses. */
780 rc = dbgcEvalSubUnary(pDbgc, pszExpr, cchExpr, pResult);
781
782 return rc;
783}
784
785
786/**
787 * Parses the arguments of one command.
788 *
789 * @returns 0 on success.
790 * @returns VBox error code on parse error with *pcArg containing the argument causing trouble.
791 * @param pDbgc Debugger console instance data.
792 * @param pCmd Pointer to the command descriptor.
793 * @param pszArg Pointer to the arguments to parse.
794 * @param paArgs Where to store the parsed arguments.
795 * @param cArgs Size of the paArgs array.
796 * @param pcArgs Where to store the number of arguments.
797 * In the event of an error this is used to store the index of the offending argument.
798 */
799static int dbgcProcessArguments(PDBGC pDbgc, PCDBGCCMD pCmd, char *pszArgs, PDBGCVAR paArgs, unsigned cArgs, unsigned *pcArgs)
800{
801 Log2(("dbgcProcessArguments: pCmd=%s pszArgs='%s'\n", pCmd->pszCmd, pszArgs));
802 /*
803 * Check if we have any argument and if the command takes any.
804 */
805 *pcArgs = 0;
806 /* strip leading blanks. */
807 while (*pszArgs && RT_C_IS_BLANK(*pszArgs))
808 pszArgs++;
809 if (!*pszArgs)
810 {
811 if (!pCmd->cArgsMin)
812 return 0;
813 return VERR_PARSE_TOO_FEW_ARGUMENTS;
814 }
815 /** @todo fixme - foo() doesn't work. */
816 if (!pCmd->cArgsMax)
817 return VERR_PARSE_TOO_MANY_ARGUMENTS;
818
819 /*
820 * This is a hack, it's "temporary" and should go away "when" the parser is
821 * modified to match arguments while parsing.
822 */
823 if ( pCmd->cArgsMax == 1
824 && pCmd->cArgsMin == 1
825 && pCmd->cArgDescs == 1
826 && pCmd->paArgDescs[0].enmCategory == DBGCVAR_CAT_STRING
827 && cArgs >= 1)
828 {
829 *pcArgs = 1;
830 RTStrStripR(pszArgs);
831 return dbgcEvalSubString(pDbgc, pszArgs, strlen(pszArgs), &paArgs[0]);
832 }
833
834
835 /*
836 * The parse loop.
837 */
838 PDBGCVAR pArg0 = &paArgs[0];
839 PDBGCVAR pArg = pArg0;
840 *pcArgs = 0;
841 do
842 {
843 /*
844 * Can we have another argument?
845 */
846 if (*pcArgs >= pCmd->cArgsMax)
847 return VERR_PARSE_TOO_MANY_ARGUMENTS;
848 if (pArg >= &paArgs[cArgs])
849 return VERR_PARSE_ARGUMENT_OVERFLOW;
850
851 /*
852 * Find the end of the argument.
853 */
854 int cPar = 0;
855 char chQuote = '\0';
856 char *pszEnd = NULL;
857 char *psz = pszArgs;
858 char ch;
859 bool fBinary = false;
860 for (;;)
861 {
862 /*
863 * Check for the end.
864 */
865 if ((ch = *psz) == '\0')
866 {
867 if (chQuote)
868 return VERR_PARSE_UNBALANCED_QUOTE;
869 if (cPar)
870 return VERR_PARSE_UNBALANCED_PARENTHESIS;
871 pszEnd = psz;
872 break;
873 }
874 /*
875 * When quoted we ignore everything but the quotation char.
876 * We use the REXX way of escaping the quotation char, i.e. double occurrence.
877 */
878 else if (ch == '\'' || ch == '"' || ch == '`')
879 {
880 if (chQuote)
881 {
882 /* end quote? */
883 if (ch == chQuote)
884 {
885 if (psz[1] == ch)
886 psz++; /* skip the escaped quote char */
887 else
888 chQuote = '\0'; /* end of quoted string. */
889 }
890 }
891 else
892 chQuote = ch; /* open new quote */
893 }
894 /*
895 * Parenthesis can of course be nested.
896 */
897 else if (ch == '(')
898 {
899 cPar++;
900 fBinary = false;
901 }
902 else if (ch == ')')
903 {
904 if (!cPar)
905 return VERR_PARSE_UNBALANCED_PARENTHESIS;
906 cPar--;
907 fBinary = true;
908 }
909 else if (!chQuote && !cPar)
910 {
911 /*
912 * Encountering blanks may mean the end of it all. A binary operator
913 * will force continued parsing.
914 */
915 if (RT_C_IS_BLANK(*psz))
916 {
917 pszEnd = psz++; /* just in case. */
918 while (RT_C_IS_BLANK(*psz))
919 psz++;
920 PCDBGCOP pOp = dbgcOperatorLookup(pDbgc, psz, fBinary, ' ');
921 if (!pOp || pOp->fBinary != fBinary)
922 break; /* the end. */
923 psz += pOp->cchName;
924 while (RT_C_IS_BLANK(*psz)) /* skip blanks so we don't get here again */
925 psz++;
926 fBinary = false;
927 continue;
928 }
929
930 /*
931 * Look for operators without a space up front.
932 */
933 if (dbgcIsOpChar(*psz))
934 {
935 PCDBGCOP pOp = dbgcOperatorLookup(pDbgc, psz, fBinary, ' ');
936 if (pOp)
937 {
938 if (pOp->fBinary != fBinary)
939 {
940 pszEnd = psz;
941 /** @todo this is a parsing error really. */
942 break; /* the end. */
943 }
944 psz += pOp->cchName;
945 while (RT_C_IS_BLANK(*psz)) /* skip blanks so we don't get here again */
946 psz++;
947 fBinary = false;
948 continue;
949 }
950 }
951 fBinary = true;
952 }
953
954 /* next char */
955 psz++;
956 }
957 *pszEnd = '\0';
958 /* (psz = next char to process) */
959
960 /*
961 * Parse and evaluate the argument.
962 */
963 int rc = dbgcEvalSub(pDbgc, pszArgs, strlen(pszArgs), pArg);
964 if (RT_FAILURE(rc))
965 return rc;
966
967 /*
968 * Next.
969 */
970 pArg++;
971 (*pcArgs)++;
972 pszArgs = psz;
973 while (*pszArgs && RT_C_IS_BLANK(*pszArgs))
974 pszArgs++;
975 } while (*pszArgs);
976
977 /*
978 * Match the arguments.
979 */
980 return dbgcEvalSubMatchVars(pDbgc, pCmd->cArgsMin, pCmd->cArgsMax, pCmd->paArgDescs, pCmd->cArgDescs, pArg0, pArg - pArg0);
981}
982
983
984/**
985 * Evaluate one command.
986 *
987 * @returns VBox status code. Any error indicates the termination of the console session.
988 *
989 * @param pDbgc Debugger console instance data.
990 * @param pszCmd Pointer to the command.
991 * @param cchCmd Length of the command.
992 * @param fNoExecute Indicates that no commands should actually be executed.
993 * @todo Change pszCmd into argc/argv?
994 */
995int dbgcEvalCommand(PDBGC pDbgc, char *pszCmd, size_t cchCmd, bool fNoExecute)
996{
997 char *pszCmdInput = pszCmd;
998
999 /*
1000 * Skip blanks.
1001 */
1002 while (RT_C_IS_BLANK(*pszCmd))
1003 pszCmd++, cchCmd--;
1004
1005 /* external command? */
1006 bool fExternal = *pszCmd == '.';
1007 if (fExternal)
1008 pszCmd++, cchCmd--;
1009
1010 /*
1011 * Find arguments.
1012 */
1013 char *pszArgs = pszCmd;
1014 while (RT_C_IS_ALNUM(*pszArgs))
1015 pszArgs++;
1016 if (*pszArgs && (!RT_C_IS_BLANK(*pszArgs) || pszArgs == pszCmd))
1017 {
1018 pDbgc->rcCmd = VINF_PARSE_INVALD_COMMAND_NAME;
1019 pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "Syntax error in command '%s'!\n", pszCmdInput);
1020 return 0;
1021 }
1022
1023 /*
1024 * Find the command.
1025 */
1026 PCDBGCCMD pCmd = dbgcRoutineLookup(pDbgc, pszCmd, pszArgs - pszCmd, fExternal);
1027 if (!pCmd || (pCmd->fFlags & DBGCCMD_FLAGS_FUNCTION))
1028 {
1029 pDbgc->rcCmd = VINF_PARSE_COMMAND_NOT_FOUND;
1030 return pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "Unknown command '%s'!\n", pszCmdInput);
1031 }
1032
1033 /*
1034 * Parse arguments (if any).
1035 */
1036 unsigned cArgs;
1037 int rc = dbgcProcessArguments(pDbgc, pCmd, pszArgs, &pDbgc->aArgs[pDbgc->iArg], RT_ELEMENTS(pDbgc->aArgs) - pDbgc->iArg, &cArgs);
1038
1039 /*
1040 * Execute the command.
1041 */
1042 if (!rc)
1043 {
1044 if (!fNoExecute)
1045 rc = pCmd->pfnHandler(pCmd, &pDbgc->CmdHlp, pDbgc->pVM, &pDbgc->aArgs[0], cArgs, NULL);
1046 pDbgc->rcCmd = rc;
1047 if (rc == VERR_DBGC_COMMAND_FAILED)
1048 rc = VINF_SUCCESS;
1049 }
1050 else
1051 {
1052 pDbgc->rcCmd = rc;
1053
1054 /* report parse / eval error. */
1055 switch (rc)
1056 {
1057 case VERR_PARSE_TOO_FEW_ARGUMENTS:
1058 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1059 "Syntax error: Too few arguments. Minimum is %d for command '%s'.\n", pCmd->cArgsMin, pCmd->pszCmd);
1060 break;
1061 case VERR_PARSE_TOO_MANY_ARGUMENTS:
1062 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1063 "Syntax error: Too many arguments. Maximum is %d for command '%s'.\n", pCmd->cArgsMax, pCmd->pszCmd);
1064 break;
1065 case VERR_PARSE_ARGUMENT_OVERFLOW:
1066 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1067 "Syntax error: Too many arguments.\n");
1068 break;
1069 case VERR_PARSE_UNBALANCED_QUOTE:
1070 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1071 "Syntax error: Unbalanced quote (argument %d).\n", cArgs);
1072 break;
1073 case VERR_PARSE_UNBALANCED_PARENTHESIS:
1074 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1075 "Syntax error: Unbalanced parenthesis (argument %d).\n", cArgs);
1076 break;
1077 case VERR_PARSE_EMPTY_ARGUMENT:
1078 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1079 "Syntax error: An argument or subargument contains nothing useful (argument %d).\n", cArgs);
1080 break;
1081 case VERR_PARSE_UNEXPECTED_OPERATOR:
1082 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1083 "Syntax error: Invalid operator usage (argument %d).\n", cArgs);
1084 break;
1085 case VERR_PARSE_INVALID_NUMBER:
1086 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1087 "Syntax error: Ivalid numeric value (argument %d). If a string was the intention, then quote it.\n", cArgs);
1088 break;
1089 case VERR_PARSE_NUMBER_TOO_BIG:
1090 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1091 "Error: Numeric overflow (argument %d).\n", cArgs);
1092 break;
1093 case VERR_PARSE_INVALID_OPERATION:
1094 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1095 "Error: Invalid operation attempted (argument %d).\n", cArgs);
1096 break;
1097 case VERR_PARSE_FUNCTION_NOT_FOUND:
1098 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1099 "Error: Function not found (argument %d).\n", cArgs);
1100 break;
1101 case VERR_PARSE_NOT_A_FUNCTION:
1102 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1103 "Error: The function specified is not a function (argument %d).\n", cArgs);
1104 break;
1105 case VERR_PARSE_NO_MEMORY:
1106 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1107 "Error: Out memory in the regular heap! Expect odd stuff to happen...\n", cArgs);
1108 break;
1109 case VERR_PARSE_INCORRECT_ARG_TYPE:
1110 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1111 "Error: Incorrect argument type (argument %d?).\n", cArgs);
1112 break;
1113 case VERR_PARSE_VARIABLE_NOT_FOUND:
1114 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1115 "Error: An undefined variable was referenced (argument %d).\n", cArgs);
1116 break;
1117 case VERR_PARSE_CONVERSION_FAILED:
1118 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1119 "Error: A conversion between two types failed (argument %d).\n", cArgs);
1120 break;
1121 case VERR_PARSE_NOT_IMPLEMENTED:
1122 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1123 "Error: You hit a debugger feature which isn't implemented yet (argument %d).\n", cArgs);
1124 break;
1125 case VERR_PARSE_BAD_RESULT_TYPE:
1126 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1127 "Error: Couldn't satisfy a request for a specific result type (argument %d). (Usually applies to symbols)\n", cArgs);
1128 break;
1129 case VERR_PARSE_WRITEONLY_SYMBOL:
1130 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1131 "Error: Cannot get symbol, it's set only (argument %d).\n", cArgs);
1132 break;
1133
1134 case VERR_DBGC_COMMAND_FAILED:
1135 rc = VINF_SUCCESS;
1136 break;
1137
1138 default:
1139 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1140 "Error: Unknown error %d!\n", rc);
1141 return rc;
1142 }
1143
1144 /*
1145 * Parse errors are non fatal.
1146 */
1147 if (rc >= VERR_PARSE_FIRST && rc < VERR_PARSE_LAST)
1148 rc = VINF_SUCCESS;
1149 }
1150
1151 return rc;
1152}
1153
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