Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

702 lines
18 KiB

  1. /*++
  2. Copyright (C) 1996-2001 Microsoft Corporation
  3. Module Name:
  4. TREES.CPP
  5. Abstract:
  6. History:
  7. --*/
  8. #include <objbase.h>
  9. #include "trees.h"
  10. #include "hmmstr.h"
  11. #include "stackt.h"
  12. class CTreeInstruction
  13. {
  14. public:
  15. enum {INST_PUSH = SQL1_OP_EXPRESSION,
  16. INST_AND = SQL1_AND,
  17. INST_OR = SQL1_OR
  18. };
  19. int m_nInst;
  20. union
  21. {
  22. int m_nArg;
  23. CHmmNode* m_pArg;
  24. };
  25. };
  26. HRESULT CHmmNode::EvaluateNode(CHmmNode* pNode, IHmmPropertySource* pSource)
  27. {
  28. if(pNode == NULL) return HMM_S_NO_ERROR;
  29. CHmmStack<CTreeInstruction> m_aInstructions(100);
  30. BOOL bResult;
  31. CTreeInstruction Inst;
  32. Inst.m_nInst = CTreeInstruction::INST_PUSH;
  33. Inst.m_pArg = pNode;
  34. m_aInstructions.Push(Inst);
  35. while(!m_aInstructions.IsEmpty())
  36. {
  37. CTreeInstruction Inst;
  38. m_aInstructions.Pop(Inst);
  39. int i;
  40. int nCurrentPos;
  41. int nNumChildren;
  42. CHmmNode* pCurrentNode;
  43. CLogicalNode* pLogNode;
  44. switch(Inst.m_nInst)
  45. {
  46. case CTreeInstruction::INST_OR:
  47. if(bResult)
  48. {
  49. m_aInstructions.PopToSize(Inst.m_nArg);
  50. }
  51. break;
  52. case CTreeInstruction::INST_AND:
  53. if(!bResult)
  54. {
  55. m_aInstructions.PopToSize(Inst.m_nArg);
  56. }
  57. break;
  58. case CTreeInstruction::INST_PUSH:
  59. pCurrentNode = Inst.m_pArg;
  60. switch(pCurrentNode->m_lTokenType)
  61. {
  62. case SQL1_AND:
  63. case SQL1_OR:
  64. pLogNode = (CLogicalNode*)pCurrentNode;
  65. nNumChildren = pLogNode->m_apChildren.GetSize();
  66. nCurrentPos = m_aInstructions.GetSize();
  67. for(i = nNumChildren-1; i >= 0; i--)
  68. {
  69. Inst.m_nInst = CTreeInstruction::INST_PUSH;
  70. Inst.m_pArg = pLogNode->m_apChildren[i];
  71. m_aInstructions.Push(Inst);
  72. if(i != 0)
  73. {
  74. Inst.m_nInst = pLogNode->m_lTokenType;
  75. Inst.m_nArg = nCurrentPos;
  76. m_aInstructions.Push(Inst);
  77. }
  78. }
  79. break;
  80. default:
  81. bResult = (pCurrentNode->Evaluate(pSource) == HMM_S_NO_ERROR);
  82. break;
  83. }
  84. break;
  85. }
  86. }
  87. if(bResult)
  88. return HMM_S_NO_ERROR;
  89. else
  90. return HMM_S_FALSE;
  91. }
  92. void CHmmNode::PrintOffset(int nOffset)
  93. {
  94. for(int i = 0; i < nOffset; i++)
  95. {
  96. printf(" ");
  97. }
  98. }
  99. HMM_RELATIONSHIP CHmmNode::RelateNodes(CHmmNode* pFirst, CHmmNode* pSecond,
  100. CMetaData* pMeta)
  101. {
  102. if(pFirst->m_lTokenType == SQL1_OP_EXPRESSION)
  103. {
  104. if(pSecond->m_lTokenType == SQL1_OP_EXPRESSION)
  105. {
  106. // They can compare themselves
  107. // ===========================
  108. return CSql1Token::ComputeRelation((CSql1Token*)pFirst,
  109. (CSql1Token*)pSecond, pMeta);
  110. }
  111. else
  112. {
  113. // Swap them to merge with the other case
  114. // ======================================
  115. HMM_RELATIONSHIP InverseRel = RelateNodes(pSecond, pFirst, pMeta);
  116. return CRelationship::ReverseRoles(InverseRel);
  117. }
  118. }
  119. else
  120. {
  121. // Relate to all the children of the first
  122. // =======================================
  123. CLogicalNode* pFirstLog = (CLogicalNode*)pFirst;
  124. int nNumChildren = pFirstLog->m_apChildren.GetSize();
  125. HMM_RELATIONSHIP* aChildRels = new HMM_RELATIONSHIP[nNumChildren];
  126. for(int i = 0; i < nNumChildren; i++)
  127. {
  128. aChildRels[i] = RelateNodes(pFirstLog->m_apChildren[i], pSecond,
  129. pMeta);
  130. }
  131. // Combine the results
  132. // ===================
  133. HMM_RELATIONSHIP nResult;
  134. if(pFirstLog->m_lTokenType == SQL1_AND)
  135. {
  136. nResult = CRelationship::GetRelationshipOfFirstWithANDofSeconds(
  137. nNumChildren, aChildRels);
  138. }
  139. else
  140. {
  141. nResult = CRelationship::GetRelationshipOfFirstWithORofSeconds(
  142. nNumChildren, aChildRels);
  143. }
  144. delete [] aChildRels;
  145. return nResult;
  146. }
  147. }
  148. void CLogicalNode::Add(CHmmNode* pNode)
  149. {
  150. if(pNode == NULL)
  151. return;
  152. if(pNode->m_lTokenType != m_lTokenType)
  153. {
  154. m_apChildren.Add(pNode);
  155. return;
  156. }
  157. // Copy the children
  158. // =================
  159. CLogicalNode* pLogNode = (CLogicalNode*)pNode;
  160. for(int i = 0; i < pLogNode->m_apChildren.GetSize(); i++)
  161. {
  162. Add(pLogNode->m_apChildren[i]);
  163. }
  164. }
  165. HRESULT CLogicalNode::Evaluate(IHmmPropertySource* pSource)
  166. {
  167. if(m_lTokenType == SQL1_AND)
  168. {
  169. for(int i = 0; i < m_apChildren.GetSize(); i++)
  170. {
  171. HRESULT hres = m_apChildren[i]->Evaluate(pSource);
  172. if(hres != HMM_S_NO_ERROR) return hres;
  173. }
  174. return HMM_S_NO_ERROR;
  175. }
  176. else
  177. {
  178. for(int i = 0; i < m_apChildren.GetSize(); i++)
  179. {
  180. HRESULT hres = m_apChildren[i]->Evaluate(pSource);
  181. if(hres != HMM_S_FALSE) return hres;
  182. }
  183. return HMM_S_NO_ERROR;
  184. }
  185. }
  186. HRESULT CLogicalNode::Negate()
  187. {
  188. for(int i = 0; i < m_apChildren.GetSize(); i++)
  189. {
  190. HRESULT hres = m_apChildren[i]->Negate();
  191. if(hres != HMM_S_FALSE) return hres;
  192. }
  193. if(m_lTokenType == SQL1_AND)
  194. m_lTokenType = SQL1_OR;
  195. else
  196. m_lTokenType = SQL1_AND;
  197. return HMM_S_NO_ERROR;
  198. }
  199. void CLogicalNode::Print(FILE* f, int nOffset)
  200. {
  201. PrintOffset(nOffset);
  202. printf("%s\n", (m_lTokenType == SQL1_AND)?"AND":"OR");
  203. for(int i = 0; i < m_apChildren.GetSize(); i++)
  204. {
  205. m_apChildren[i]->Print(f, nOffset+1);
  206. }
  207. }
  208. CSql1Token::CSql1Token()
  209. {
  210. m_lTokenType = SQL1_OP_EXPRESSION;
  211. VariantInit(&m_vConstValue);
  212. }
  213. CSql1Token::CSql1Token(const CSql1Token& Token)
  214. {
  215. m_lTokenType = Token.m_lTokenType;
  216. if(m_lTokenType == SQL1_OP_EXPRESSION)
  217. {
  218. m_wsProperty = Token.m_wsProperty;
  219. m_lOperator = Token.m_lOperator;
  220. VariantInit(&m_vConstValue);
  221. VariantCopy(&m_vConstValue, (VARIANT*)&Token.m_vConstValue);
  222. m_lPropertyFunction = Token.m_lPropertyFunction;
  223. m_lConstFunction = Token.m_lConstFunction;
  224. }
  225. }
  226. CSql1Token::CSql1Token(const HMM_SQL1_TOKEN& Token)
  227. {
  228. VariantInit(&m_vConstValue);
  229. Load(Token);
  230. }
  231. CSql1Token::~CSql1Token()
  232. {
  233. VariantClear(&m_vConstValue);
  234. }
  235. void CSql1Token::Load(const HMM_SQL1_TOKEN& Token)
  236. {
  237. m_lTokenType = Token.m_lTokenType;
  238. if(m_lTokenType == SQL1_OP_EXPRESSION)
  239. {
  240. m_wsProperty = Token.m_wszProperty;
  241. m_lOperator = Token.m_lOperator;
  242. VariantCopy(&m_vConstValue, (VARIANT*)&Token.m_vConstValue);
  243. m_lPropertyFunction = Token.m_lPropertyFunction;
  244. m_lConstFunction = Token.m_lConstFunction;
  245. }
  246. }
  247. void CSql1Token::Save(HMM_SQL1_TOKEN& Token)
  248. {
  249. Token.m_lTokenType = m_lTokenType;
  250. if(m_lTokenType == SQL1_OP_EXPRESSION)
  251. {
  252. Token.m_wszProperty = HmmStringCopy(m_wsProperty);
  253. Token.m_lOperator = m_lOperator;
  254. VariantInit(&Token.m_vConstValue);
  255. VariantCopy(&Token.m_vConstValue, &m_vConstValue);
  256. Token.m_lPropertyFunction = m_lPropertyFunction;
  257. Token.m_lConstFunction = m_lConstFunction;
  258. }
  259. else
  260. {
  261. Token.m_wszProperty = NULL;
  262. VariantInit(&Token.m_vConstValue);
  263. }
  264. }
  265. HRESULT CSql1Token::Evaluate(IHmmPropertySource* pPropSource)
  266. {
  267. HRESULT hres;
  268. // Get the value of the property
  269. // =============================
  270. VARIANT vProperty;
  271. VariantInit(&vProperty);
  272. if(m_wsProperty.Length() == 0)
  273. {
  274. V_VT(&vProperty) = VT_UNKNOWN;
  275. V_UNKNOWN(&vProperty) = pPropSource;
  276. pPropSource->AddRef();
  277. }
  278. else if(FAILED(pPropSource->GetPropertyValue(m_wsProperty, 0,
  279. &vProperty)))
  280. {
  281. return HMM_E_INSUFFICIENT_INFO;
  282. }
  283. if(V_VT(&vProperty) == VT_NULL)
  284. {
  285. return HMM_S_FALSE;
  286. }
  287. // Apply functions to property and constant
  288. // ========================================
  289. VARIANT vFinalProp;
  290. VariantInit(&vFinalProp);
  291. hres = EvaluateFunction(m_lPropertyFunction, &vProperty,
  292. &vFinalProp);
  293. VariantClear(&vProperty);
  294. if(FAILED(hres)) return hres;
  295. VARIANT vFinalConst;
  296. VariantInit(&vFinalConst);
  297. hres = EvaluateFunction(m_lConstFunction, &m_vConstValue, &vFinalConst);
  298. if(FAILED(hres)) return hres;
  299. // Handle ineritance
  300. // =================
  301. if(m_lOperator == SQL1_OPERATOR_INHERITSFROM)
  302. {
  303. if(V_VT(&vFinalProp) != VT_UNKNOWN || V_VT(&vFinalConst) != VT_BSTR)
  304. return HMM_S_FALSE;
  305. IHmmPropertySource* pProp = (IHmmPropertySource*)V_UNKNOWN(&vFinalProp);
  306. hres = pProp->IsDerivedFrom(V_BSTR(&vFinalConst));
  307. VariantClear(&vFinalConst);
  308. VariantClear(&vFinalProp);
  309. return hres;
  310. }
  311. else if(m_lOperator == SQL1_OPERATOR_NOTINHERITSFROM)
  312. {
  313. if(V_VT(&vFinalProp) != VT_UNKNOWN || V_VT(&vFinalConst) != VT_BSTR)
  314. return HMM_S_FALSE;
  315. IHmmPropertySource* pProp = (IHmmPropertySource*)V_UNKNOWN(&vFinalProp);
  316. hres = pProp->IsDerivedFrom(V_BSTR(&vFinalConst));
  317. VariantClear(&vFinalConst);
  318. VariantClear(&vFinalProp);
  319. return (hres == HMM_S_FALSE)?HMM_S_NO_ERROR : HMM_S_FALSE;
  320. }
  321. // Coerce the constant to the right type
  322. // =====================================
  323. if(FAILED(VariantChangeType(&vFinalConst, &vFinalConst, 0,
  324. V_VT(&vFinalProp))))
  325. {
  326. // Fatal type mismatch --- expression == FALSE
  327. VariantClear(&vFinalConst);
  328. VariantClear(&vFinalProp);
  329. return HMM_S_FALSE;
  330. }
  331. // Compare the two variants
  332. // ========================
  333. int nCompare = CompareVariants(vFinalProp, vFinalConst);
  334. // Apply relational operator
  335. // =========================
  336. BOOL bResult;
  337. switch(m_lOperator)
  338. {
  339. case SQL1_OPERATOR_EQUALS:
  340. bResult = (nCompare == 0);
  341. break;
  342. case SQL1_OPERATOR_NOTEQUALS:
  343. bResult = (nCompare != 0);
  344. break;
  345. case SQL1_OPERATOR_GREATEROREQUALS:
  346. bResult = (nCompare >= 0);
  347. break;
  348. case SQL1_OPERATOR_LESSOREQUALS:
  349. bResult = (nCompare <= 0);
  350. break;
  351. case SQL1_OPERATOR_GREATER:
  352. bResult = (nCompare > 0);
  353. break;
  354. case SQL1_OPERATOR_LESS:
  355. bResult = (nCompare < 0);
  356. break;
  357. default:
  358. bResult = FALSE;
  359. break;
  360. }
  361. VariantClear(&vFinalConst);
  362. VariantClear(&vFinalProp);
  363. if(bResult)
  364. return HMM_S_NO_ERROR;
  365. else
  366. return HMM_S_FALSE;
  367. }
  368. int CSql1Token::CompareVariants(VARIANT& v1, VARIANT& v2)
  369. {
  370. switch(V_VT(&v2))
  371. {
  372. case VT_BSTR:
  373. return wcscmp(V_BSTR(&v1), V_BSTR(&v2));
  374. case VT_I4:
  375. return V_I4(&v1) - V_I4(&v2);
  376. case VT_I2:
  377. return V_I2(&v1) - V_I2(&v2);
  378. case VT_UI1:
  379. return V_UI1(&v1) - V_UI1(&v2);
  380. case VT_R4:
  381. return (V_R4(&v1) > V_R4(&v2))?1:-1;
  382. case VT_R8:
  383. return (V_R8(&v1) > V_R8(&v2))?1:-1;
  384. }
  385. return 1;
  386. }
  387. HRESULT CSql1Token::EvaluateFunction(IN long lFunctionID,
  388. IN READ_ONLY VARIANT* pvArg,
  389. OUT INIT_AND_CLEAR_ME VARIANT* pvDest)
  390. {
  391. unsigned int nLen;
  392. unsigned int i;
  393. BSTR str;
  394. switch(lFunctionID)
  395. {
  396. case SQL1_FUNCTION_UPPER:
  397. if(V_VT(pvArg) != VT_BSTR)
  398. {
  399. return HMM_E_INVALID_QUERY;
  400. }
  401. VariantCopy(pvDest, pvArg);
  402. str = V_BSTR(pvDest);
  403. nLen = SysStringLen(str);
  404. for(i = 0; i < nLen; i++)
  405. {
  406. str[i] = towupper(str[i]);
  407. }
  408. break;
  409. case SQL1_FUNCTION_LOWER:
  410. if(V_VT(pvArg) != VT_BSTR)
  411. {
  412. return HMM_E_INVALID_QUERY;
  413. }
  414. VariantCopy(pvDest, pvArg);
  415. str = V_BSTR(pvDest);
  416. nLen = SysStringLen(str);
  417. for(i = 0; i < nLen; i++)
  418. {
  419. str[i] = towlower(str[i]);
  420. }
  421. break;
  422. case SQL1_FUNCTION_NONE:
  423. default:
  424. VariantCopy(pvDest, pvArg);
  425. break;
  426. }
  427. return HMM_S_NO_ERROR;
  428. }
  429. HRESULT CSql1Token::Negate()
  430. {
  431. switch(m_lOperator)
  432. {
  433. case SQL1_OPERATOR_EQUALS:
  434. m_lOperator = SQL1_OPERATOR_NOTEQUALS;
  435. break;
  436. case SQL1_OPERATOR_NOTEQUALS:
  437. m_lOperator = SQL1_OPERATOR_EQUALS;
  438. break;
  439. case SQL1_OPERATOR_GREATEROREQUALS:
  440. m_lOperator = SQL1_OPERATOR_LESS;
  441. break;
  442. case SQL1_OPERATOR_LESSOREQUALS:
  443. m_lOperator = SQL1_OPERATOR_GREATER;
  444. break;
  445. case SQL1_OPERATOR_GREATER:
  446. m_lOperator = SQL1_OPERATOR_LESSOREQUALS;
  447. break;
  448. case SQL1_OPERATOR_LESS:
  449. m_lOperator = SQL1_OPERATOR_GREATEROREQUALS;
  450. break;
  451. case SQL1_OPERATOR_LIKE:
  452. m_lOperator = SQL1_OPERATOR_UNLIKE;
  453. break;
  454. case SQL1_OPERATOR_UNLIKE:
  455. m_lOperator = SQL1_OPERATOR_LIKE;
  456. break;
  457. case SQL1_OPERATOR_INHERITSFROM:
  458. m_lOperator = SQL1_OPERATOR_NOTINHERITSFROM;
  459. break;
  460. case SQL1_OPERATOR_NOTINHERITSFROM:
  461. m_lOperator = SQL1_OPERATOR_INHERITSFROM;
  462. break;
  463. default:
  464. return HMM_E_FAILED;
  465. }
  466. return HMM_S_NO_ERROR;
  467. }
  468. #define HMM_SMALL -100
  469. #define HMM_LARGE 100
  470. HMM_RELATIONSHIP CSql1Token::ComputeRelation(CSql1Token* pFirst,
  471. CSql1Token* pSecond,
  472. CMetaData* pMeta)
  473. {
  474. if(!pFirst->m_wsProperty.EqualNoCase(pSecond->m_wsProperty))
  475. {
  476. // TBD: the case of INHERITSFROM versus __CLASS== needs to be handled
  477. // here. Ignore for now, since SQL1 does not support it.
  478. // ==================================================================
  479. return RELATION_NONE;
  480. }
  481. if(pFirst->m_lOperator == SQL1_OPERATOR_LIKE ||
  482. pFirst->m_lOperator == SQL1_OPERATOR_UNLIKE ||
  483. pSecond->m_lOperator == SQL1_OPERATOR_LIKE ||
  484. pSecond->m_lOperator == SQL1_OPERATOR_UNLIKE)
  485. {
  486. return RELATION_NONE;
  487. }
  488. BOOL bFirstAboutInheritance =
  489. (pFirst->m_lOperator == SQL1_OPERATOR_INHERITSFROM ||
  490. pFirst->m_lOperator == SQL1_OPERATOR_NOTINHERITSFROM);
  491. BOOL bSecondAboutInheritance =
  492. (pSecond->m_lOperator == SQL1_OPERATOR_INHERITSFROM ||
  493. pSecond->m_lOperator == SQL1_OPERATOR_NOTINHERITSFROM);
  494. if(bFirstAboutInheritance != bSecondAboutInheritance)
  495. {
  496. return RELATION_NONE;
  497. }
  498. if(bFirstAboutInheritance && bSecondAboutInheritance)
  499. {
  500. // Both talk about inhertance
  501. // ==========================
  502. int nClassRel = pMeta->GetClassRelation(V_BSTR(&pFirst->m_vConstValue),
  503. V_BSTR(&pSecond->m_vConstValue));
  504. BOOL bFirstIn =
  505. (pFirst->m_lOperator == SQL1_OPERATOR_INHERITSFROM);
  506. BOOL bSecondIn =
  507. (pSecond->m_lOperator == SQL1_OPERATOR_INHERITSFROM);
  508. switch(nClassRel)
  509. {
  510. case SEPARATE_BRANCHES:
  511. // It can't be in both.
  512. return (HMM_RELATIONSHIP)
  513. (RELATION_NONE - CTwoValues::Combine(bFirstIn, bSecondIn));
  514. case FIRST_IS_PARENT:
  515. // If it's in second, it's in first
  516. return (HMM_RELATIONSHIP)
  517. (RELATION_NONE - CTwoValues::Combine(!bFirstIn, bSecondIn));
  518. case SECOND_IS_PARENT:
  519. // If it's in first, it's in second
  520. return (HMM_RELATIONSHIP)
  521. (RELATION_NONE - CTwoValues::Combine(bFirstIn, !bSecondIn));
  522. }
  523. }
  524. // None talk about inhertiance
  525. // ===========================
  526. int nCompare = CompareVariants(pFirst->m_vConstValue,
  527. pSecond->m_vConstValue);
  528. // Assume that the first constant is 0, and the second is nCompare*5.
  529. // =================================================================
  530. // Compute the segment for X based on the first token
  531. // ==================================================
  532. int nFirstStart, nFirstEnd;
  533. ComputeSegment(pFirst->m_lOperator, 0, nFirstStart, nFirstEnd);
  534. int nSecondStart, nSecondEnd;
  535. ComputeSegment(pSecond->m_lOperator, nCompare*5, nSecondStart, nSecondEnd);
  536. int nRelation = 0;
  537. if(nFirstStart <= nSecondEnd && nSecondStart <= nFirstEnd)
  538. nRelation += VALUE_BOTH_TRUE;
  539. if(nFirstStart < nSecondStart || nFirstEnd > nSecondEnd)
  540. nRelation += VALUE_ONLY_FIRST;
  541. if(nSecondStart < nFirstStart || nSecondEnd > nFirstEnd)
  542. nRelation += VALUE_ONLY_SECOND;
  543. if((nFirstStart > HMM_SMALL && nSecondStart > HMM_SMALL) ||
  544. (nFirstEnd < HMM_LARGE && nSecondEnd < HMM_LARGE))
  545. nRelation += VALUE_BOTH_FALSE;
  546. return (HMM_RELATIONSHIP)nRelation;
  547. }
  548. void CSql1Token::ComputeSegment(long lOperator, int nRightHand,
  549. int& nStart, int& nEnd)
  550. {
  551. switch(lOperator)
  552. {
  553. case SQL1_OPERATOR_EQUALS:
  554. nStart = nEnd = nRightHand;
  555. return;
  556. case SQL1_OPERATOR_NOTEQUALS:// TBD --- at this point a NOOP
  557. nStart = HMM_SMALL;
  558. nStart = HMM_LARGE;
  559. return;
  560. case SQL1_OPERATOR_LESS:
  561. nStart = HMM_SMALL;
  562. nEnd = nRightHand - 1;
  563. return;
  564. case SQL1_OPERATOR_LESSOREQUALS:
  565. nStart = HMM_SMALL;
  566. nEnd = nRightHand;
  567. return;
  568. case SQL1_OPERATOR_GREATER:
  569. nStart = nRightHand + 1;
  570. nEnd = HMM_LARGE;
  571. case SQL1_OPERATOR_GREATEROREQUALS:
  572. nStart = nRightHand;
  573. nEnd = HMM_LARGE;
  574. default:
  575. nStart = HMM_SMALL;
  576. nStart = HMM_LARGE;
  577. return;
  578. }
  579. }
  580. void CSql1Token::Print(FILE* f, int nOffset)
  581. {
  582. PrintOffset(nOffset);
  583. printf("EXP\n");
  584. PrintOffset(nOffset+1);
  585. printf("Property: %S\n", m_wsProperty);
  586. PrintOffset(nOffset+1);
  587. printf("Property function: %d\n", m_lPropertyFunction);
  588. PrintOffset(nOffset+1);
  589. printf("Operator: %d\n", m_lOperator);
  590. PrintOffset(nOffset+1);
  591. printf("Constant: ");
  592. switch(V_VT(&m_vConstValue))
  593. {
  594. case VT_BSTR:
  595. printf("%S", V_BSTR(&m_vConstValue)); break;
  596. case VT_I4:case VT_I2:case VT_UI1:
  597. printf("%d", V_I4(&m_vConstValue)); break;
  598. case VT_R4: case VT_R8:
  599. printf("%f", V_R8(&m_vConstValue)); break;
  600. default:
  601. printf("error");
  602. }
  603. printf("\n");
  604. PrintOffset(nOffset+1);
  605. printf("Constant function: %d\n", m_lConstFunction);
  606. }