###############################################################################
#
# object15.eagle --
#
# Extensible Adaptable Generalized Logic Engine (Eagle)
#
# Copyright (c) 2007-2012 by Joe Mistachkin.  All rights reserved.
#
# See the file "license.terms" for information on usage and redistribution of
# this file, and for a DISCLAIMER OF ALL WARRANTIES.
#
# RCS: @(#) $Id: $
#
###############################################################################

source [file join [file normalize [file dirname [info script]]] prologue.eagle]

###############################################################################

runTest {test object-15.1 {type to baseType implicit conversion} -body {
  set assembly [object load System.Xml]

  object import System.IO
  object import System.Xml

  list [set strReader [object create -alias StringReader "this is a test."]] \
      [set xmlReader [object invoke -alias XmlReader Create $strReader]]
} -cleanup {
  object unimport -importpattern System.IO
  object unimport -importpattern System.Xml

  unset -nocomplain strReader xmlReader assembly
} -constraints {eagle} -match regexp -result {^System#IO#StringReader#\d+\
System#Xml#XmlTextReader(?:Impl)?#\d+$}}

###############################################################################

runTest {test object-15.2 {object invoke -identity} -setup {
  unset -nocomplain name
  set name(0) [object create -alias Object]
} -body {
  set name(1) [$name(0) -identity -alias ""]
  list $name(0) $name(1) [expr {$name(0) eq $name(1)}]
} -cleanup {
  unset -nocomplain name
} -constraints {eagle} -match regexp -result \
{^System#Object#\d+ System#Object#\d+ True$}}

###############################################################################

runTest {test object-15.3 {unique ObjectId attribute values} -setup {
  unset -nocomplain entry id type ids result
} -body {
  set result true; # TEST PASS

  foreach entry [info engine objectids] {
    #
    # NOTE: Grab the ObjectId ane Type name from this entry.
    #
    set id [lindex $entry 0]
    set type [lindex $entry 1]

    #
    # NOTE: If the value is an empty string, that is a problem.
    #
    if {[string length $id] == 0} then {
      tputs $test_channel [appendArgs \
          "---- found invalid object Id \"" $entry \"\n]

      set result false; continue; # TEST FAIL
    }

    #
    # NOTE: If the value is an empty GUID, that is a problem.
    #
    if {$id eq "00000000-0000-0000-0000-000000000000"} then {
      tputs $test_channel [appendArgs \
          "---- found empty object Id \"" $entry \"\n]

      set result false; continue; # TEST FAIL
    }

    #
    # NOTE: If we have already seen this value, that is a problem.
    #
    if {[info exists ids($id)]} then {
      lappend ids($id) $type
    } else {
      set ids($id) [list $type]; continue
    }

    tputs $test_channel [appendArgs \
        "---- found duplicate object Id \"" $id "\" in types \{" \
        $ids($id) "\} (" [llength $ids($id)] " times)\n"]

    set result false; # TEST FAIL
  }

  set result
} -cleanup {
  unset -nocomplain entry id type ids result
} -constraints {eagle} -result {true}}

###############################################################################

runTest {test object-15.4 {Interpreter/IInterpreter interface} -setup {
  unset -nocomplain interface interfaces member members found result
} -body {
  set result true; # TEST PASS

  #
  # NOTE: This is the list of all classes and interfaces that we know
  #       are used by the Interpreter class.
  #
  set interfaces [list ScriptMarshalByRefObject \
                       System.Object \
                       System.IDisposable \
                       Eagle._Interfaces.Public.ICallbackQueueManager \
                       Eagle._Interfaces.Public.ICacheManager \
                       Eagle._Interfaces.Public.IDebugManager \
                       Eagle._Interfaces.Public.IEngineManager \
                       Eagle._Interfaces.Public.IEntityManager \
                       Eagle._Interfaces.Public.IErrorManager \
                       Eagle._Interfaces.Public.IHistoryManager \
                       Eagle._Interfaces.Public.IInteractiveManager \
                       Eagle._Interfaces.Public.IInterpreter \
                       Eagle._Interfaces.Public.IInterpreterManager \
                       Eagle._Interfaces.Public.INotifyManager \
                       Eagle._Interfaces.Public.IPackageManager \
                       Eagle._Interfaces.Public.IPluginManager \
                       Eagle._Interfaces.Public.IPropertyManager \
                       Eagle._Interfaces.Public.IResolveManager \
                       Eagle._Interfaces.Public.IResourceManager \
                       Eagle._Interfaces.Public.IScriptManager \
                       Eagle._Interfaces.Public.ISecurityManager \
                       Eagle._Interfaces.Public.IShellManager \
                       Eagle._Interfaces.Public.ISynchronize \
                       Eagle._Interfaces.Public.ITclEntityManager \
                       Eagle._Interfaces.Public.ITclManager \
                       Eagle._Interfaces.Public.IVariableManager]

  #
  # NOTE: Grab the public non-static members from all the IInterpreter
  #       interface and all the interfaces it derives from.  This will
  #       also grab the public non-static members for the classes the
  #       Interpreter class derives from.
  #
  foreach interface $interfaces {
    #
    # NOTE: This may throw an error because we are potentially querying
    #       for interfaces that do not exist in the current binary (i.e.
    #       some optional features have been disabled at compile-time);
    #       therefore, initialize the array element with an empty list
    #       so we can still easily refer to it in the loop below.
    #
    set members($interface) [list]

    catch {
      eval lappend members($interface) \
          [object members -signatures -flags -Static $interface]
    }
  }

  set members(Interpreter) \
      [object members -signatures -flags -Static Interpreter]

  foreach member $members(Interpreter) {
    #
    # NOTE: Make sure this member is also defined on the IInterpreter
    #       interface (or one of the interfaces it derives from).
    #
    set found false

    foreach interface $interfaces {
      if {[lsearch -exact $members($interface) $member] != -1} then {
        set found true; break
      }
    }

    if {!$found} then {
      tputs $test_channel [appendArgs \
          "---- member \"" $member "\" not present in formal interface\n"]

      set result false; # TEST FAIL
    }
  }

  set result
} -cleanup {
  unset -nocomplain interface interfaces member members found result
} -constraints {eagle} -result {true}}

###############################################################################

runTest {test object-15.5 {Interpreter static members} -body {
  lsort [object members -flags -Instance Interpreter]
} -constraints {eagle} -result \
{{memberType Field memberName DefaultEncoding}\
{memberType Field memberName SystemEncoding}\
{memberType Method memberName Create}\
{memberType Method memberName Equals}\
{memberType Method memberName GetActive}\
{memberType Method memberName GetAny}\
{memberType Method memberName GetEncoding}\
{memberType Method memberName GetStartupCreateFlags}\
{memberType Method memberName GetStartupLibraryPath}\
{memberType Method memberName InteractiveLoop}\
{memberType Method memberName InteractiveLoopThread}\
{memberType Method memberName IsPendingCleanup}\
{memberType Method memberName IsPendingDispose}\
{memberType Method memberName IsPendingWait}\
{memberType Method memberName ProcessStartupOptions}\
{memberType Method memberName Ready}\
{memberType Method memberName ReferenceEquals}\
{memberType Method memberName ShellMain}\
{memberType Method memberName ShellMainCore}\
{memberType Method memberName ShellThreadMain}\
{memberType Method memberName get_NewHostCallback}\
{memberType Method memberName set_NewHostCallback}\
{memberType Property memberName NewHostCallback}}}

###############################################################################

runTest {test object-15.6 {Interpreter static member signatures} -body {
  lsort [string map \
      [list Eagle._Components.Public.Delegates EComPD \
            Eagle._Components.Public EComP \
            Eagle._Containers.Public EConP \
            Eagle._Interfaces.Public EIP] \
      [object members -signatures -flags -Instance Interpreter]]
} -constraints {eagle} -result \
{{memberType Field memberName DefaultEncoding fieldType System.Text.Encoding}\
{memberType Field memberName SystemEncoding fieldType System.Text.Encoding}\
{memberType Method memberName Create methodType Method methodName Create\
callingConvention Standard returnType EComP.Interpreter parameterTypes\
EComP.Result&} {memberType Method memberName Create methodType Method\
methodName Create callingConvention Standard returnType EComP.Interpreter\
parameterTypes {EComP.InterpreterSettings System.Boolean EComP.Result&}}\
{memberType Method memberName Create methodType Method methodName Create\
callingConvention Standard returnType EComP.Interpreter parameterTypes\
{{System.Collections.Generic.IEnumerable`1[System.String]} EComP.CreateFlags\
EComP.Result&}} {memberType Method memberName\
Create methodType Method methodName Create callingConvention Standard\
returnType EComP.Interpreter parameterTypes\
{{System.Collections.Generic.IEnumerable`1[System.String]} EComP.CreateFlags\
EConP.TraceList EComP.Result&}} {memberType Method memberName Create methodType\
Method methodName Create callingConvention Standard returnType\
EComP.Interpreter parameterTypes\
{{System.Collections.Generic.IEnumerable`1[System.String]} EComP.CreateFlags\
EIP.IHost EComP.Result&}} {memberType Method memberName Create methodType\
Method methodName Create callingConvention Standard returnType\
EComP.Interpreter parameterTypes\
{{System.Collections.Generic.IEnumerable`1[System.String]} EComP.CreateFlags\
System.Object System.Object System.Object System.Object\
EConP.ExecuteCallbackDictionary EConP.TraceList System.String System.String\
EConP.StringList EComP.Result&}} {memberType Method memberName Create\
methodType Method methodName Create callingConvention Standard returnType\
EComP.Interpreter parameterTypes\
{{System.Collections.Generic.IEnumerable`1[System.String]} EComP.CreateFlags\
System.String EComP.Result&}} {memberType Method memberName Create\
methodType Method methodName Create callingConvention Standard returnType\
EComP.Interpreter parameterTypes\
{{System.Collections.Generic.IEnumerable`1[System.String]} EComP.CreateFlags\
System.String System.String EConP.StringList EComP.Result&}} {memberType Method\
memberName Create methodType Method methodName Create callingConvention\
Standard returnType EComP.Interpreter parameterTypes\
{{System.Collections.Generic.IEnumerable`1[System.String]} EComP.Result&}}\
{memberType Method memberName Create methodType Method methodName Create\
callingConvention Standard returnType EComP.Interpreter parameterTypes\
{{System.Collections.Generic.IEnumerable`1[System.String]} System.String\
EComP.CreateFlags EComP.ScriptFlags System.AppDomain EIP.IHost System.String\
System.Object System.Object System.Object System.Object\
EConP.ExecuteCallbackDictionary EConP.TraceList System.String System.String\
EConP.StringList EComP.Result&}} {memberType Method memberName Equals\
methodType Method methodName Equals callingConvention Standard returnType\
System.Boolean parameterTypes {System.Object System.Object}} {memberType\
Method memberName GetActive methodType Method methodName GetActive\
callingConvention Standard returnType EComP.Interpreter parameterTypes {}}\
{memberType Method memberName GetAny methodType Method methodName GetAny\
callingConvention Standard returnType EComP.Interpreter parameterTypes {}}\
{memberType Method memberName GetEncoding methodType Method methodName\
GetEncoding callingConvention Standard returnType EComP.ReturnCode\
parameterTypes {EComP.Interpreter System.String System.Text.Encoding&\
EComP.Result&}} {memberType Method memberName GetStartupCreateFlags methodType\
Method methodName GetStartupCreateFlags callingConvention Standard returnType\
EComP.CreateFlags parameterTypes\
{{System.Collections.Generic.IEnumerable`1[System.String]} EComP.CreateFlags\
EComP.OptionOriginFlags System.Boolean System.Boolean}} {memberType Method\
memberName GetStartupLibraryPath methodType Method methodName\
GetStartupLibraryPath callingConvention Standard returnType EComP.ReturnCode\
parameterTypes {{System.Collections.Generic.IEnumerable`1[System.String]}\
EComP.CreateFlags EComP.OptionOriginFlags System.Boolean System.Boolean\
System.String& EComP.Result&}} {memberType Method\
memberName InteractiveLoop methodType Method methodName InteractiveLoop\
callingConvention Standard returnType EComP.ReturnCode parameterTypes\
{EComP.Interpreter System.Boolean\
{System.Collections.Generic.IEnumerable`1[System.String]} EComP.ReturnCode\
EComP.BreakpointType System.String EIP.IToken EIP.ITraceInfo EComP.EngineFlags\
EComP.SubstitutionFlags EComP.EventFlags EComP.ExpressionFlags\
EComP.HeaderFlags EIP.IClientData EConP.ArgumentList System.Boolean&\
EComP.Result&}} {memberType Method memberName InteractiveLoop methodType Method\
methodName InteractiveLoop callingConvention Standard returnType\
EComP.ReturnCode parameterTypes {EComP.Interpreter\
{System.Collections.Generic.IEnumerable`1[System.String]} EComP.Result&}}\
{memberType Method memberName InteractiveLoop methodType Method methodName\
InteractiveLoop callingConvention Standard returnType EComP.ReturnCode\
parameterTypes {EComP.Interpreter\
{System.Collections.Generic.IEnumerable`1[System.String]} System.Boolean&\
EComP.Result&}} {memberType Method memberName InteractiveLoopThread methodType\
Method methodName InteractiveLoopThread callingConvention Standard returnType\
System.Threading.Thread parameterTypes {EComP.Interpreter\
{System.Collections.Generic.IEnumerable`1[System.String]} System.Boolean\
EComP.Result&}} {memberType Method memberName IsPendingCleanup methodType\
Method methodName IsPendingCleanup callingConvention Standard returnType\
System.Boolean parameterTypes EComP.Interpreter} {memberType Method memberName\
IsPendingDispose methodType Method methodName IsPendingDispose\
callingConvention Standard returnType System.Boolean parameterTypes\
EComP.Interpreter} {memberType Method memberName IsPendingWait methodType\
Method methodName IsPendingWait callingConvention Standard returnType\
System.Boolean parameterTypes EComP.Interpreter} {memberType Method memberName\
ProcessStartupOptions methodType Method methodName ProcessStartupOptions\
callingConvention Standard returnType EComP.ReturnCode parameterTypes\
{EComP.Interpreter {System.Collections.Generic.IEnumerable`1[System.String]}\
EComP.CreateFlags EComP.OptionOriginFlags System.Boolean System.Boolean\
EComP.Result&}} {memberType Method memberName ProcessStartupOptions methodType\
Method methodName ProcessStartupOptions callingConvention Standard returnType\
EComP.ReturnCode parameterTypes {EComP.Interpreter\
{System.Collections.Generic.IEnumerable`1[System.String]} EComP.CreateFlags\
EComP.OptionOriginFlags System.Boolean System.Boolean System.Boolean&\
EComP.Result&}} {memberType Method memberName ProcessStartupOptions methodType\
Method methodName ProcessStartupOptions callingConvention Standard returnType\
EComP.ReturnCode parameterTypes {EComP.Interpreter\
{System.Collections.Generic.IEnumerable`1[System.String]} EComP.CreateFlags\
EComP.OptionOriginFlags System.Boolean System.Boolean System.Boolean&\
System.Boolean& EComP.Result&}} {memberType Method memberName Ready methodType\
Method methodName Ready callingConvention Standard returnType EComP.ReturnCode\
parameterTypes {EComP.Interpreter EComP.Result&}} {memberType Method memberName\
ReferenceEquals methodType Method methodName ReferenceEquals callingConvention\
Standard returnType System.Boolean parameterTypes {System.Object\
System.Object}} {memberType Method memberName ShellMain methodType Method\
methodName ShellMain callingConvention Standard returnType EComP.ExitCode\
parameterTypes {{System.Collections.Generic.IEnumerable`1[System.String]}}}\
{memberType Method memberName ShellMainCore methodType Method methodName\
ShellMainCore callingConvention Standard returnType EComP.ExitCode\
parameterTypes {EComP.Interpreter EComPD.ArgumentCallback\
EComPD.EvaluateScriptCallback EComPD.EvaluateFileCallback\
EComPD.EvaluateEncodedFileCallback EComPD.InteractiveLoopCallback\
EIP.IClientData {System.Collections.Generic.IEnumerable`1[System.String]}\
System.Boolean System.Boolean EComP.Result&}} {memberType Method memberName\
ShellThreadMain methodType Method methodName ShellThreadMain callingConvention\
Standard returnType System.Threading.Thread parameterTypes\
{{System.Collections.Generic.IEnumerable`1[System.String]} System.Boolean}}\
{memberType Method memberName get_NewHostCallback methodType Method methodName\
get_NewHostCallback callingConvention Standard returnType\
EComPD.NewHostCallback parameterTypes {}} {memberType Method memberName\
set_NewHostCallback methodType Method methodName set_NewHostCallback\
callingConvention Standard returnType System.Void parameterTypes\
EComPD.NewHostCallback} {memberType Property memberName NewHostCallback\
methodType Method methodName get_NewHostCallback callingConvention Standard\
returnType EComPD.NewHostCallback parameterTypes {} methodType Method\
methodName set_NewHostCallback callingConvention Standard returnType\
System.Void parameterTypes EComPD.NewHostCallback}}}

###############################################################################

runTest {test object-15.7 {object invoke -typeidentity} -setup {
  unset -nocomplain name
  set name(0) [object create -alias Object]
} -body {
  set name(1) [$name(0) -create -alias -typeidentity ""]; $name(1) ToString
} -cleanup {
  unset -nocomplain name
} -constraints {eagle} -result {System.Object}}

###############################################################################

runTest {test object-15.8 {type string conversion} -setup {
  unset -nocomplain type
} -body {
  set type(0) [object invoke -create -flags +NonPublic -alias \
      Eagle._Components.Private.MarshalOps TypeIdentity Object]

  set type(1) [object invoke -create -flags +NonPublic -alias \
      Eagle._Components.Private.MarshalOps TypeIdentity Interpreter]

  set type(2) [object invoke -create -flags +NonPublic -alias \
      Eagle._Components.Private.MarshalOps TypeIdentity ArgumentList]

  set type(3) [object invoke -create -flags +NonPublic -alias \
      Eagle._Components.Private.MarshalOps TypeIdentity \
      Eagle._Interfaces.Public.IClientData]

  list [$type(0) ToString] [$type(1) ToString] [$type(2) ToString] \
      [$type(3) ToString]
} -cleanup {
  unset -nocomplain type
} -constraints {eagle} -result {System.Object\
Eagle._Components.Public.Interpreter Eagle._Containers.Public.ArgumentList\
Eagle._Interfaces.Public.IClientData}}

###############################################################################

runTest {test object-15.9 {prevent automatic StringList conversion} -setup {
  unset -nocomplain x y z code result result2 result3
} -body {
  set x "this is a test"
  set y "that \{ a test"
  set z [object create StringList a b c d]

  set code Ok
  set result null

  list [catch {object invoke -flags +NonPublic \
      Eagle._Components.Private.InteractiveOps+Commands args $x code result} \
      result2] $code $result $result2 [catch {object invoke -flags +NonPublic \
      Eagle._Components.Private.InteractiveOps+Commands args $y code result} \
      result2] $code $result $result2 [catch {object invoke -flags +NonPublic \
      Eagle._Components.Private.InteractiveOps+Commands args $z code result} \
      result2] $code $result $result2 [catch {object invoke $result ToString} \
      result3] $result3
} -cleanup {
  unset -nocomplain x y z code result result2 result3
} -constraints {eagle interactiveCommand.args} -match regexp -result \
{^1 Ok null \{could not convert method #\d+\
"Eagle\._Components\.Private\.InteractiveOps\+Commands\.args" argument #0\
"args" with value "this is a test" to type\
"System\.Collections\.Generic\.IEnumerable`1\[\[System\.String, mscorlib,\
Version=\d+\.0\.0\.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\]\]":\
cannot convert from type "System\.String" to type\
"System\.Collections\.Generic\.IEnumerable`1\[\[System\.String, mscorlib,\
Version=\d+\.0\.0\.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\]\]"\} 1\
Ok null could\\ not\\ convert\\ method\\ #\d+\\\
\\"Eagle\._Components\.Private\.InteractiveOps\+Commands\.args\\"\\ argument\\\
#0\\ \\"args\\"\\ with\\ value\\ \\"that\\ \\\{\\ a\\ test\\"\\ to\\ type\\\
\\"System\.Collections\.Generic\.IEnumerable`1\\\[\\\[System\.String,\\\
mscorlib,\\ Version=\d+\.0\.0\.0,\\ Culture=neutral,\\\
PublicKeyToken=b77a5c561934e089\\\]\\\]\\":\\ cannot\\ convert\\ from\\ type\\\
\\"System\.String\\"\\ to\\ type\\\
\\"System\.Collections\.Generic\.IEnumerable`1\\\[\\\[System\.String,\\\
mscorlib,\\ Version=\d+\.0\.0\.0,\\ Culture=neutral,\\\
PublicKeyToken=b77a5c561934e089\\\]\\\]\\" 0 Ok Result#\d+ \{\} 0 \{a b c d\}$}}

###############################################################################

runTest {test object-15.10 {object invokeraw w/late binding} -body {
  set type [object invoke -create \
      Type GetTypeFromProgID Microsoft.Update.AutoUpdate]

  set autoUpdate [object invoke -objectflags +NoComObjectReturn \
      Activator CreateInstance $type]

  set settings [object invokeraw \
      -bindingflags {-InvokeMethod +GetProperty} \
      -objectflags +NoComObject $autoUpdate Settings]

  lappend result [object invokeraw -bindingflags \
      {-InvokeMethod +GetProperty} -objectflags +NoComObjectLookup \
      $settings NotificationLevel]

  lappend result [object invokeraw -bindingflags \
      {-InvokeMethod +GetProperty} -objectflags +NoComObjectLookup \
      $autoUpdate ServiceEnabled]

  set result
} -cleanup {
  unset -nocomplain result settings autoUpdate type
} -constraints {eagle monoToDo} -match regexp -result {^\d+ (?:False|True)$}}

###############################################################################

runTest {test object-15.11 {no internal members on private classes} -setup {
  if {[info exists env(NeverWaitForGC)]} then {
    set savedEnv(NeverWaitForGC) $env(NeverWaitForGC)
  } else {
    unset -nocomplain savedEnv(NeverWaitForGC)
  }

  set env(NeverWaitForGC) 1; # BUGBUG: This speeds things up a lot.

  set assembly [object invoke -alias -flags +NonPublic \
      Eagle._Components.Private.GlobalState GetAssembly]
} -body {
  array set count {
    typesSeen          0 typesChecked       0 typesFailed        0
    methodsSeen        0 methodsChecked     0 methodsFailed      0
  }

  set result [list]
  set types [$assembly -create GetTypes]

  object foreach -alias type $types {
    #
    # NOTE: We have seen another type; however, it may be skipped.
    #
    incr count(typesSeen)

    #
    # NOTE: Limit the search to non-public classes only.
    #
    if {![$type IsClass] || ![$type IsNotPublic]} then {
      continue
    }

    #
    # NOTE: Query for all the non-public methods on this type.
    #
    set methods [$type GetMethods {Instance Static NonPublic}]
    set oldMethodsFailed $count(methodsFailed)

    object foreach -alias method $methods {
      #
      # NOTE: We have seen another method; however, it may be skipped.
      #
      incr count(methodsSeen)

      #
      # NOTE: Make sure that this method was actually declared on this
      #       type.
      #
      if {[$method DeclaringType.FullName] ne [$type FullName]} then {
        continue
      }

      #
      # NOTE: Another method is going to be checked against the list of
      #       "forbidden" attribute values.
      #
      incr count(methodsChecked)

      #
      # NOTE: Grab the attributes for this method.  They are basically
      #       just a flags enumeration value.  Then, convert them into
      #       a proper Tcl list using a transformation.
      #
      set attributes [apply {{list} {
        lmap element $list {
          expr {[string length $element] > 0 ? $element : [continue]}
        }
      }} [split [$method Attributes] ", "]]

      #
      # NOTE: If this is an "internal" method on a non-public class,
      #       flag it, thus failing the test.
      #
      if {[lsearch -exact -- $attributes Assembly] != -1} then {
        #
        # NOTE: Another method has failed the "internal" check.
        #
        incr count(methodsFailed)

        #
        # NOTE: Keep track of the full type and method names for later
        #       logging.
        #
        lappend result [list [$type FullName] [$method Name]]
      }
    }

    #
    # NOTE: Did any methods of this type fail the check?
    #
    if {$count(methodsFailed) > $oldMethodsFailed} then {
      incr count(typesFailed)
    }
  }

  tputs $test_channel [appendArgs \
      "---- test counts: " [array get count] \n]

  set result
} -cleanup {
  unset -nocomplain oldMethodsFailed count result \
      attributes method methods type types assembly

  if {[info exists savedEnv(NeverWaitForGC)]} then {
    set env(NeverWaitForGC) $savedEnv(NeverWaitForGC)
    unset -nocomplain savedEnv(NeverWaitForGC)
  } else {
    unset -nocomplain env(NeverWaitForGC)
  }
} -constraints {eagle} -time true -result {}}

###############################################################################

source [file join [file normalize [file dirname [info script]]] epilogue.eagle]
