DIRECTORY AMBridge USING[ GetWorld, SetTVFromLC, SetTVFromWordSequence, TVForFrame, TVForPointerReferent, TVForReferent, TVForRemoteFrame, TVForRemotePointerReferent, TVForRemoteSignal, TVForSignal, TVToCardinal, TVToWordSequence, WordSequence, WordSequenceRecord ], AMEvents USING[ BootedNotifier, Event, EventProc, EventRec, Outcome ], AMEventsPrivate USING[ ], AMModel USING[ Section ], AMModelLocation USING[ CodeLocation, EntryLocations, ExitLocations ], AMTypes USING[ Assign, Class, Domain, Error, New, Range, Size, TV, TVType, Type, UnderClass ], Booting USING[ CheckpointProc, RegisterProcs, RollbackProc ], ConvertUnsafe USING[ ToRope ], DebuggerFormat USING[ DebugParameter, ExternalStateVector, SwapInfo ], DebuggerSwap USING[ CallDebugger ], FastBreak USING[ FastBreakHandler, SpecifyDefaultBreakHandler ], PrincOps USING[ BytePC, ControlLink, Frame, FrameHandle, FrameSizeIndex, FrameVec, GlobalFrame, GlobalFrameHandle, InstWord, MaxParamsInStack, NullFrame, NullLink, op, PDA, sBreak, SD, sSignal, sSignalList, StateVector, SVPointer, zBRK, zRET ], PrincOpsUtils USING[ Alloc, Free, GetReturnFrame, GetReturnLink, PsbHandleToIndex, LongCOPY, MyLocalFrame, ReadPSB, ReadXTS, SetReturnFrame, SetReturnLink, WriteXTS ], Process USING[ Abort, GetCurrent, Detach ], Rope USING[ Equal, ROPE ], RuntimeError USING [ InformationalSignal, RegisterUncaughtSignalHandler, UCSProc, UNCAUGHT ], SafeStorage USING[ EquivalentTypes, nullType ], WorldVM USING [Address, ClearBreak, CopyRead, CopyWrite, CurrentIncarnation, Go, LocalWorld, Long, SetBreak, ShortAddress, World, WorldName]; AMEventsImpl: MONITOR IMPORTS AMBridge, AMModelLocation, AMTypes, Booting, ConvertUnsafe, DebuggerSwap, FastBreak, PrincOpsUtils, Process, Rope, RuntimeError, SafeStorage, WorldVM EXPORTS AMEvents, AMEventsPrivate = { Address: TYPE = WorldVM.Address; DebugParameter: TYPE = DebuggerFormat.DebugParameter; Event: TYPE = AMEvents.Event; EventProc: TYPE = AMEvents.EventProc; ExternalStateVector: TYPE = DebuggerFormat.ExternalStateVector; ROPE: TYPE = Rope.ROPE; Section: TYPE = AMModel.Section; StateVector: TYPE = PrincOps.StateVector; StateVectorPtr: TYPE = LONG POINTER TO StateVector; SVPointer: TYPE = PrincOps.SVPointer; TV: TYPE = AMTypes.TV; Type: TYPE = AMTypes.Type; World: TYPE = WorldVM.World; swapInfoAddr: Address = LOOPHOLE[@PrincOps.PDA.available, Address]; actors: Actor _ NIL; -- the list of event watchers localActor: Actor _ NIL; clientChanged: CONDITION; -- notified when level, bootCount, running or runCount changes. oldCatcher: RuntimeError.UCSProc _ NIL; oldBreak: PROC _ NIL; supressUncaughtAborted: BOOL _ TRUE; crashOnRecursiveAppearance: BOOL _ FALSE; informing: BOOL _ TRUE; wsls: BOOL _ TRUE; breaks: REF BreakRec _ NIL; bootedNotifierRegistry: BootedNotifierRegistry _ NIL; Debugging: PUBLIC --INFORMATIONAL-- SIGNAL = CODE; Debugged: PUBLIC --INFORMATIONAL-- SIGNAL = CODE; BadControlLink: PUBLIC ERROR = CODE; BadArgType: PUBLIC ERROR = CODE; BadResStack: PUBLIC ERROR = CODE; EndSession: PUBLIC ERROR = CODE; DuplicateBreakpoint: PUBLIC ERROR = CODE; KillThisTurkey: ERROR = CODE; NotImplemented: ERROR = CODE; BreakNotFound: ERROR = CODE; Operation: TYPE = { screen, kill, activate, boot, proceed, quit, init }; BreakID: TYPE = REF BreakRec; BreakRec: PUBLIC TYPE = RECORD[ rest: REF BreakRec _ NIL, id: BreakID _ NIL, world: World, addr: Address, pc: PrincOps.BytePC, oldByte: PrincOps.op, clientData: REF]; Actor: TYPE = REF ActorObject; ActorObject: TYPE = RECORD[ next: Actor, world: World, data: REF, -- data to be passed back to the event proc proc: EventProc, -- event notification handler users: INT _ 1, -- reference count on GetEvents/StopEvents running: BOOL _ TRUE, -- client is running listener: PROCESS _ NIL, -- if not NIL, the process looking at the client bootCount: INT _ 0, -- incremented when a client session ends level: CARDINAL _ 0, -- nesting level of client procedure calls swapInfo: DebuggerFormat.SwapInfo _ NULL, esvAddr: Address _ 0, esv: ExternalStateVector _ NULL, paramAddr: Address _ 0, param: DebugParameter _ NULL, stateAddr: Address _ 0, state: StateVector _ NULL ]; BootedNotifierRegistry: TYPE = LIST OF BNRec; BNRec: TYPE = RECORD[proc: AMEvents.BootedNotifier, world: World, clientData: REF]; GetEvents: PUBLIC ENTRY PROC [world: World, data: REF, proc: EventProc] = { ENABLE UNWIND => NULL; IF proc = NIL THEN IF localActor = NIL OR data # NIL THEN ERROR ELSE {proc _ localActor.proc; data _ localActor.data}; FOR a: Actor _ actors, a.next UNTIL a = NIL DO IF a.world = world THEN { a.users _ a.users+1; a.data _ data; a.proc _ proc; EXIT}; REPEAT FINISHED => { new: Actor = NEW[ActorObject _ [actors, world, data, proc]]; actors _ new; IF Rope.Equal[WorldVM.WorldName[world], "Outload", FALSE] THEN Booting.RegisterProcs[c: MyCheckpoint, r: MyRollback, clientData: new]; IF world = WorldVM.LocalWorld[] THEN { localActor _ new; GrabLocalEvents[] } ELSE Process.Detach[new.listener _ FORK LookAtClient[new, init]]; } ENDLOOP; }; StopEvents: PUBLIC PROC [world: World] = { a: Actor; oldSession: INT; [a, oldSession] _ EntryStop[world]; FlushBreaks[world]; IF a # NIL THEN CallBootedNotifiers[a.world, oldSession]; }; EntryStop: ENTRY PROC [world: World] RETURNS [a: Actor, oldSession: INT] = { ENABLE UNWIND => NULL; prev: Actor _ NIL; FOR a _ actors, a.next UNTIL a = NIL DO IF a.world = world THEN { a.users _ a.users-1; IF a.users = 0 THEN { IF a = actors THEN actors _ actors.next ELSE prev.next _ a.next; IF a.listener # NIL THEN { Process.Abort[a.listener]; a.listener _ NIL; }; oldSession _ a.bootCount; a.bootCount _ a.bootCount + 1; BROADCAST clientChanged; IF world = WorldVM.LocalWorld[] THEN { localActor _ NIL; ReleaseLocalEvents[]; }; } ELSE a _ NIL; EXIT }; prev _ a; ENDLOOP; }; InvokeEvent: PROC [a: Actor, event: Event] RETURNS [AMEvents.Outcome] = { RETURN[ a.proc[a.data, event] ]; }; ProvokeProcessEvent: PUBLIC PROC [ p: TV, frame: TV, msg: ROPE] RETURNS [outcome: AMEvents.Outcome] = { a: Actor _ NIL; Find: ENTRY PROC = { ENABLE UNWIND => NULL; FOR a _ actors, a.next UNTIL a = NIL DO IF a.world = event.world THEN EXIT ENDLOOP; IF a # NIL THEN event.session _ a.bootCount; }; event: Event = NEW[AMEvents.EventRec _ [detail: call[msg]] ]; event.frame _ frame; event.world _ AMBridge.GetWorld[p]; event.process _ p; Find[]; RETURN[IF a # NIL THEN InvokeEvent[a, event] ELSE [proceed[NIL]] ] }; Call: PROC [world: World, which: Operation, state: SVPointer] RETURNS [ok: BOOL] = { a: Actor _ NIL; Find: ENTRY PROC = { ENABLE UNWIND => NULL; FOR a _ actors, a.next UNTIL a = NIL DO IF a.world = world THEN EXIT ENDLOOP; }; Find[]; IF a = NIL THEN RETURN[FALSE]; GetOutcome[a, which, state]; RETURN[TRUE] }; MyCheckpoint: ENTRY Booting.CheckpointProc = TRUSTED { ENABLE UNWIND => NULL; a: Actor = NARROW[clientData]; }; MyRollback: ENTRY Booting.RollbackProc = TRUSTED { ENABLE UNWIND => NULL; a: Actor = NARROW[clientData]; IF NOT a.running AND a.users # 0 THEN { a.running _ TRUE; Process.Detach[a.listener _ FORK RunClient[a, boot]]; }; }; GetOutcome: ENTRY PROC [a: Actor, which: Operation[screen..activate], state: SVPointer -- only for which = activate --] = { ENABLE UNWIND => NULL; level: CARDINAL; bootCount: INT; DO IF a.running THEN { WAIT clientChanged; LOOP }; IF a.users = 0 THEN ERROR EndSession[]; level _ a.level; bootCount _ a.bootCount; -- unique ID of this call -- a.esv.level _ a.level _ a.level + 1; EXIT ENDLOOP; a.running _ TRUE; IF which = activate THEN a.param.sv _ state^; Process.Detach[a.listener _ FORK RunClient[a, which]]; UNTIL a.level = level DO IF a.bootCount # bootCount THEN ERROR EndSession[]; WAIT clientChanged; ENDLOOP; IF which = activate THEN state^ _ a.param.sv; a.running _ FALSE; BROADCAST clientChanged; }; RemoteEvent: PROC [a: Actor, level: CARDINAL, bootCount: INT, event: Event] = { outcome: AMEvents.Outcome; Unlock: ENTRY PROC = { ENABLE UNWIND => NULL; a.running _ FALSE; BROADCAST clientChanged; }; Lock: ENTRY PROC RETURNS [BOOL] = { ENABLE UNWIND => NULL; DO IF a.running THEN { WAIT clientChanged; LOOP }; IF a.bootCount # bootCount THEN RETURN[FALSE]; IF a.level # level THEN { WAIT clientChanged; LOOP }; EXIT; ENDLOOP; a.running _ TRUE; a.listener _ Process.GetCurrent[]; RETURN[TRUE] }; Unlock[]; outcome _ InvokeEvent[a, event]; IF Lock[] THEN WITH o: outcome SELECT FROM proceed => RunClient[a, proceed]; quit => RunClient[a, quit]; ENDCASE => ERROR NotImplemented[]; }; RunClient: PROC [a: Actor, which: Operation] = { Unlock: ENTRY PROC = { ENABLE UNWIND => NULL; a.running _ FALSE; a.listener _ NIL; BROADCAST clientChanged; }; IF NOT a.running THEN ERROR; { ENABLE { ABORTED => { Unlock[]; CONTINUE }; UNWIND => Unlock[]; }; SELECT which FROM proceed, quit => { a.esv.reason _ IF which = proceed THEN proceed ELSE quit; WorldVM.CopyWrite[a.world, @a.esv, SIZE[ExternalStateVector], a.esvAddr]; IF a.stateAddr # 0 THEN WorldVM.CopyWrite[a.world, @a.state, SIZE[StateVector], a.stateAddr]; WorldVM.Go[a.world]; }; screen, kill, activate => { SELECT which FROM screen => a.esv.reason _ showscreen; kill => a.esv.reason _ kill; activate => { a.esv.reason _ call; IF a.paramAddr # 0 THEN WorldVM.CopyWrite[a.world, @a.param, SIZE[DebugParameter], a.paramAddr]; }; ENDCASE => ERROR; WorldVM.CopyWrite[a.world, @a.esv, SIZE[ExternalStateVector], a.esvAddr]; WorldVM.Go[a.world]; }; boot => NULL; ENDCASE => ERROR; LookAtClient[a, which]; }; -- ENABLE UNWIND -- }; -- RunClient -- LookAtClient: PROC [a: Actor, which: Operation] = { NotifyHappening: ENTRY PROC [event: Event] = { ENABLE UNWIND => NULL; IF event = NIL THEN a.level _ a.esv.level _ a.level - 1 ELSE { event.session _ a.bootCount; Process.Detach[FORK RemoteEvent[a, a.level, a.bootCount, event ] ]; }; a.listener _ NIL; BROADCAST clientChanged; }; IF NOT a.running THEN ERROR; -- someone should still have the lock! -- WorldVM.CopyRead[a.world, swapInfoAddr, SIZE[DebuggerFormat.SwapInfo], @a.swapInfo]; a.esvAddr _ LOOPHOLE[a.swapInfo.externalStateVector, Address]; WorldVM.CopyRead[a.world, a.esvAddr, SIZE[ExternalStateVector], @a.esv]; a.paramAddr _ WorldVM.Long[a.world, LOOPHOLE[a.esv.parameter, WorldVM.ShortAddress]]; IF a.paramAddr # 0 THEN WorldVM.CopyRead[a.world, a.paramAddr, SIZE[DebugParameter], @a.param]; a.stateAddr _ WorldVM.Long[a.world, LOOPHOLE[a.esv.state, WorldVM.ShortAddress]]; IF a.stateAddr # 0 THEN WorldVM.CopyRead[a.world, a.stateAddr, SIZE[StateVector], @a.state]; IF which # init AND a.level # a.esv.level THEN { NotifyBooted: ENTRY PROC RETURNS [oldSession: INT] = { ENABLE UNWIND => NULL; oldSession _ a.bootCount; a.bootCount _ a.bootCount+1; a.listener _ NIL; BROADCAST clientChanged; }; Start: ENTRY PROC RETURNS [ok: BOOL] = { ENABLE UNWIND => NULL; IF (ok _ a.users#0) THEN { a.listener _ Process.GetCurrent[]; a.level _ 0 } }; FlushBreaks[a.world]; CallBootedNotifiers[a.world, NotifyBooted[]]; -- with "a.running" still locked -- IF NOT Start[] THEN RETURN; }; a.esv.level _ a.level; IF a.esv.reason = return AND a.level > 0 -- return at level 0 is illegal -- THEN -- client is returning from a call -- NotifyHappening[NIL] ELSE { event: Event _ NIL; SELECT a.esv.reason FROM breakpoint, worrybreak => IF a.stateAddr = 0 THEN event _ UnknownEvent["No state for breakpoint"] ELSE { break: BreakID _ BreakEvent[a.world, @a.state, a.esv.reason=worrybreak]; IF break = NIL THEN event _ UnknownEvent["Unknown breakpoint"] ELSE event _ NEW[AMEvents.EventRec _ [worry: a.esv.reason=worrybreak, detail: break[break.id, break.clientData]] ]; }; explicitcall, worrycall => { event _ IF a.stateAddr = 0 OR a.state.stkptr = 0 THEN UnknownEvent["No argument for call debugger"] ELSE NEW[AMEvents.EventRec _ [worry: a.esv.reason=worrycall, detail: call[ RopeFromRemote[a.world, WorldVM.Long[a.world, a.state.stk[0]]]]]]; a.state.stkptr _ 0; }; uncaughtsignal => event _ SignalEvent[a.world, a.state.stk[1], a.state.stk[0]]; ENDCASE => event _ UnknownEvent["Unknown entry reason"]; IF a.stateAddr = 0 THEN event.frame _ NIL ELSE event.frame _ AMBridge.TVForRemoteFrame[ [world: a.world, worldIncarnation: WorldVM.CurrentIncarnation[a.world], fh: LOOPHOLE[a.state.dest, WorldVM.ShortAddress]], StackFromState[@a.state], a.state.instbyte = PrincOps.zRET, event.type = break ]; event.world _ a.world; event.process _ PSBIToTV[a.esv.psb, a.world]; NotifyHappening[event]; }; }; -- LookAtClient -- RopeFromRemote: PROC [world: World, addr: Address] RETURNS [ROPE] = { temp: STRING = [0]; s: STRING = [128]; -- restrict the length to protect ourselves -- min: CARDINAL = SIZE[StringBody[0]]; IF addr = 0 THEN RETURN[NIL]; WorldVM.CopyRead[world: world, from: addr, to: temp, nwords: min]; s.length _ MIN[temp.length, s.maxlength]; WorldVM.CopyRead[ world: world, from: addr+min, to: s+min, nwords: SIZE[StringBody[s.length]]-min]; RETURN[ConvertUnsafe.ToRope[s]] }; GrabLocalEvents: INTERNAL PROC = { IF wsls THEN oldCatcher _ RuntimeError.RegisterUncaughtSignalHandler[MyCatcher]; oldBreak _ PrincOps.SD[PrincOps.sBreak]; FastBreak.SpecifyDefaultBreakHandler[MyBreak]; -- RRA: first go through fast break handler PrincOps.SD[PrincOps.sBreak] _ FastBreak.FastBreakHandler; }; ReleaseLocalEvents: INTERNAL PROC = { IF wsls THEN [] _ RuntimeError.RegisterUncaughtSignalHandler[oldCatcher]; FastBreak.SpecifyDefaultBreakHandler[oldBreak]; PrincOps.SD[PrincOps.sBreak] _ oldBreak; }; WorldSwapLocalSignals: PUBLIC ENTRY PROC [yes: BOOL] = { ENABLE UNWIND => NULL; IF wsls = yes THEN RETURN; IF localActor # NIL THEN { wsls _ yes; IF wsls THEN [] _ RuntimeError.RegisterUncaughtSignalHandler[oldCatcher] ELSE oldCatcher _ RuntimeError.RegisterUncaughtSignalHandler[MyCatcher] }; }; MyCatcher: RuntimeError.UCSProc = { firstSignal: SIGNAL ANY RETURNS ANY = signal; -- to avoid name clash below! signaller: PrincOps.GlobalFrameHandle; f: PrincOps.FrameHandle _ PrincOpsUtils.GetReturnFrame[]; signaller _ f.accesslink; f _ LOOPHOLE[f.returnlink, PrincOps.FrameHandle]; IF f.accesslink = signaller THEN f _ LOOPHOLE[f.returnlink, PrincOps.FrameHandle]; IF supressUncaughtAborted AND signal = LOOPHOLE[ABORTED] THEN { TurkeyCatcher[frame]; ERROR KillThisTurkey; }; { ENABLE RuntimeError.UNCAUGHT => IF crashOnRecursiveAppearance THEN IF signal--arg of catch-phrase-- = firstSignal-- arg of this procedure -- THEN DebuggerSwap.CallDebugger["Recursively uncaught signal"]; LocalEvent[SignalEvent[WorldVM.LocalWorld[], signal, msg], f, NIL]; }; }; TurkeyCatcher: PROC [root: PrincOps.FrameHandle] = { endProcess: PrincOps.ControlLink = root.returnlink; Caller: PROC = LOOPHOLE[PrincOpsUtils.GetReturnLink[]]; root.returnlink _ [frame[PrincOpsUtils.MyLocalFrame[]]]; PrincOpsUtils.SetReturnFrame[PrincOps.NullFrame]; Caller[ ! KillThisTurkey => CONTINUE]; PrincOpsUtils.SetReturnLink[endProcess]; }; MyBreak: PROC = { state: RECORD[ padding: LONG CARDINAL, v: StateVector]; event: Event; break: BreakID; state.v _ STATE; state.v.dest _ PrincOpsUtils.GetReturnLink[]; break _ BreakEvent[WorldVM.LocalWorld[], @state.v, FALSE]; IF break = NIL THEN { localFrame: PrincOps.FrameHandle = state.v.dest.frame; globalFrame: PrincOps.GlobalFrameHandle = localFrame.accesslink; codeBase: LONG POINTER = globalFrame.code.longbase; instAddr: LONG POINTER TO PrincOps.InstWord = codeBase + localFrame.pc / 2; inst: PrincOps.op = IF localFrame.pc MOD 2 = 0 THEN instAddr.evenbyte ELSE instAddr.oddbyte; IF inst = PrincOps.zBRK THEN { state.v.dest _ LOOPHOLE[oldBreak]; state.v.source _ LOOPHOLE[PrincOpsUtils.GetReturnLink[]]; RETURN WITH state.v }; } ELSE LocalEvent[ event _ NEW[AMEvents.EventRec_[detail: break[break.id, break.clientData]] ], PrincOpsUtils.GetReturnFrame[], @state.v, state.v.instbyte = PrincOps.zRET]; state.v.dest _ PrincOpsUtils.GetReturnLink[]; state.v.source _ PrincOps.NullLink; IF PrincOpsUtils.ReadXTS[] = on THEN PrincOpsUtils.WriteXTS[skip1]; RETURN WITH state.v; }; CallDebugger: PUBLIC PROC [msg: ROPE] = { IF localActor # NIL THEN LocalEvent[ NEW[AMEvents.EventRec _ [detail: call[msg]] ], PrincOpsUtils.GetReturnFrame[], NIL] ELSE DebuggerSwap.CallDebugger["Call debugger"]; }; LocalEvent: PROC [event: Event, f: PrincOps.FrameHandle, stack: SVPointer, return: BOOL _ FALSE] = { outcome: AMEvents.Outcome; event.world _ localActor.world; event.session _ localActor.bootCount; event.process _ PSBIToTV[PrincOpsUtils.PsbHandleToIndex[PrincOpsUtils.ReadPSB[]], localActor.world]; event.frame _ AMBridge.TVForFrame[f, stack, return, event.type = break]; event.worry _ FALSE; IF informing THEN RuntimeError.InformationalSignal[Debugging]; outcome _ InvokeEvent[localActor, event]; IF informing THEN RuntimeError.InformationalSignal[Debugged]; WITH o: outcome SELECT FROM proceed => NULL; quit => ERROR ABORTED -- I don't like it, but that's the convention for now --; retry, returnFrom => ERROR NotImplemented[]; ENDCASE => ERROR; }; UnknownEvent: PROC [msg: ROPE] RETURNS [event: Event] = { event _ NEW[AMEvents.EventRec _ [worry: TRUE, detail: unknown[msg]]]; }; StackFromState: PROC [state: StateVectorPtr] RETURNS [stack: AMBridge.WordSequence] = { stack _ NEW[AMBridge.WordSequenceRecord[state.stkptr]]; FOR i: INT IN [0..stack.size) DO stack[i] _ state.stk[i] ENDLOOP; }; SignalEvent: PROC [world: World, signal, msg: UNSPECIFIED] RETURNS [Event] = { local: BOOL = (world = WorldVM.LocalWorld[]); sigTV: TV; argType: Type; arg: TV; {ENABLE AMTypes.Error => GOTO cant; sigTV _ IF local THEN AMBridge.TVForSignal[LOOPHOLE[signal, ERROR ANY RETURNS ANY]] ELSE AMBridge.TVForRemoteSignal[ [world: world, worldIncarnation: WorldVM.CurrentIncarnation[world], sed: signal] ]; argType _ AMTypes.Domain[AMTypes.TVType[sigTV]]; IF argType = SafeStorage.nullType THEN arg _ NIL ELSE IF AMTypes.Size[argType] = 1 THEN { arg _ AMTypes.New[argType, mutable, world]; AMBridge.SetTVFromLC[arg, LOOPHOLE[msg, CARDINAL]] } ELSE arg _ TVForPointer[world, LOOPHOLE[msg, WorldVM.ShortAddress], argType]; EXITS cant => { sigTV _ AMBridge.TVForReferent[NEW[CARDINAL _ LOOPHOLE[signal, CARDINAL]]]; arg _ AMBridge.TVForReferent[NEW[CARDINAL _ LOOPHOLE[msg, CARDINAL]]]; }; }; RETURN[ NEW[AMEvents.EventRec _ [detail: signal[sigTV, arg]]] ] }; TVForPointer: PROC [world: World, ptr: WorldVM.ShortAddress, type: Type] RETURNS [tv: TV] = { IF world = WorldVM.LocalWorld[] THEN tv _ AMBridge.TVForPointerReferent[LOOPHOLE[ptr, POINTER], type] ELSE tv _ AMBridge.TVForRemotePointerReferent[ [world: world, worldIncarnation: WorldVM.CurrentIncarnation[world], ptr: WorldVM.Long[world, ptr]], type]; }; PSBIToTV: PROC [psbi: CARDINAL, world: World] RETURNS [p: TV] = { ENABLE AMTypes.Error => { p _ NIL; CONTINUE }; p _ AMTypes.New[CODE[PROCESS], mutable, world]; AMBridge.SetTVFromLC[p, psbi]; }; BreakAt: PUBLIC PROC [world: World, section: Section, clientData: REF] RETURNS [id: BreakID] = { id _ BreakIt[TRUE, world, section, clientData]; }; BreakAfter: PUBLIC PROC [world: World, section: Section, clientData: REF] RETURNS [id: BreakID] = { id _ BreakIt[FALSE, world, section, clientData]; }; BreakIt: PROC [at: BOOL, world: World, section: Section, clientData: REF] RETURNS [id: BreakID] = { list: LIST OF AMModelLocation.CodeLocation; secWorld: World; [secWorld, list] _ IF at THEN AMModelLocation.EntryLocations[section] ELSE AMModelLocation.ExitLocations[section]; id _ NIL; FOR loc: LIST OF AMModelLocation.CodeLocation _ list, loc.rest UNTIL loc = NIL DO loc.first.codeBase.out _ FALSE; id _ RealSetBreak[id, world, LOOPHOLE[loc.first.codeBase.longbase], loc.first.pc, clientData]; ENDLOOP; }; FrameBreak: PUBLIC PROC [gf: TV, pc: CARDINAL, clientData: REF] RETURNS [id: BreakID] = { gfAddr: WorldVM.ShortAddress = AMBridge.TVToCardinal[gf]; world: World = AMBridge.GetWorld[gf]; gfHead: PrincOps.GlobalFrame; WorldVM.CopyRead[world: world, from: WorldVM.Long[world, gfAddr], to: @gfHead, nwords: SIZE[PrincOps.GlobalFrame]]; gfHead.code.out _ FALSE; id _ SetBreak[world, LOOPHOLE[gfHead.code.longbase], pc, clientData]; }; SetBreak: PUBLIC PROC [world: World, addr: Address, pc: CARDINAL, clientData: REF] RETURNS [id: BreakID] = { id _ RealSetBreak[NIL, world, addr, pc, clientData]; }; RealSetBreak: ENTRY PROC [id: BreakID, world: World, addr: Address, pc: CARDINAL, clientData: REF] RETURNS [newID: BreakID] = { ENABLE UNWIND => NULL; new: REF BreakRec = NEW[BreakRec _ [world: world, addr: addr, pc: [pc], oldByte: , clientData: clientData]]; new.id _ newID _ IF id = NIL THEN new ELSE id; FOR b: REF BreakRec _ breaks, b.rest UNTIL b = NIL DO x: BreakID = b; IF x.world = world AND x.addr = addr AND x.pc = pc THEN RETURN WITH ERROR DuplicateBreakpoint[]; ENDLOOP; new.oldByte _ WorldVM.SetBreak[world, addr, [pc]]; new.rest _ breaks; breaks _ new; }; ClearBreak: PUBLIC ENTRY PROC [id: BreakID] = { ENABLE UNWIND => NULL; prev: REF BreakRec _ NIL; FOR b: REF BreakRec _ breaks, b.rest UNTIL b = NIL DO IF b.id = id THEN { WorldVM.ClearBreak[b.world, b.addr, b.pc, b.oldByte]; IF b = breaks THEN breaks _ b.rest ELSE prev.rest _ b.rest; b.id _ NIL; -- break circular structure -- EXIT } ELSE prev _ b; REPEAT FINISHED => NULL -- probably from a previous session, abolished by FlushBreaks ENDLOOP; }; FlushBreaks: ENTRY PROC [world: World] = { ENABLE UNWIND => NULL; prev: REF BreakRec _ NIL; FOR b: REF BreakRec _ breaks, b.rest UNTIL b = NIL DO IF b.world = world THEN { IF b = breaks THEN breaks _ b.rest ELSE prev.rest _ b.rest; b.id _ NIL; -- break circular structure -- } ELSE prev _ b; REPEAT FINISHED => NULL -- probably from a previous session, abolished by FlushBreaks ENDLOOP; }; NextBreak: PUBLIC ENTRY PROC [world: World, prev: BreakID] RETURNS [id: BreakID, clientData: REF] = { ENABLE UNWIND => NULL; FOR b: REF BreakRec _ (IF prev = NIL THEN breaks ELSE prev.rest), b.rest UNTIL b = NIL DO IF b.id = b AND ( b.world = world OR world = NIL ) THEN RETURN[b, b.clientData]; ENDLOOP; RETURN[NIL,NIL] }; BreakEvent: PROC [world: World, state: StateVectorPtr, worry: BOOL] RETURNS [break: BreakID] = { localFrame: PrincOps.Frame; globalFrame: PrincOps.GlobalFrame; word: Address; byte: [0..1]; FindBreak: ENTRY PROC RETURNS [BreakID] = { ENABLE UNWIND => NULL; FOR b: REF BreakRec _ breaks, b.rest UNTIL b = NIL DO IF b.world = world AND b.addr + b.pc/2 = word AND b.pc MOD 2 = byte THEN { state.instbyte _ b.oldByte; RETURN[ b ] }; ENDLOOP; state.instbyte _ 0; RETURN[NIL] }; WorldVM.CopyRead[world: world, from: WorldVM.Long[world, LOOPHOLE[state.dest.frame, WorldVM.ShortAddress]], to: @localFrame, nwords: SIZE[PrincOps.Frame] ]; WorldVM.CopyRead[world: world, from: WorldVM.Long[world, LOOPHOLE[localFrame.accesslink, WorldVM.ShortAddress]], to: @globalFrame, nwords: SIZE[PrincOps.GlobalFrame] ]; word _ LOOPHOLE[globalFrame.code.longbase, Address] + localFrame.pc / 2; byte _ localFrame.pc MOD 2; RETURN[FindBreak[]]; }; Apply: PUBLIC PROC [control, args: TV] RETURNS [result: TV] = { world: World = AMBridge.GetWorld[control]; local: BOOL = world = WorldVM.LocalWorld[]; controlType: Type = AMTypes.TVType[control]; class: AMTypes.Class _ AMTypes.UnderClass[controlType]; argType: Type; argBits: AMBridge.WordSequence; resType: Type; resSize: CARDINAL; state: StateVector; dstPtr: LONG POINTER _ @state.stk; SELECT class FROM procedure, signal, globalFrame => NULL; error => class _ signal; ENDCASE => ERROR BadControlLink[]; state.dest _ LOOPHOLE[AMBridge.TVToCardinal[control]]; state.source _ LOOPHOLE[PrincOpsUtils.MyLocalFrame[]]; argType _ AMTypes.Domain[controlType]; IF (argType = SafeStorage.nullType AND args # NIL) OR NOT SafeStorage.EquivalentTypes[argType, AMTypes.TVType[args]] OR (argType # SafeStorage.nullType AND world # AMBridge.GetWorld[args]) THEN ERROR BadArgType[]; argBits _ IF argType = SafeStorage.nullType THEN NEW[AMBridge.WordSequenceRecord[0]] ELSE AMBridge.TVToWordSequence[args]; IF argBits.size > PrincOps.MaxParamsInStack OR (class = signal AND argBits.size > 1) THEN { -- long argument record -- IF NOT local THEN ERROR BadArgType[]; FOR i: PrincOps.FrameSizeIndex IN PrincOps.FrameSizeIndex DO IF argBits.size <= PrincOps.FrameVec[i] THEN { state.stk[0] _ PrincOpsUtils.Alloc[i]; EXIT } REPEAT FINISHED => ERROR--args too big for any frame!-- ENDLOOP; state.stkptr _ 1; dstPtr _ LOOPHOLE[state.stk[0], POINTER]; } ELSE { -- short argument record -- state.stkptr _ argBits.size; }; IF argBits.size # 0 THEN PrincOpsUtils.LongCOPY[from: @argBits[0], to: dstPtr, nwords: argBits.size]; IF class = signal THEN { IF NOT local THEN ERROR BadControlLink[]; state.stk[1] _ state.stk[0]; -- message -- state.stk[0] _ state.dest; -- signal -- state.stkptr _ 2; state.dest _ PrincOps.SD[IF argBits.size > 1 THEN PrincOps.sSignalList ELSE PrincOps.sSignal]; }; state.instbyte _ 0; IF local THEN { TRANSFER WITH state; state _ STATE } ELSE IF NOT Call[world, activate, @state] THEN ERROR--we aren't debugging that world--; resType _ AMTypes.Range[controlType]; IF resType = SafeStorage.nullType THEN { IF state.stkptr # 0 THEN ERROR BadResStack[]; result _ NIL } ELSE { resSize _ AMTypes.Size[resType]; result _ AMTypes.New[type: resType, world: world]; IF resSize > PrincOps.MaxParamsInStack THEN { res: TV; IF state.stkptr # 1 THEN ERROR BadResStack[]; res _ TVForPointer[world, LOOPHOLE[state.stk[0],WorldVM.ShortAddress], resType]; AMTypes.Assign[lhs: result, rhs: res]; IF local THEN PrincOpsUtils.Free[LOOPHOLE[state.stk[0]]] ELSE NULL--TEMP?--; } ELSE { IF state.stkptr # resSize THEN ERROR BadResStack[]; IF local THEN AMTypes.Assign[ lhs: result, rhs: AMBridge.TVForPointerReferent[@state.stk, resType]] ELSE { ws: AMBridge.WordSequence _ NEW[AMBridge.WordSequenceRecord[resSize]]; PrincOpsUtils.LongCOPY[from: @state.stk, nwords: resSize, to: @ws[0]]; AMBridge.SetTVFromWordSequence[tv: result, ws: ws]}; } }; }; Kill: PUBLIC SAFE PROC [world: World] = TRUSTED { state: StateVector; IF world # WorldVM.LocalWorld[] THEN [] _ Call[world, kill, @state ! EndSession => CONTINUE]; }; Screen: PUBLIC SAFE PROC [world: World] = TRUSTED { state: StateVector; IF world # WorldVM.LocalWorld[] THEN [] _ Call[world, screen, @state ! EndSession => CONTINUE]; }; RegisterBootedNotifier: PUBLIC ENTRY SAFE PROC [proc: AMEvents.BootedNotifier, world: World _ NIL, clientData: REF _ NIL] = TRUSTED{ ENABLE UNWIND => NULL; bootedNotifierRegistry _ CONS[[proc, world, clientData], bootedNotifierRegistry]; }; UnRegisterBootedNotifier: PUBLIC ENTRY SAFE PROC [proc: AMEvents.BootedNotifier, world: World _ NIL, clientData: REF _ NIL] = TRUSTED{ ENABLE UNWIND => NULL; prev: BootedNotifierRegistry _ NIL; FOR this: BootedNotifierRegistry _ bootedNotifierRegistry, this.rest UNTIL this = NIL DO IF this.first.proc = proc AND (world = NIL OR world = this.first.world) AND (clientData = NIL OR clientData = this.first.clientData) THEN {IF prev = NIL THEN bootedNotifierRegistry _ this.rest ELSE prev.rest _ this.rest} ELSE prev _ this; ENDLOOP; }; CallBootedNotifiers: PROC [world: World, session: INT] = { FOR bnr: BootedNotifierRegistry _ bootedNotifierRegistry, NextBootedNotifier[world, bnr] UNTIL bnr = NIL DO bnr.first.proc[world, session, bnr.first.clientData]; ENDLOOP; }; NextBootedNotifier: ENTRY PROC [world: World, bnr: BootedNotifierRegistry] RETURNS [next: BootedNotifierRegistry _ NIL--nomore--] = { ENABLE UNWIND => NULL; FOR this: BootedNotifierRegistry _ bnr.rest, this.rest UNTIL this = NIL DO IF this.first.world = NIL OR world = this.first.world THEN RETURN[this]; ENDLOOP; }; }. ΌAMEventsImpl.mesa Copyright c 1984 by Xerox Corporation. All rights reserved. Andrew Birrell October 25, 1983 12:08 pm Russ Atkinson, December 13, 1984 5:06:12 pm PST Maxwell, March 31, 1983 8:05 am (search for 'JTM') Paul Rovner January 12, 1984 11:17 am EventProc: TYPE = PROC[data: REF ANY, event: Event] RETURNS[outcome: Outcome]; Event: TYPE = REF EventRec; Eventuality: TYPE = { break, call, signal, unknown }; EventRec: TYPE = RECORD[ world: World, session: INT, process: RTBasic.TV, frame: RTBasic.TV, worry: BOOLEAN, detail: SELECT type: Eventuality FROM break => [id: BreakID, clientData: REF ANY], call => [msg: Rope.ROPE], signal => [signal, args: RTBasic.TV ], unknown => [why: Rope.ROPE], ENDCASE]; Outcome: TYPE = RECORD[ SELECT type: * FROM proceed => [result: RTBasic.TV], quit => NULL, retry => [frame, result: RTBasic.TV], returnFrom => [frame, result: RTBasic.TV], ENDCASE] _ [proceed[NIL]]; Stack: TYPE = RECORD[ SEQUENCE length: CARDINAL OF WORD ]; ******** Constants ******** -- This address is the root of the interface with PilotNub -- ******** Global Variables ******** -- This is TRUE to kill off processes that do not have a base frame that catches ABORTED and flush out the process. This makes it easier to kill off processes. You can set this FALSE to find processes that are being aborted when you don't think that they are. RRA: We can set this to TRUE to carsh when a signal appears to be recursive. Experience has shown that signals appear to be recursive more often than they are recursive, however. Whether to raise the informational signals. You have to remember that ANY handles informational signals, so you probably want to use RuntimeError.UNCAUGHT to handle errors that others will not handle. If TRUE, will world-swap uncaught local signals, instead of sending them to the registered handler. This is useful before the handler is established, or when the handler is buggy. ******** ERRORS and SIGNALS ******** -- PUBLIC Private to this module ******** TYPES ******** -- what to do to client before reading his state -- There may be multiple BreakRec's for a single breakpoint; the first is used as the ID. -- Management of event watchers ("Actor") for each world common fields the remainder is only for world-swap and teledebug clients the remainder is data for interfacing with client Locking for fields in an ActorObject: Immutable: a.world, a.data, a.proc. Inside monitor: a.next, a.users, a.running, a.listener, a.bootCount By claiming "running": a.swapInfo, a.esvAddr, a.esv, a.paramAddr, a.param, a.stateAddr, a.state "a.level" is altered with the monitor and a.running claimed, so it may be read either inside the monitor or by claiming a.running. There are three sources of synchronisation problems: * ensuring that only one process transfers control to the client at a time * the client booting * someone calling "StopEvents" The a.running field provides mutual exclusion on transferring to the client. The a.bootCount field indicates when the client has booted. This is notified to the public through the BootedNotifiers. Having a.users=0 indicates a desire to disconnect from the client. This is notified to the BootedNotifiers and by aborting a.listener. Note that we never access the client with our monitor locked: local events require access to our monitor, but access to a remote client involves unbounded delays. here to make a new actor ******** Events for non-local worlds ******** -- Perform call (or UserScreen, or Kill) and get result. Some locking notes "a.running" is a global exclusive lock. It must be claimed before transferring control to the client. It is released by "RemoteEvent" or "GetOutcome". "a.bootCount" is incremented when client boots, to indicate failure of outstanding calls, and to intercept resumption from outstanding events. "a.level" and "esv.level" are used to detect the client booting (when they're unequal). a.level is incremented when making a call, to allow detection of which "return" corresponds to this call. TEMP: PilotNub believes that calls are nested. PilotNub doesn't alter esv.level, so we can't match up a return with the appropriate call correctly. Instead, we increment a.level (and esv.level) before making a call, and inside "RunClient" we decrement them when we notice a return. Thus, outside of "RunClient", we can pretend that a.level (and esv.level) are a unique ID for a call. This kludge can be removed someday be changing PilotNub's handling of calls/returns. Note that there are cases where this kludge doesn't give the right answer; hard luck. "RunClient" transfers control to client, waits (implicitly, inside WVM) until client invokes us, then looks to see why. If client has booted, calls booted-notifiers; if client returns from a call (not necessarily one we invoked), notifies presence of the result; if client has a new event, calls "RemoteEvent" to notify the event. Notify return from some call, or some event -- client has booted: report to notifiers (but not first time: think of as "endSession"). client is invoking us, or is hopelessly confused -- addr is remote address of a string (or 0) ******** Local events ******** -- RRA: this may cause problems, but they are no worse than for regular breakpoints that the old handler does not understand. I believe that this procedure is completely useless. The call stack below here is: Signaller, [Signaller,] offender breakpoint must have been set by world-swap debugger ******** Subroutines for both local and non-local worlds ******** -- ******** Breakpoints ******** -- JTM: don't set break until allocations are done! called at end of session; breaks have gone away JTM: changed BreakEvent to return a BreakID rather than an Event If we get here, we've hit a break point not recorded in our list. This is either a world-swap breakpoint, or a race where we cleared the break point after the process hit it. If it's the race, we can fix it by retrying the instruction. We can distinguish the cases by looking at the code byte (see the local breakpoint handler). -- ******** Invoking procedures in client worlds ******** -- otherwise we'd get a bounds trap really, call the signal handler through SD SignalHandler: PROC[signal: SIGNAL, message: UNSPECIFIED] long result record short result record Κ&”˜šœ™Jšœ Οmœ1™<—˜Jšœ)™)Jšœ/™/Jšœ2™2Jšœ&™&—J˜codešΟk ˜ Kšœ žœς˜€Kšœ žœ8˜FKšœžœ˜Kšœžœ ˜Kšœžœ0˜EKšœžœ2žœ˜^Kšœžœ0˜=Kšœžœ ˜Kšœžœ2˜FKšœ žœ˜#Kšœ žœ1˜@Kšœ žœšžœ žœ=˜τKšœžœ”˜§Kšœžœ˜+Kšœžœ žœ˜Kšœ žœ@žœ˜]Kšœ žœ˜/Kšœžœ€˜—J˜šœž˜Kšžœ–˜Jšžœ˜%J˜Jšœ žœ˜ Jšœžœ!˜5Jšœžœ˜Jšœ žœ˜%Jšœžœ&˜?Jšžœžœžœ˜Jšœ žœ˜ Jšœ žœ˜)Jš œžœžœžœžœ ˜3Jšœ žœ˜%Jšžœžœ žœ˜Jšœžœ˜Jšœžœ˜—˜JšœN™NJ™Jšœ™J™šœ™Jšœ™—J™šœ™Jšœ™Jšœ ™ Jšœ™Jšœ™Jšœ™šœ%™%Jšœ.™.Jšœ™Jšœ'™'Jšœ™—Jšœ ™ —J™šœ™šœ™Jšœ!™!Jšœ™Jšœ'™'Jšœ*™*—Jšœ™—J™Jšœ:™:J˜J˜—šœ™J˜šœžœ žœ˜CJšœ:™:J˜—J˜—šœ%™%J˜JšœžœΟc˜3Jšœžœ˜Jšœž œŸ?˜YJšœ#žœ˜'Jšœ žœžœ˜J˜šœžœžœ˜$JšœžœBžœ[žœM™‚—šœžœžœ˜)Jšžœžœ—™³—J˜šœ žœžœ˜JšœGžœIžœ.™Ι—šœžœžœ˜Jšœžœ­™΄—J˜Jšœžœ žœ˜Jšœ1žœ˜5J˜J˜—Jšœ'™'™Jšœ™Jš œ žœŸœžœžœ˜2Jš œ žœŸœžœžœ˜1Jšœžœžœžœ˜$Jšœ žœžœžœ˜ Jšœ žœžœžœ˜!Jšœ žœžœžœ˜ šœžœžœžœ˜)J™—Jšœ™Jšœžœžœ˜Jšœžœžœ˜Jšœžœžœ˜J˜—J˜šœ™J˜šœ žœ9˜HJšœ0™0—J˜Jšœ žœžœ ˜J˜JšœZ™Zšœ žœžœžœ˜Jšœžœ žœ˜Jšœžœ˜Jšœ ˜ Jšœ˜J˜J˜Jšœ žœ˜J˜—Jšœ5™5Jšœžœžœ ˜–2.2 in tabStopsšœ žœžœ˜–2.2 in tabStops™ J–2.2 in tabStops˜ J–2.2 in tabStops˜ J–2.2 in tabStopsšœžœŸ+˜6J–2.2 in tabStopsšœŸ˜.J–2.2 in tabStopsšœžœŸ*˜:—–2.2 in tabStopsšœ:™:J–2.2 in tabStopsšœ žœžœŸ˜*J–2.2 in tabStopsšœ žœžœŸ0˜IJ–2.2 in tabStopsšœ žœŸ)˜=J–2.2 in tabStopsšœžœŸ*˜?—–2.2 in tabStopsšœ1™1J–2.2 in tabStopsšœ$žœ˜)J–2.2 in tabStopsšœ˜J–2.2 in tabStopsšœžœ˜ J–2.2 in tabStopsšœ˜J–2.2 in tabStopsšœžœ˜J–2.2 in tabStopsšœ˜J–2.2 in tabStopsšœžœ˜—J–2.2 in tabStops˜—Jšœžœžœžœ˜-J˜šœžœžœ˜3Jšœ ˜ Jšœ žœ˜J˜J˜——šœ%™%Jšœ$™$JšœC™CJ™Jšœ_™_Jšœ‚™‚J˜šœ4™4JšœJ™JJšœ™Jšœ™—JšœL™LJšœx™xJšœ‡™‡J˜Jšœ£™£J˜—š Οn œžœžœžœžœ˜KJšžœžœžœ˜šžœž˜šžœžœžœž˜!Jšžœž˜ Jšžœ2˜6——šžœžœž˜.šžœžœ˜Jšœ˜Jšœ˜Jšœ˜Jšžœ˜—šžœžœ˜Jšœ™Jšœ žœ,˜JšœG˜G—šžœ˜Jšžœ(˜,Jšžœžœ˜A—Jšœ˜—Jšžœ˜—Jšœ˜J˜—š  œžœžœ˜*J˜ Jšœ žœ˜J˜#J˜Jšžœžœžœ*˜9Jšœ˜J˜—š   œžœžœžœžœ˜LJšžœžœžœ˜Jšœžœ˜šžœžœž˜'šžœžœ˜J˜šžœ ˜šžœ˜Jšžœ žœžœ˜@šžœžœžœ˜Jšœ˜Jšœ žœ˜Jšœ˜—Jšœ˜Jšœž œ˜7šžœžœ˜&Jšœ žœ˜Jšœ˜Jšœ˜—Jšœ˜—Jšžœžœ˜ —Jšž˜Jšœ˜—J˜ Jšžœ˜—Jšœ˜J˜—š  œžœž œ˜IJšžœ˜ Jšœ˜J˜—š œžœžœžœ žœžœž œ˜gJšœ žœ˜š œžœžœ˜Jšžœžœžœ˜Jšžœžœž˜$Jš žœžœžœžœžœ˜.Jšžœžœžœ˜,Jšœ˜—Jšœžœ+˜=J˜J˜#J˜J˜Jš žœžœžœžœžœ žœ˜BJšœ˜J˜—Jšœ0™0J˜š œžœ2ž œžœ˜TJšœ žœ˜š œžœžœ˜Jšžœžœžœ˜Jšžœžœžœžœžœžœžœ˜MJšœ˜—J˜Jš žœžœžœžœžœ˜J˜Jšžœžœ˜ Jšœ˜J˜—šœžœžœ˜6Jšžœžœžœ˜Jšœ žœ ˜Jšœ˜—J˜šœ žœžœ˜2Jšžœžœžœ˜Jšœ žœ ˜šžœžœ žœ žœ˜'Jšœ žœ˜Jšœžœ˜5Jšœ˜—Jšœ˜—J˜š  œžœžœ@Ÿœ˜{Jšœ5™5Jšžœžœžœ˜Jšœžœ˜Jšœ žœ˜šž˜Jšžœ žœžœžœ˜/Jšžœ žœžœ˜'Jšœ*Ÿ˜FJ˜$Jšž˜Jšžœ˜—Jšœ žœ˜Jšžœžœ˜-Jšœžœ˜6šžœž˜Jšžœžœžœ˜3Jšžœ˜Jšžœ˜—Jšžœžœ˜-Jšœ žœ˜Jšž œ˜Jšœ˜J˜—š  œžœžœ žœ˜OJ˜š œžœžœ˜Jšžœžœžœ˜Jšœ žœ˜Jšž œ˜Jšœ˜—š  œžœžœžœžœ˜#Jšžœžœžœ˜šž˜Jšžœ žœžœžœ˜/Jšžœžœžœžœ˜.Jšžœžœžœžœ˜5Jšžœ˜Jšžœ˜—Jšœ žœ˜J˜"Jšžœžœ˜ Jšœ˜—J˜ J˜ šžœž˜šžœ žœž˜J˜!J˜Jšžœžœ˜"——Jšœ˜J˜—™Jšœ˜™˜JšœŽ™ŽJšœW™WJšœi™iJšœ±™±—J˜š  œžœ!˜0JšœΝ™Νš œžœžœ˜Jšžœžœžœ˜Jšœ žœ˜Jšœ žœ˜Jšž œ˜Jšœ˜—Jšžœžœ žœžœ˜šœ˜šžœ˜Jšžœžœ˜"Jšžœ ˜Jšœ˜—šžœž˜šœ˜Jšœžœžœ žœ˜9Jšœ#žœ"˜Išžœž˜Jšœ%žœ˜E—J˜Jšœ˜—šœ˜šžœž˜J˜$J˜šœ ˜ J˜šžœž˜Jšœ%žœ˜H—Jšœ˜—Jšžœžœ˜—Jšœ#žœ"˜IJ˜Jšœ˜—Jšœžœ˜ Jšžœžœ˜—Jšœ˜JšœŸ˜—JšœŸ˜J˜—š  œžœ ˜3š œžœžœ˜.Jšœ.™.Jšžœžœžœ˜šžœ ž˜Jšžœ$˜(šžœ˜Jšœ˜Jšœžœ0˜CJšœ˜——Jšœ žœ˜Jšž œ˜Jšœ˜—Jš žœžœ žœžœŸ)˜FJšœ(žœ(˜TJšœ žœ*˜>Jšœ%žœ˜HJšœ#ž œ)˜Ušžœž˜Jšœ'žœ˜G—Jšœ#ž œ%˜Qšžœž˜Jšœ'žœ˜D—šžœžœžœ˜0JšœV™Vš   œžœžœžœ žœ˜6Jšžœžœžœ˜Jšœ˜Jšœ˜Jšœ žœž œ˜*Jšœ˜—š  œžœžœžœžœ˜(Jšžœžœžœ˜Jšžœžœ3˜KJšœ˜—J˜Jšœ.Ÿ#˜QJšžœžœ žœžœ˜Jšœ˜—Jšœ˜šžœžœ Ÿ#˜KJšžŸ&œžœ˜?šžœ˜Jšœ3™3Jšœžœ˜šžœž˜˜šžœ˜Jšžœ0˜4šžœ˜J˜Hšžœ žœ˜Jšžœ+˜/šžœ žœ˜%J˜N——Jšœ˜———šœ˜šœžœžœ˜0Jšžœ.˜2šžœžœB˜JJ˜B——J˜Jšœ˜—˜J˜=—šžœ˜ J˜-——šžœ˜Jšžœž˜šžœ)˜-˜J˜6Jšœžœ&˜2—J˜J˜!J˜——J˜J˜-J˜Jšœ˜——JšžœŸ˜J˜—š œžœžœžœ˜EJšœ)™)Jšœžœ˜Jšœžœ Ÿ.˜AJšœžœžœ˜$Jšžœ žœžœžœ˜J˜BJšœ žœ˜)šœ˜Jšœ1žœ˜Q—Jšžœ˜Jšœ˜J˜J˜—Jšœ!™!J˜š œžœžœ˜"JšžœžœD˜PJšœžœ˜(Jšœ/Ÿ+˜ZJšœ žœ/˜:Jšœ˜J˜—š œžœžœ˜%Jšžœžœ=˜Išœ/˜/Jšœ°™°—Jšœ žœ˜(Jšœ˜J˜—š  œžœžœžœžœ˜8Jšžœžœžœ˜Jšžœ žœžœ˜šžœžœ˜Jšœ ˜ šžœ˜Jšžœ<˜@JšžœC˜G—Jšœ˜—Jšœ˜J˜—šœ#˜#Jš œ žœžœžœžœ Ÿ˜KJšœ&˜&J˜9J˜Jšœ>™>Jšœžœ%˜1Jšžœžœžœ%˜Rš žœžœ žœžœžœ˜?Jšœ˜Jšžœ˜Jšœ˜—šœ˜šžœžœ˜šžœž˜"šžœŸœŸœž˜NJšœ9˜9———Jšœ>žœ˜CJšœ˜—Jšœ˜J˜—š  œžœ!˜4J˜3Jš œžœžœ ˜7J˜8J˜1Jšœžœ˜&J˜(Jšœ˜J˜—š œžœ˜Jšœžœ žœžœ˜7Jšœ ˜ J˜Jšœ žœ˜J˜-Jšœ3žœ˜:šžœ ž˜šžœ˜J˜6J˜@Jšœ žœžœ˜3Jšœ žœžœžœ2˜K˜Jšžœžœžœžœ˜H—šžœžœ˜Jšœ4™4Jšœžœ ˜"Jšœžœ ˜9Jšžœžœ˜Jšœ˜—Jšœ˜—šžœ ˜JšœžœB˜MJ˜L——J˜-J˜#Jšžœžœ˜CJšžœžœ ˜Jšœ˜J˜—š  œžœžœžœ˜)šžœž˜šžœ ˜JšžœLžœ˜S—Jšžœ,˜0—Jšœ˜J˜—š  œžœBžœžœ˜dJ˜J˜J˜%˜JšœT˜T—J˜HJšœžœ˜Jšžœ žœ-˜>J˜)Jšžœ žœ,˜=šžœ žœž˜Jšœ žœ˜JšœžœžœŸ8œ˜OJšœžœ˜,—Jšžœžœ˜Jšœ˜J˜—š  œžœžœžœ˜9Jšœžœžœ˜EJšœ˜J˜—JšœD™DJ˜š œžœž œ"˜WJšœžœ,˜7Jš žœžœžœžœžœ˜AJšœ˜J˜—š  œžœž œž œ ˜NJšœžœ"˜-Jšœžœ˜ Jšœ˜Jšœžœ˜šœžœžœ˜#šœžœ˜Jš žœžœ žœžœžœžœ˜Bšžœ˜ J˜S——J˜0šžœ˜!Jšžœž˜šžœžœ˜!šžœ.˜2Jšœžœžœ˜4—Jšžœžœ&˜M——šžœ ˜Jš œžœžœžœ žœ˜KJš œžœžœžœžœ˜FJšœ˜—Jšœ˜—Jšžœžœ4˜?Jšœ˜J˜—š  œžœ5ž œžœ˜]šžœ˜Jšžœ$žœžœ˜Ešžœ*˜.˜J˜4J˜—J˜——Jšœ˜J˜—š  œžœžœžœžœ˜AJšžœžœžœ˜.Jšœžœžœ˜/J˜Jšœ˜J˜J˜—Jšœ ™ J˜š  œžœžœ-žœž œ˜`Jšœ žœ˜/Jšœ˜J˜—š   œžœžœ-žœž œ˜cJšœ žœ˜0Jšœ˜J˜—š  œžœžœ.žœž œ˜cJšœžœžœ˜+Jšœ˜˜šžœ˜Jšžœ(˜,Jšžœ(˜,——Jšœžœ˜ š žœžœžœ/žœž˜QJšœžœ˜Jšœžœ9˜^Jšžœ˜—Jšœ˜J˜—š  œžœžœžœžœžœž œ˜YJ˜9Jšœ%˜%J˜˜J˜"J˜ Jšœžœ˜$—Jšœžœ˜Jšœžœ(˜EJšœ˜J˜—š  œžœžœ"žœžœž œ˜lJšœžœ˜4Jšœ˜J˜—š   œžœžœ/žœžœž œ˜Jšžœžœžœ˜šœžœ ˜JšžœU˜X—Jš œžœžœžœžœ˜.šžœžœžœž˜5Jšœ˜šžœžœžœ ž˜7Jšžœžœžœ˜(—Jšžœ˜—šœ2˜2Jšœ0™0—Jšœ ˜ Jšœ˜J˜—š  œžœžœžœ˜/Jšžœžœžœ˜Jšœžœ žœ˜šžœžœžœž˜5šžœ ˜ šžœ˜šœ5˜5Jšžœ žœžœ˜;JšœžœŸ˜*Jšž˜Jšœ˜——Jšžœ ˜—JšžœžœžœŸ=˜UJšžœ˜—Jšœ˜J˜—š  œžœžœ˜*Jšœ0™0Jšžœžœžœ˜Jšœžœ žœ˜šžœžœžœž˜5šžœ˜šžœ˜Jšžœ žœžœ˜;JšœžœŸ˜*Jšœ˜—Jšžœ ˜—JšžœžœžœŸ=˜UJšžœ˜—Jšœ˜J˜—š   œžœžœžœžœžœ˜eJšžœžœžœ˜šžœžœ žœžœžœžœžœž˜YJš žœ žœžœ žœžœžœ˜PJšžœ˜—Jšžœžœžœ˜Jšœ˜J˜—š  œžœ-žœž œ˜`Jšœ@™@J˜J˜"Jšœ˜J˜ š  œžœžœžœ ˜+Jšžœžœžœ˜š žœžœžœžœž˜5š žœžœžœžœ žœ˜JJšœ˜Jšžœ˜ Jšœ˜—Jšžœ˜—JšœΞ™ΞJ˜Jšžœžœ˜ Jšœ˜—˜Jšœžœ*˜LJšœžœ˜0—˜Jšœžœ/˜QJšœžœ˜7—Jšœžœ9˜HJšœžœ˜Jšžœ˜Jšœ˜J˜—Jšœ9™9J˜š  œžœžœžœž œ žœ˜?Jšœ*˜*Jšœžœ ˜+Jšœ,˜,J˜7Jšœ˜J˜Jšœ˜Jšœ žœ˜Jšœ˜Jšœžœžœ˜"šžœž˜Jšœ"žœ˜'J˜Jšžœžœ˜"—Jšœ žœ!˜6Jšœžœ˜6J˜&šžœ!žœžœ˜2Jšžœžœ;˜AJšžœ!žœ!˜GJšžœžœ˜—šœ žœ˜+Jšžœžœ ˜(Jšžœ!˜%—šžœ)˜+Jšžœžœ˜(šžœŸ˜!Jšžœžœžœžœ˜%šžœžœž˜<šžœ%˜'Jšžœ*žœ˜4—JšžœžœžŸ˜7Jšžœ˜—J˜Jšœ žœžœ˜)Jšœ˜—šžœŸ˜"J˜Jšœ˜——šžœž˜Jšœ ™ JšœL˜LJ˜—šžœžœ˜šœ*™*Jšœžœ žœ ž œ™9—Jšžœžœžœžœ˜)JšœŸ ˜*JšœŸ ˜'J˜Jš œžœžœžœžœ˜^Jšœ˜—J˜šžœ˜Jšžœžœžœžœ˜+Jš žœžœžœžœžŸ"œ˜W—J˜%šžœ˜!šžœ˜Jšžœžœžœ˜-Jšœ ž˜ Jšœ˜—šžœ˜J˜ J˜2šžœ$˜&šžœ˜Jšœ™Jšœžœ˜Jšžœžœžœ˜-Jšœžœ.˜PJ˜&Jš žœžœžœžœžŸ œ˜LJšœ˜—šžœ˜Jšœ™Jšžœžœžœ˜3šžœ˜šžœ˜JšœE˜E—šžœ˜Jšœžœ'˜FJ˜FJ˜4——Jšœ˜——Jšœ˜——Jšœ˜J˜—š  œžœžœžœžœ˜1Jšœ˜šžœž˜$Jšœ.žœ˜8—Jšœ˜J˜—š  œžœžœžœžœ˜3Jšœ˜šžœž˜$Jšœ0žœ˜:—Jšœ˜J˜—š œžœžœžœžœ0žœžœžœžœ˜…Jšžœžœžœ˜Jšœžœ4˜QJ˜J˜—š œžœžœžœžœ0žœžœžœžœ˜‡Jšžœžœžœ˜Jšœžœ˜#šžœBžœž˜Xšžœžœ žœžœžœžœžœ$˜„Jš žœžœžœžœ$žœ˜WJšžœ ˜—Jšžœ˜—J˜J˜—š œžœžœ˜:šžœVžœž˜kJšœ5˜5Jšžœ˜—J˜J˜—š  œžœžœ+ž œ žŸ œ˜…Kšžœžœžœ˜šžœ4žœž˜JKš žœžœžœžœžœ˜HKšžœ˜—K˜K˜—Jšœ˜J˜—…—jΠͺ