Posted to tcl by rar at Tue Jul 10 20:11:46 GMT 2007view raw

  1. /// <summary>
  2. /// Dispatch is the callback registered with Tcl API for every added command.
  3. /// when we get a callback, we look at argv[0] to figure out what command is
  4. /// desired, and find the actual method in the commandHash and call that with
  5. /// appropriate arguments.
  6. /// note this matches delegate Tcl_ObjCmdProc
  7. /// </summary>
  8. /// <param name="clientData"></param>
  9. /// <param name="pinterp"></param>
  10. /// <param name="argc"></param>
  11. /// <param name="argv"></param>
  12. /// <returns></returns>
  13. public unsafe int Dispatch(IntPtr clientData,
  14. IntPtr interpPtr,
  15. Int32 objc,
  16. IntPtr objv)
  17. {
  18. // get the context
  19. GCHandle contextHandle = (GCHandle)clientData;
  20. object context = (object)contextHandle.Target;
  21.  
  22. // get a/the wrapped interpreter
  23. TclInterpreter interp = TclInterpreter.GetWrappedInterp(interpPtr);
  24.  
  25. // parse objc+objv -> args
  26. // note that we are marshalling from unmanaged to managed manually here,
  27. // since C# doesn't know how big the objv array is -- but we do, since it's
  28. // provided by objc. see also comments for Tcl_ObjCmdProc delegate in TclAPI.cs
  29. // this should be a reasonably safe bit of unsafe code, as long as we can
  30. // trust objc. this MAY blow up on 64bit, depending on how smart my use
  31. // of IntPtr (vs., say, Int32) is.
  32. // also note that entries in the objv arrays are pointers to TclObjs -- we
  33. // simply use these pointers (i.e. the address of the TclObj) as opaque
  34. // handles on which we call Tcl_GetStringFromObj.
  35. string[] args = new string[objc];
  36.  
  37. for (int i = 0; i < objc; i++)
  38. {
  39. // we assume here that only strings (or string representations)
  40. // will be passed; this is a valid assumption, though possibly
  41. // not the most efficient.
  42. // question: do we need to make a private copy of this string?
  43. // i.e. is args[i] going to point to memory managed and mutable
  44. // by Tcl?
  45. // answer: apparently not, because the marshalling code does
  46. // this automatically, at least in our case of converting 8 bit
  47. // ansi/ascii chars to 16 bit unicode chars.
  48.  
  49. // at some point I was getting Invalid Access errors, i.e. there
  50. // would be a bad pointer getting referenced inside of Tcl_GetString.
  51. // after rebooting the system, I no longer see this. I suspect this
  52. // issue is related to how Tcl handles empty strings (i.e. ""), as it
  53. // was only empty string arguments that were causing the problem.
  54. // just hit it again, with: Stub setSMA ""
  55. // it appears to be the case that this happens after some amount of
  56. // uptime, probably prior to that the bad pointers are to zeroed-out
  57. // memory so things work ok.
  58. // this is bug 1038, which may or may not get fixed depending on whether
  59. // it ever is seen in real use
  60. IntPtr tclObjAddr = Marshal.ReadIntPtr(objv, i * sizeof(IntPtr));
  61. args[i] = TclAPI.Tcl_GetString(tclObjAddr);
  62. }
  63.  
  64. // XXX bug 1038 XXX
  65. // this is a super ugly hack. to get around the problem of crashing when
  66. // passing in an empty string argument, Stub is aliased to a command that
  67. // will substitute "" with _EMPTY_STRING_ARG_. so here we need to look
  68. // through our args, and replace _EMPTY_STRING_ARG_ with ""
  69. for (int i = 0; i < args.Length; i++)
  70. {
  71. if (args[i] == "_EMPTY_STRING_ARG_")
  72. {
  73. args[i] = "";
  74. }
  75. }
  76.  
  77. // get the actual command from the commandHash based on command
  78. // name from tcl
  79. // XXX bug 1038 hack XXX
  80. // if we've wrapped the command, we have to remove the .wrapped bit
  81. // or we won't find a matching command. uf, hadn't thought of this
  82. // before...
  83. // need a more generalized mechanism if we're going to wrap other
  84. // commands.
  85. if (args[0] == "Stub.wrapped")
  86. {
  87. args[0] = "Stub";
  88. }
  89.  
  90. TclCommand command = commandHash[args[0]] as TclCommand;
  91.  
  92. // execute that command, cast the Tcl_Result to an int
  93. return ((int)command.Execute(args, context, interp));
  94.  
  95. }
  96.