Module: dfmc-application Synopsis: Creation and consolidation of proxy objects for the application Author: Paul Howard 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 ///// PROXIES // The following list shows what kind of objects are used as application // proxies for the various environment object classes. // Environment Object Class Proxy Class Defined In // ------------------------ ----------- ----------- // DM // Acc. Path. // Here // Here // DM // Acc. Path. // Acc. Path. // Acc. Path. ///// // An object of any kind that is used as the application proxy for an // environment object. define constant = type-union(, /* , */ , , , , , , , ); /* --- Not currently used. ///// // The kind of application proxy that specifies the program-counter // co-ordinate of a breakpoint. define class () slot proxy-entry-point-symbol :: , required-init-keyword: entry-point-symbol:; slot proxy-entry-point-offset :: , required-init-keyword: entry-point-offset:; end class; */ ///// // The kind of application proxy that has something specifically to do // with a . define abstract class () end class; define sealed domain make (subclass()); define sealed domain initialize (); ///// // A proxy for a DYLAN runtime object. define abstract class () end class; ///// // A proxy for a FOREIGN runtime object. It is assumed that these // are static - once we know its value, it never changes. We can't // possibly know how some foreign garbage collector works, so we // have no way to track foreign objects. define class () constant slot static-foreign-value :: , required-init-keyword: value:; end class; ///// // A proxy for a foreign function runtime object. define class () constant slot static-foreign-symbol :: , required-init-keyword: symbol:; end class; /*---*** andrewa: will be useful, how do we turn it into a source location? define method foreign-function-location (application :: , proxy :: , #key decorate? = #t) => (filename :: false-or(), line :: false-or()) let symbol = proxy.static-foreign-symbol; let source-locator-map = function-source-location-map (application.debug-target-access-path, symbol); let filename = source-locator-map & source-filename(source-locator-map); let line = source-locator-map & base-linenumber(source-locator-map); values(filename, line) end method; */ ///// // A proxy for a DYLAN runtime object that is known to be static, hence // not subject to any relocation by the memory manager. define class () constant slot static-dylan-value :: , required-init-keyword: value:; end class; ///// // A proxy for a dylan runtime object known to be a statically-heaped // instance of . // This proxy additionally caches information that is needed when // browsing instances of the class. define class () slot class-proxy-environment-class-cached? :: = #f; slot class-proxy-browser-data-cached? :: = #f; slot class-proxy-environment-class :: subclass(); slot class-proxy-slots :: ; slot class-proxy-navigation :: ; slot class-proxy-repeat :: false-or(); slot class-proxy-count-offset :: false-or(); slot class-proxy-element-size :: false-or(); slot class-proxy-element-offset :: false-or(); slot class-proxy-class-slot-count :: ; slot class-proxy-incarnation :: false-or() = #f; end class; ///// // The proxy for a DYLAN runtime object that is not necessarily known // to be static. It is potentially relocatable, and therefore required // to be tracked. define abstract class () constant slot tracked-dylan-value :: , required-init-keyword: value:; end class; /* --- Currently not used. --- The app-server is releasing all non-static objects at the end of each debugger transaction. ///// // This is being tracked, but the tracking is allowed to go stale if // the remote application stops referencing the actual runtime object. define class () end class; ///// // This is being tracked, and we force tracking to remain live, even // if the true runtime object becomes garbage in the remote application. define class () end class; */ ///// APPLICATION-PROXY-PRIMITIVE-NAME // A generic function that can generate the primitive name for an // application proxy, regardless of what kind of proxy it is. // The first method is a default which should never get called once // the full implementation is in place. define method application-proxy-primitive-name (application :: , proxy :: , #key decorate? = #t) => (name :: ) "Undescribed Application Object" end method; define method application-proxy-primitive-name (application :: , proxy :: , #key decorate? = #t) => (name :: ) let target = application.application-target-app; let value = runtime-proxy-to-remote-value(application, proxy); print-dylan-object(target, value, length: 10, level: 3, decorate?: decorate?) end method; define method application-proxy-primitive-name (application :: , proxy :: , #key decorate? = #t) => (name :: ) format-to-string("{%=}", proxy.static-foreign-value) end method; define method application-proxy-primitive-name (application :: , proxy :: , #key decorate? = #t) => (name :: ) let symbol = proxy.static-foreign-symbol; let name = symbol.remote-symbol-name; let dll-context = symbol.remote-symbol-library; if (dll-context) format-to-string("%s!%s", dll-context.library-core-name, name) else name end end method; define method application-proxy-primitive-name (application :: , proxy :: , #key decorate? = #t) => (name :: ) "Uninterpreted Stack Frame" end method; define method application-proxy-primitive-name (application :: , proxy :: , #key decorate? = #t) => (name :: ) "Dylan Cleanup Frame" end method; define method application-proxy-primitive-name (application :: , proxy :: , #key decorate? = #t) => (name :: ) if (application.application-target-app) remote-value-as-string (application.application-target-app.debug-target-access-path, proxy, 16) else "????????" end if end method; define method application-proxy-primitive-name (application :: , proxy :: , #key decorate? = #t) => (name :: ) proxy.register-name end method; define method application-proxy-primitive-name (application :: , proxy :: , #key decorate? = #t) => (name :: ) proxy.library-core-name end method; define method application-proxy-primitive-name (application :: , proxy :: , #key decorate? = #t) => (name :: ) let target = application.application-target-app; let path = target.debug-target-access-path; let (sym, obj, gen) = call-frame-function(target, proxy); let byte-offset = call-frame-code-offset(target, proxy); let ip = call-frame-instruction-pointer(target, proxy); let printed-ip = remote-value-as-string(path, ip, 16); if (dylan-call-frame?(target, proxy)) if (sym) format-to-string("%s + 0x%x", sym.remote-symbol-name, byte-offset); else format-to-string("0x%s (No symbols available)", printed-ip); end if elseif (sym) format-to-string("%s:%s + 0x%x", sym.remote-symbol-library.library-core-name, sym.remote-symbol-name, byte-offset); else format-to-string("0x%s (No symbols available)", printed-ip); end if; end method; define method application-proxy-primitive-name (application :: , proxy :: , #key decorate? = #t) => (name :: ) let state-model = thread-state-model(application, proxy); let access-path-name = proxy.thread-name; let thread-name = state-model.thread-state-thread-name; // Cache the thread name since it can never change, and we need // to be able to return it after a thread has died. let (dylan-thread?, dm-name) = if (thread-name) values(#t, thread-name) else let target = application.application-target-app; if (target) let (dylan-thread?, dm-name, dm-object) = remote-thread-information(target, proxy); if (dylan-thread? & dm-name) state-model.thread-state-thread-name := dm-name; end; values(dylan-thread?, dm-name) else values(#f, #f) end end; if (dylan-thread?) if (dm-name = access-path-name) "Anonymous Thread" else dm-name end if else "Foreign Thread" end if; end method; // Todo: Maybe split up into more methods for the various // classes. define method application-proxy-primitive-name (application :: , proxy :: , #key decorate? = #t) => (name :: ) format-to-string("Debug Point"); end method; define method application-proxy-primitive-name (application :: , proxy :: , #key decorate? = #t) => (name :: ) proxy.local-lexical-name end method; ///// $STALE-REMOTE-VALUE // This is a single (interned) instance that can be used // as the value of a tracked object that has gone stale, or an object // that couldn't be found at all. (Things get dirty if everything keeps // returning false-or all the time). define constant $stale-remote-value = as-remote-value(0); ///// DEFINITION-ID-TO-STRING-TRIPLE (Internal convenience function) // Maps a to the name, module and library. define method definition-id-to-string-triple (id :: ) => (binding-name :: , module-name :: , library-name :: ) let module-id = id-module(id); let library-id = id-library(module-id); values (id-name(id), id-name(module-id), id-name(library-id)) end method; ///// RUNTIME-PROXY-TO-REMOTE-VALUE // Internal clients of this function must ensure a debugger transaction // before calling it. // A mapping within the context of a . // Given any general , returns a for it. // This may require looking up the "current" value in the case of // a tracked proxy. // A is guaranteed to be returned from this function. If // the value is not valid, the result will be ID to the constant object // $STALE-REMOTE-VALUE. define method runtime-proxy-to-remote-value (application :: , proxy :: ) => (value :: ) $stale-remote-value end method; define method runtime-proxy-to-remote-value (application :: , proxy :: ) => (value :: ) proxy // This is just identity - no conversion is necessary! end method; define method runtime-proxy-to-remote-value (application :: , proxy :: ) call-frame-frame-pointer (application.application-target-app, proxy); end method; define method runtime-proxy-to-remote-value (application :: , proxy :: ) => (value :: ) proxy.static-foreign-value end method; define method runtime-proxy-to-remote-value (application :: , proxy :: ) => (value :: ) proxy.static-foreign-symbol.remote-symbol-address end method; define method runtime-proxy-to-remote-value (application :: , proxy :: ) proxy.library-base-address end method; define method runtime-proxy-to-remote-value (application :: , proxy :: ) => (value :: ) proxy.static-dylan-value end method; define method runtime-proxy-to-remote-value (application :: , proxy :: ) => (value :: ) remote-object-value(application.application-target-app, proxy.tracked-dylan-value) | $stale-remote-value end method; define method runtime-proxy-to-remote-value (application :: , proxy :: ) => (value :: ) let value = $stale-remote-value; block () value := read-dylan-value(application.application-target-app, proxy.application-variable-address); exception () value := $stale-remote-value end block end method; define method runtime-proxy-to-remote-value (application :: , proxy :: ) => (value :: ) let target = application.application-target-app; let (dylan-thread?, thread-name, dylan-thread-object) = remote-thread-information(target, proxy); dylan-thread-object | $stale-remote-value end method; ///// APPLICATION-NAME-TO-RUNTIME-PROXY // Internal clients of this function must ensure a debugger transaction // before calling it. // A mapping within the context of a . // Given any triple of binding-name, module-name and library-name, // either finds or generates a instance to describe it // in a canonical fashion. define method application-name-to-runtime-proxy (application :: , binding-name :: , module-name :: , library-name :: , #key constant? = #t, address-already-known = #f, value-already-known = #f, class? = #f) => (proxy :: false-or()) local method intern-id () => (id :: ) make(, name: binding-name, module: make(, name: module-name, library: make(, name: library-name))); end method; local method generate-new-proxy () => (p :: false-or()) let context = make(, library: library-name, module: module-name); let target = application.application-target-app; let (value, address) = block () let (value-d, address-d) = resolve-dylan-name(target, binding-name, context, indirect?: #f); if (value-d & address-d) values(value-d, address-d) else resolve-dylan-name(target, binding-name, context, indirect?: #t); end if end block; if (address & value) if (address = value) // If this object has a precise symbolic name, it cannot be // subject to any relocations by the MM, and hence we need // only generate a static proxy. remote-value-to-runtime-proxy(application, value); // If an indirection was performed, then the binding name // must be a variable. A variable-style proxy must be // created: the DM will need to decide whether the variable // is thread local, based upon the contents of the value // cell. elseif (thread-local-variable?(target, address)) make(, name: binding-name, namespace: context, address: address); else make(, name: binding-name, namespace: context, address: address); end if else #f end if; end method; let known-proxies = application.application-proxy-factory; let known-modules = element(known-proxies.proxy-factory-known-names, library-name, default: #f); if (known-modules) let known-names = element(known-modules, module-name, default: #f); if (known-names) let binding-proxy-pair = element(known-names, binding-name, default: #f); if (binding-proxy-pair) if (tail(binding-proxy-pair)) tail(binding-proxy-pair); else let id = head(binding-proxy-pair); let proxy = generate-new-proxy(); tail(binding-proxy-pair) := proxy; known-proxies.proxy-factory-proxy-to-id-mappings[proxy] := id; proxy; end if; else let proxy = generate-new-proxy(); let id = intern-id(); let the-pair = pair(id, proxy); add!(known-proxies.proxy-factory-ordered-data, the-pair); known-proxies.proxy-factory-proxy-to-id-mappings[proxy] := id; known-names[binding-name] := the-pair; proxy; end if else let proxy = generate-new-proxy(); let id = intern-id(); let the-pair = pair(id, proxy); add!(known-proxies.proxy-factory-ordered-data, the-pair); known-proxies.proxy-factory-proxy-to-id-mappings[proxy] := id; known-names := make(); known-names[binding-name] := the-pair; known-modules[module-name] := known-names; proxy; end if else let proxy = generate-new-proxy(); let id = intern-id(); let the-pair = pair(intern-id(), proxy); add!(known-proxies.proxy-factory-ordered-data, the-pair); known-proxies.proxy-factory-proxy-to-id-mappings[proxy] := id; let known-names = make(); known-modules := make(); known-names[binding-name] := the-pair; known-modules[module-name] := known-names; known-proxies.proxy-factory-known-names[library-name] := known-modules; proxy; end if; end method; ///// REMOTE-VALUE-TO-RUNTIME-PROXY // Internals clients of this function must ensure a debugger transaction // before calling it. // A mapping within the context of a . // Given any , either finds or generates a // instance to describe it in a canonical fashion. define method remote-value-to-runtime-proxy (application :: , value :: , #key classification :: false-or() = #f, address? :: = #f) => (proxy :: ) let proxies = application.application-proxy-factory; if (address?) enquire-object(proxies.static-address-proxies, value) else enquire-object(proxies.static-proxies, value) end | enquire-object(proxies.per-transaction-proxies, value) | remote-value-to-new-runtime-proxy (application, value, classification: classification, address?: address?) end method; define method remote-value-to-new-runtime-proxy (application :: , value :: , #key classification :: false-or() = #f, address? :: = #f) => (proxy :: ) let target = application.application-target-app; let classification = classification | classify-runtime-value(target, value, address?: address?); let is-definition? = member?(classification, #[#"dylan-class", #"dylan-method", #"dylan-generic-function"]); let is-primitive? = member?(classification, #[#"dylan-integer", #"dylan-character"]); local method intern-value-proxy (style :: ) => (p :: ) let p = select (classification) #"dylan-class" => make(, value: value); #"foreign-function" => let st = debug-target-symbol-table(target); let symbol = symbol-table-symbol-relative-address(st, value); make(, symbol: symbol); #"foreign-object" => make(, value: value); otherwise => make(, value: value); end; unless (is-primitive?) let proxy-factory = application.application-proxy-factory; select (style) #"static" => let table = if (address?) proxy-factory.static-address-proxies else proxy-factory.static-proxies end; add-object(table, value, p); #"dynamic" => add-object(proxy-factory.per-transaction-proxies, value, p); otherwise => #f; end end; p end method; if (is-definition?) let (binding-name, name-context, precise?, constant?) = find-dylan-name(target, value); if (precise? & constant?) let proxy = intern-value-proxy(#"static"); let factory = application.application-proxy-factory; let library-name = name-context.context-library; let module-name = name-context.context-module; let library-id = make(, name: library-name); let module-id = make(, name: module-name, library: library-id); let id = make(, name: binding-name, module: module-id); factory.proxy-factory-proxy-to-id-mappings[proxy] := id; proxy; else intern-value-proxy(#"dynamic"); end if else intern-value-proxy(#"dynamic") end if; end method; ///// EXCHANGE-VALUE-PROXY-FOR-BROWSABLE-CLASS-PROXY // Given a modelling an instance of a browsable class // (ie, a proxy from a ), return a proxy that // models the object's class. // This function can return #f for instances of non-browsable classes // (such as booleans, integers and characters). // Must be called from within a debugger transaction. define method exchange-value-proxy-for-browsable-class-proxy (application :: , value-proxy :: ) => (class-proxy :: false-or()) let factory = application.application-proxy-factory; let cache = factory.proxy-factory-last-object-exchanged-for-class; if (head(cache) == value-proxy) tail(cache) else let target = application.application-target-app; let instance-val = runtime-proxy-to-remote-value(application, value-proxy); let (class-val, incarnation, current-incarnation, immediate?) = dylan-object-class(target, instance-val, browsable-only?: #t); if (class-val) let class-proxy = remote-value-to-runtime-proxy(application, class-val); head(cache) := value-proxy; tail(cache) := class-proxy; class-proxy; else #f end if; end if; end method; ///// ENSURE-APPLICATION-VALUE-PROXY define method ensure-application-value-proxy (application :: , object :: ) => (ensured-proxy :: false-or()) let proxy = object.application-object-proxy; let target = application.application-target-app; unless (proxy) let id = environment-object-id(application.server-project, object); if (instance?(id, )) let (name, module-name, library-name) = definition-id-to-string-triple(id); let context = make(, module: module-name, library: library-name); let (value-d, address-d) = resolve-dylan-name(target, name, context, indirect?: #f); let (value, address) = if (value-d & address-d) values(value-d, address-d) else resolve-dylan-name(target, name, context, indirect?: #t) end if; if (value) proxy := remote-value-to-runtime-proxy(application, value); object.application-object-proxy := proxy; end if end if; end unless; proxy; end method; ///// ENSURE-APPLICATION-GLOBAL-VARIABLE-PROXY define method ensure-application-global-variable-proxy (application :: , object :: ) => (ensured-proxy :: false-or()) let proxy = object.application-object-proxy; let target = application.application-target-app; unless (instance?(proxy, )) let id = environment-object-id(application.server-project, object); if (instance?(id, )) let (name, module-name, library-name) = definition-id-to-string-triple(id); let context = make(, module: module-name, library: library-name); let (value, address) = resolve-dylan-name(target, name, context, indirect?: #t); if (address) proxy := make(, name: name, namespace: context, address: address); object.application-object-proxy := proxy; end if end if; end unless; proxy; end method; ///// ENSURE-APPLICATION-THREAD-VARIABLE-PROXY define method ensure-application-thread-variable-proxy (application :: , object :: ) => (ensured-proxy :: false-or()) let proxy = object.application-object-proxy; let target = application.application-target-app; unless (instance?(proxy, )) let id = environment-object-id(application.server-project, object); if (instance?(id, )) let (name, module-name, library-name) = definition-id-to-string-triple(id); let context = make(, module: module-name, library: library-name); let (value, address) = resolve-dylan-name(target, name, context, indirect?: #t); if (address) proxy := make(, name: name, namespace: context, address: address); object.application-object-proxy := proxy; end if end if; end unless; proxy; end method; ///// CLASS-PROXY-APPROPRIATE-USER-OBJECT-MODEL // Given a modelling a browsable user-defined class, // returns the class , or a subclass thereof, which should // instantiated as the environment model. define method class-proxy-appropriate-user-object-model (application :: , class-proxy :: ) => (eclass :: subclass()) if (class-proxy.class-proxy-environment-class-cached?) class-proxy.class-proxy-environment-class else unless (application.runtime-class-user-class-mappings-initialized?) initialize-user-object-class-mappings(application); end unless; let class-to-adopt = ; let target = application.application-target-app; let the-class = runtime-proxy-to-remote-value(application, class-proxy); block (exit) for (class-model-pair in application.runtime-class-user-class-mappings) let runtime-class = head(class-model-pair); let model-class = tail(class-model-pair); if (remote-subclass?(target, the-class, runtime-class)) class-to-adopt := model-class; exit(); end if; end for; end block; class-proxy.class-proxy-environment-class := class-to-adopt; class-proxy.class-proxy-environment-class-cached? := #t; class-to-adopt; end if end method; define method class-proxy-appropriate-user-object-model (application :: , class-proxy :: ) => (eclass :: subclass()) end method; ///// CLASS-PROXY-BROWSER-INFORMATION // Given a modelling a browsable class, obtain // all navigation information for instances of the class, and // fill in caches. // Must be called from within a debugger transaction. define method class-proxy-browser-information (application :: , class-proxy :: , #key incarnation = #f) => (slots :: , navigation :: , repeat :: false-or(), count-offset :: false-or(), element-size :: false-or(), element-offset :: false-or(), class-slot-count :: ) if (class-proxy.class-proxy-browser-data-cached? & (class-proxy.class-proxy-incarnation = incarnation)) values(class-proxy.class-proxy-slots, class-proxy.class-proxy-navigation, class-proxy.class-proxy-repeat, class-proxy.class-proxy-count-offset, class-proxy.class-proxy-element-size, class-proxy.class-proxy-element-offset, class-proxy.class-proxy-class-slot-count) else let target = application.application-target-app; let class-val = runtime-proxy-to-remote-value(application, class-proxy); let (slots, navigation, repeat, count-offset, element-size, element-offset, class-slot-count) = dylan-class-browser-information (target, class-val, use-incarnation: incarnation); class-proxy.class-proxy-slots := slots; class-proxy.class-proxy-navigation := navigation; class-proxy.class-proxy-repeat := repeat; class-proxy.class-proxy-count-offset := count-offset; class-proxy.class-proxy-element-size := element-size; class-proxy.class-proxy-element-offset := element-offset; class-proxy.class-proxy-browser-data-cached? := #t; class-proxy.class-proxy-class-slot-count := class-slot-count; class-proxy.class-proxy-incarnation := incarnation; values(slots, navigation, repeat, count-offset, element-size, element-offset, class-slot-count) end if end method; define method class-proxy-browser-information (application :: , class-proxy :: , #key incarnation = #f) => (slots :: , navigation :: , repeat :: false-or(), count-offset :: false-or(), element-size :: false-or(), element-offset :: , class-slot-count :: ) let target = application.application-target-app; let class-val = runtime-proxy-to-remote-value(application, class-proxy); dylan-class-browser-information (target, class-val, use-incarnation: incarnation) end method; /* --- Not currently used. -- Proxies are disposed on a wholesale basis, rather than individually. ///// DISPOSE-RUNTIME-PROXY // Discards a proxy for a runtime value. Also tells the environment to // invalidate the proxy, and de-registers it with the DM if it is being // tracked. // Internal function: Assumes caller has set up a debugger transaction! define method dispose-runtime-proxy (application :: , proxy :: ) => () // invalidate-application-proxy(application.server-project, proxy); end method; define method dispose-runtime-proxy (application :: , proxy :: ) => () let target = application.application-target-app; free-remote-object(target, proxy.tracked-dylan-value); // invalidate-application-proxy(application.server-project, proxy); end method; */