241 lines
8.4 KiB
Python
241 lines
8.4 KiB
Python
import os
|
|
import sys
|
|
|
|
import pythoncom
|
|
import win32api
|
|
import win32com.client.connect
|
|
import win32com.server.util
|
|
import winerror
|
|
from win32com.axdebug import adb, axdebug, contexts, documents, gateways, stackframe
|
|
from win32com.axdebug.codecontainer import SourceCodeContainer
|
|
from win32com.axdebug.util import _wrap, _wrap_remove
|
|
from win32com.client.util import Enumerator
|
|
from win32com.server.exception import COMException
|
|
from win32com.util import IIDToInterfaceName
|
|
|
|
from .framework import trace
|
|
|
|
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)
|