209 lines
7.2 KiB
Python
209 lines
7.2 KiB
Python
|
import traceback, sys, string
|
||
|
|
||
|
import win32com.server.util
|
||
|
from win32com.util import IIDToInterfaceName
|
||
|
from win32com.client.util import Enumerator
|
||
|
from win32com.server.exception import COMException
|
||
|
import pythoncom
|
||
|
from .framework import trace
|
||
|
from win32com.axdebug import axdebug, gateways, contexts, stackframe, documents, adb
|
||
|
from win32com.axdebug.codecontainer import SourceCodeContainer
|
||
|
from win32com.axdebug.util import _wrap, _wrap_remove
|
||
|
import win32com.client.connect
|
||
|
import win32api, winerror
|
||
|
import os
|
||
|
|
||
|
try:
|
||
|
os.environ["DEBUG_AXDEBUG"]
|
||
|
debuggingTrace = 1 # Should we print "trace" output?
|
||
|
except KeyError:
|
||
|
debuggingTrace = 0
|
||
|
|
||
|
def trace(*args):
|
||
|
"""A function used instead of "print" for debugging output.
|
||
|
"""
|
||
|
if not debuggingTrace:
|
||
|
return
|
||
|
print(win32api.GetCurrentThreadId(), end=' ')
|
||
|
for arg in args:
|
||
|
print(arg, end=' ')
|
||
|
print()
|
||
|
|
||
|
# Note that the DebugManager is not a COM gateway class for the
|
||
|
# debugger - but it does create and manage them.
|
||
|
class DebugManager:
|
||
|
_debugger_interfaces_ = [axdebug.IID_IActiveScriptDebug]
|
||
|
def __init__(self, scriptEngine):
|
||
|
self.scriptEngine = scriptEngine
|
||
|
self.adb = adb.Debugger()
|
||
|
self.rootNode = None
|
||
|
self.debugApplication = None
|
||
|
self.ccProvider = documents.CodeContainerProvider()
|
||
|
try:
|
||
|
self.scriptSiteDebug = scriptEngine.GetScriptSite(axdebug.IID_IActiveScriptSiteDebug)
|
||
|
except pythoncom.com_error:
|
||
|
# No debugger interface (ie, dumb host). Do the extra work.
|
||
|
trace("Scripting site has no debugger interface")
|
||
|
self.scriptSiteDebug = None
|
||
|
# Get the debug application object.
|
||
|
self.debugApplication = None
|
||
|
if self.scriptSiteDebug is not None:
|
||
|
# Spec says that we should test for this, and if it fails revert to
|
||
|
# PDM application.
|
||
|
try:
|
||
|
self.debugApplication = self.scriptSiteDebug.GetApplication()
|
||
|
self.rootNode = self.scriptSiteDebug.GetRootApplicationNode()
|
||
|
except pythoncom.com_error:
|
||
|
self.debugApplication = None
|
||
|
|
||
|
if self.debugApplication is None:
|
||
|
# Try to get/create the default one
|
||
|
# NOTE - Dont catch exceptions here - let the parent do it,
|
||
|
# so it knows debug support is available.
|
||
|
pdm=pythoncom.CoCreateInstance(axdebug.CLSID_ProcessDebugManager,None,pythoncom.CLSCTX_ALL, axdebug.IID_IProcessDebugManager)
|
||
|
self.debugApplication = pdm.GetDefaultApplication()
|
||
|
self.rootNode = self.debugApplication.GetRootNode()
|
||
|
|
||
|
assert self.debugApplication is not None, "Need to have a DebugApplication object by now!"
|
||
|
self.activeScriptDebug = None
|
||
|
|
||
|
if self.debugApplication is not None:
|
||
|
self.adb.AttachApp(self.debugApplication, self.ccProvider)
|
||
|
self.codeContainers = {}
|
||
|
self.activeScriptDebug = _wrap(ActiveScriptDebug(self, self.codeContainers), axdebug.IID_IActiveScriptDebug)
|
||
|
|
||
|
def Close(self):
|
||
|
# Called by the language engine when it receives a close request
|
||
|
if self.activeScriptDebug is not None:
|
||
|
_wrap_remove(self.activeScriptDebug)
|
||
|
self.activeScriptDebug = None
|
||
|
self.scriptEngine = None
|
||
|
self.rootNode = None
|
||
|
self.debugApplication = None
|
||
|
self.scriptSiteDebug = None
|
||
|
if self.ccProvider is not None:
|
||
|
self.ccProvider.Close()
|
||
|
self.ccProvider = None
|
||
|
self.codeContainers = {}
|
||
|
if self.adb:
|
||
|
self.adb.CloseApp()
|
||
|
self.adb = None
|
||
|
# print "Close complete"
|
||
|
|
||
|
def IsAnyHost(self):
|
||
|
"Do we have _any_ debugging interfaces installed?"
|
||
|
return self.debugApplication is not None
|
||
|
|
||
|
def IsSimpleHost(self):
|
||
|
return self.scriptSiteDebug is None
|
||
|
|
||
|
def HandleRuntimeError( self ):
|
||
|
"""Called by the engine when a runtime error occurs. If we have a debugger,
|
||
|
we let it know.
|
||
|
|
||
|
The result is a boolean which indicates if the error handler should call
|
||
|
IActiveScriptSite::OnScriptError()
|
||
|
"""
|
||
|
# if self.IsAnyHost:
|
||
|
# site = _wrap(self, axdebug.IID_IActiveScriptSite)
|
||
|
# breakResume, errorResume, fCallOnError = self.debugApplication(activeScriptErrorDebug, site)
|
||
|
# Do something with these!
|
||
|
# else:
|
||
|
trace("HandleRuntimeError")
|
||
|
fCallOnError = 1
|
||
|
return fCallOnError
|
||
|
|
||
|
def _query_interface_for_debugger_(self, iid):
|
||
|
if iid in self._debugger_interfaces_:
|
||
|
return self.activeScriptDebug
|
||
|
trace("DebugManager QI - unknown IID", iid)
|
||
|
return 0
|
||
|
|
||
|
|
||
|
def OnEnterScript(self):
|
||
|
trace("OnEnterScript")
|
||
|
try:
|
||
|
1/0
|
||
|
except:
|
||
|
# Bit of a hack - reach into engine.
|
||
|
baseFrame = sys.exc_info()[2].tb_frame.f_back
|
||
|
self.adb.SetupAXDebugging(baseFrame)
|
||
|
|
||
|
def OnLeaveScript(self):
|
||
|
trace("OnLeaveScript")
|
||
|
self.adb.ResetAXDebugging()
|
||
|
|
||
|
def AddScriptBlock(self, codeBlock):
|
||
|
# If we dont have debugging support, dont bother.
|
||
|
cc = DebugCodeBlockContainer(codeBlock, self.scriptSiteDebug)
|
||
|
if self.IsSimpleHost():
|
||
|
document = documents.DebugDocumentText(cc)
|
||
|
document = _wrap(document, axdebug.IID_IDebugDocument)
|
||
|
provider = documents.DebugDocumentProvider(document)
|
||
|
provider = _wrap(provider, axdebug.IID_IDebugDocumentProvider)
|
||
|
cc.debugDocument = document
|
||
|
newNode = self.debugApplication.CreateApplicationNode()
|
||
|
newNode.SetDocumentProvider(provider)
|
||
|
newNode.Attach(self.rootNode)
|
||
|
else:
|
||
|
newNode = None # Managed by smart host.
|
||
|
self.codeContainers[cc.sourceContext] = cc
|
||
|
self.ccProvider.AddCodeContainer(cc, newNode)
|
||
|
|
||
|
class DebugCodeBlockContainer(SourceCodeContainer):
|
||
|
def __init__(self, codeBlock, site):
|
||
|
self.codeBlock = codeBlock
|
||
|
SourceCodeContainer.__init__(self, codeBlock.codeText, codeBlock.GetFileName(), codeBlock.sourceContextCookie, codeBlock.startLineNumber, site)
|
||
|
|
||
|
def GetName(self, dnt):
|
||
|
if dnt==axdebug.DOCUMENTNAMETYPE_APPNODE:
|
||
|
return self.codeBlock.GetDisplayName()
|
||
|
elif dnt==axdebug.DOCUMENTNAMETYPE_TITLE:
|
||
|
return self.codeBlock.GetDisplayName()
|
||
|
# elif dnt==axdebug.DOCUMENTNAMETYPE_FILE_TAIL:
|
||
|
# elif dnt==axdebug.DOCUMENTNAMETYPE_URL:
|
||
|
else:
|
||
|
raise COMException(scode=winerror.S_FALSE)
|
||
|
|
||
|
|
||
|
class EnumDebugCodeContexts(gateways.EnumDebugCodeContexts):
|
||
|
def _wrap(self, ob):
|
||
|
return ob
|
||
|
|
||
|
class ActiveScriptDebug:
|
||
|
"""The class which implements the IActiveScriptDebug interface for the Active Script engine.
|
||
|
|
||
|
Only ever used by smart hosts.
|
||
|
"""
|
||
|
_public_methods_ = ["GetScriptTextAttributes", "GetScriptletTextAttributes", "EnumCodeContextsOfPosition"]
|
||
|
_com_interfaces_ = [axdebug.IID_IActiveScriptDebug]
|
||
|
def __init__(self, debugMgr, codeContainers):
|
||
|
self.debugMgr = debugMgr
|
||
|
self.scriptSiteDebug = debugMgr.scriptSiteDebug
|
||
|
self.codeContainers = codeContainers
|
||
|
|
||
|
def _Close(self):
|
||
|
self.debugMgr = None
|
||
|
self.scriptSiteDebug = None
|
||
|
self.codeContainers = {}
|
||
|
|
||
|
def _query_interface_(self, iid):
|
||
|
trace("DebuggerQI with", iid)
|
||
|
return _wrap(self.debugMgr.scriptEngine, iid)
|
||
|
|
||
|
def GetScriptTextAttributes(self, code, delim, flags):
|
||
|
container = SourceCodeContainer(code, "<Temp Code Block>")
|
||
|
return container.GetSyntaxColorAttributes()
|
||
|
def GetScriptletTextAttributes(self, code, delim, flags):
|
||
|
trace ("GetScriptletTextAttributes", code, delim, flags)
|
||
|
container = SourceCodeContainer(code, "<Temp Code Block>")
|
||
|
return container.GetSyntaxColorAttributes()
|
||
|
|
||
|
def EnumCodeContextsOfPosition(self, context, charOffset, numChars):
|
||
|
trace("EnumCodeContextsOfPosition", context, charOffset, numChars)
|
||
|
try:
|
||
|
context = self.codeContainers[context].GetCodeContextAtPosition(charOffset)
|
||
|
except KeyError:
|
||
|
raise COMException(scode=winerror.E_UNEXPECTED)
|
||
|
enum = EnumDebugCodeContexts([context])
|
||
|
return _wrap(enum, axdebug.IID_IEnumDebugCodeContexts)
|