Module: environment-tools Synopsis: Environment tools Author: Chris Page, Andy Armstrong, Scott McKay, Jason Trenouth Copyright: Original Code is Copyright (c) 1995-2004 Functional Objects, Inc. All rights reserved. License: Functional Objects Library Public License Version 1.0 Dual-license: GNU Lesser General Public License Warranty: Distributed WITHOUT WARRANTY OF ANY KIND /// PROTOCOLS define open generic frame-create-breakpoint (frame :: ) => (); define open generic frame-clear-breakpoint (frame :: ) => (); define open generic frame-edit-breakpoint-options (frame :: ) => (); define open generic frame-toggle-breakpoint-enabled? (frame :: ) => (); define open generic frame-create-or-toggle-breakpoint (frame :: ) => (); define open generic frame-run-to-cursor (frame :: , target :: ) => (); define open generic frame-new-breakpoint (frame :: ) => (); define open generic frame-browse-all-breakpoints (frame :: ) => (); define open generic frame-enable-all-breakpoints (frame :: ) => (); define open generic frame-disable-all-breakpoints (frame :: ) => (); define open generic frame-clear-all-breakpoints (frame :: ) => (); define open generic frame-trace-target (frame :: ) => (); define open generic frame-untrace-target (frame :: ) => (); define open generic frame-untrace-all (frame :: ) => (); define open generic record-breakpoint-source-locations (project :: ); /// TYPES (internal) // ---*** kludge: use instead of to // avoid invoking code on the compiler's s which don't // know squat about s. define constant = type-union(, ); define constant = type-union(, , ); /// (internal) define sealed class () sealed slot breakpoint-slot-label :: , required-init-keyword: label:; sealed slot breakpoint-slot-abbreviation :: , required-init-keyword: abbrev:; sealed slot breakpoint-slot-key :: , required-init-keyword: key:; sealed slot breakpoint-slot-getter :: , required-init-keyword: getter:; sealed slot breakpoint-slot-setter :: , required-init-keyword: setter:; sealed slot breakpoint-slot-init-value, required-init-keyword: value:; end class; define constant $breakpoint-slots = vector( make(, label: "Enabled", abbrev: 'E', key: #"enabled?", value: $default-breakpoint-enabled?, getter: breakpoint-enabled?, setter: breakpoint-enabled?-setter), make(, label: "Pause application", abbrev: 'B', key: #"stop?", value: $default-breakpoint-stop?, getter: breakpoint-stop?, setter: breakpoint-stop?-setter), make(, label: "Print message", abbrev: 'M', key: #"message?", value: $default-breakpoint-message?, getter: breakpoint-message?, setter: breakpoint-message?-setter), make(, label: "Toggle profiling", abbrev: 'P', key: #"profile?", value: $default-breakpoint-profile?, getter: breakpoint-profile?, setter: breakpoint-profile?-setter), make(, label: "One shot", abbrev: 'T', key: #"transient?", value: $default-breakpoint-transient?, getter: breakpoint-transient?, setter: breakpoint-transient?-setter) ); define function breakpoint-default-keys () => (keys :: ) let keys = make(); for (item in $breakpoint-slots) if (breakpoint-slot-init-value(item)) keys := add!(keys, breakpoint-slot-key(item)); end if; end for; keys end function; /// Breakpoint handling define method breakpoint-current? (breakpoint :: ) => (stopped-at? :: ) let project = breakpoint.breakpoint-project; let application = project.project-application; if (application & (application-state(application) = #"stopped")) any?(method (thread :: ) member?(breakpoint, current-stop-breakpoints(project, thread)) end, application-threads(application)) end end method breakpoint-current?; /// Breakpoint coercion define method coerce-to-breakpoint (project :: , object :: ) => (breakpoint :: ) object end method coerce-to-breakpoint; define method coerce-to-breakpoint (project :: , object :: ) => (breakpoint :: false-or()) find-breakpoint(, project: project, object: object) end method coerce-to-breakpoint; define function frame-target-to-breakpoint (frame :: ) => (object) frame-target-to-browse(frame) end function frame-target-to-breakpoint; define function frame-target-breakpoint (frame :: ) => (breakpoint :: false-or()) let object = frame-target-to-breakpoint(frame); coerce-to-breakpoint(frame.ensure-frame-project, object) end function frame-target-breakpoint; /// Breakpoint commands define function update-breakpoint-enabled-toggle (menu-box) => () let frame = menu-box.sheet-frame; let breakpoint = frame-target-breakpoint(frame); if (breakpoint) let enabled? = breakpoint.breakpoint-enabled?; menu-box.gadget-enabled? := #t; menu-box.gadget-value := if (enabled?) #[#"enabled?"] else #[] end if; else menu-box.gadget-enabled? := #f; menu-box.gadget-value := #[] end if; end function update-breakpoint-enabled-toggle; define method frame-create-breakpoint (frame :: ) => () let target = frame-target-to-breakpoint(frame); if (target & ~instance?(target, )) make(, project: frame.ensure-frame-project, object: target); end if; end method frame-create-breakpoint; define method frame-clear-breakpoint (frame :: ) => () let breakpoint = frame-target-breakpoint(frame); if (breakpoint) destroy-breakpoint(breakpoint); end if; end method frame-clear-breakpoint; define method frame-edit-breakpoint-options (frame :: ) => () let project = frame.ensure-frame-project; let target = frame-target-to-breakpoint(frame); let breakpoint = coerce-to-breakpoint(project, target); let (dialog, ok?) = choose-breakpoint-options(frame, breakpoint | target); if (ok?) update-from-breakpoint-dialog(dialog, breakpoint | target); end if; end method frame-edit-breakpoint-options; define method update-from-breakpoint-dialog (dialog :: , target :: ) => () apply(reinitialize-breakpoint, target, breakpoint-arguments(dialog)); end method update-from-breakpoint-dialog; define method update-from-breakpoint-dialog (dialog :: , target :: ) => () apply(make, , object: target, project: dialog.frame-owner.ensure-frame-project, breakpoint-arguments(dialog)) end method update-from-breakpoint-dialog; define method frame-toggle-breakpoint-enabled? (frame :: ) => () let breakpoint = frame-target-breakpoint(frame); if (breakpoint) reinitialize-breakpoint(breakpoint, enabled?: ~breakpoint.breakpoint-enabled?); end if; end method frame-toggle-breakpoint-enabled?; define method frame-create-or-toggle-breakpoint (frame :: ) => () let project = frame.ensure-frame-project; let target = frame-target-to-breakpoint(frame); let breakpoint = coerce-to-breakpoint(project, target); if (breakpoint) reinitialize-breakpoint(breakpoint, enabled?: ~breakpoint.breakpoint-enabled?); else make(, project: project, object: target); end if; end method frame-create-or-toggle-breakpoint; define method frame-run-to-cursor (frame :: , target :: ) => () let project = frame.ensure-frame-project; select (target by instance?) => make(, project: project, object: target, transient?: #t); => // ---*** Ensure we stop if there is already a breakpoint here. // However, this means that this now always stops even if it didn't // before. Either we need to reintroduce multiple breakpoints per // location or some other mechanism (e.g. callback of undo work). reinitialize-breakpoint(target, stop?: #t); end select; let application = project.project-application; select (application & application-state(application)) #"running" => #f; #"stopped" => frame-continue-application(frame); #"uninitialized", #"closed", #f => if (environment-question("The application is not currently running.\n" "Start it?", owner: frame)) frame-start-application(frame); end if; end select; end method frame-run-to-cursor; define method frame-run-to-target (frame :: ) => () let project = frame.ensure-frame-project; let target = frame-target-to-breakpoint(frame); frame-run-to-cursor(frame, target) end method frame-run-to-target; define method frame-new-breakpoint (frame :: ) => () let project = frame.ensure-frame-project; let (dialog, ok?) = choose-new-breakpoint(frame); if (ok?) let name = dialog.breakpoint-dialog-name-pane.gadget-value; let object = find-named-object(frame, name); select (object by instance?) => make(, project: project, object: object); => make(, project: project, object: object); otherwise => let module = environment-object-primitive-name(project, frame.frame-current-module); let message = format-to-string("'%s' is not a function or a class in module %s.", name, module); environment-warning-message(message, owner: frame); end end end method frame-new-breakpoint; define method frame-browse-all-breakpoints (frame :: ) => () let project = frame.ensure-frame-project; find-environment-frame (frame, , project: project, page: #"breakpoints") end method frame-browse-all-breakpoints; define method frame-enable-all-breakpoints (frame :: ) => () let project = frame.ensure-frame-project; with-compressed-breakpoint-state-changes () for (bkp in source-location-breakpoints(project)) breakpoint-enabled?(bkp) := #t; end for; for (bkp in environment-object-breakpoints(project)) breakpoint-enabled?(bkp) := #t; end for; end; end method frame-enable-all-breakpoints; define method frame-disable-all-breakpoints (frame :: ) => () let project = frame.ensure-frame-project; with-compressed-breakpoint-state-changes () for (bkp in source-location-breakpoints(project)) breakpoint-enabled?(bkp) := #f; end for; for (bkp in environment-object-breakpoints(project)) breakpoint-enabled?(bkp) := #f; end for; end; end method frame-disable-all-breakpoints; define method frame-clear-all-breakpoints (frame :: ) => () if (environment-question ("Clear all current breakpoints?", owner: frame, style: #"warning")) let project = frame.ensure-frame-project; with-compressed-breakpoint-state-changes () for (bkp in source-location-breakpoints(project)) destroy-breakpoint(bkp); end for; for (bkp in environment-object-breakpoints(project)) destroy-breakpoint(bkp); end for; end; end if; end method frame-clear-all-breakpoints; define method frame-clear-obsolete-breakpoints (frame :: , project :: ) => () let invalid-breakpoints = make(); for (breakpoint in project.environment-object-breakpoints) let object = breakpoint.breakpoint-object; unless (environment-object-exists?(project, object)) add!(invalid-breakpoints, breakpoint) end end; let invalid-count = size(invalid-breakpoints); unless (invalid-count == 0) let name = invalid-count == 1 & block () let breakpoint = invalid-breakpoints[0]; let object = breakpoint.breakpoint-object; frame-object-unique-name(frame, object) exception (object :: ) #f end; let message = if (name) format-to-string ("Removing breakpoint for '%s' which no longer exists", name); else format-to-string ("Removing %d breakpoints for objects that no longer exist", invalid-count) end; environment-warning-message(message, owner: frame); for (breakpoint in invalid-breakpoints) destroy-breakpoint(breakpoint) end end end method frame-clear-obsolete-breakpoints; /// Breakpoint Popup Menu define method update-frame-commands-for-browse-target (frame :: , object :: ) => () next-method(); let project = frame.ensure-frame-project; let breakpoint? = coerce-to-breakpoint(project, object) & #t; let source-location? = instance?(object, ); command-enabled?(frame-create-breakpoint, frame) := ~breakpoint?; command-enabled?(frame-clear-breakpoint, frame) := breakpoint?; command-enabled?(frame-run-to-target, frame) := source-location?; end method update-frame-commands-for-browse-target; /// Breakpoint Dialog define function choose-breakpoint-options (frame :: , target, #rest args, #key) => (dialog :: , ok? :: ) with-frame-manager(frame.frame-manager) let dialog = apply(make, , owner: frame, target: target, args); values(dialog, start-dialog(dialog) & #t); end; end function choose-breakpoint-options; define frame () keyword title: = "Edit Breakpoint Options"; pane options-box (dialog) make(, items: $breakpoint-slots, orientation: #"vertical", value-changed-callback: method (gadget) let message? = member?(#"message?", dialog.options-box.gadget-value); dialog.message-field.gadget-enabled? := message?; dialog.message-label.gadget-enabled? := message?; end method, label-key: breakpoint-slot-label, value-key: breakpoint-slot-key); pane message-label (dialog) make(