Class ExecutionEnvironment

    • Field Detail

      • DEFAULT_PROJECT

        private static final IProject DEFAULT_PROJECT
      • CLASS_REDEFINER_THREAD

        public static final java.lang.String CLASS_REDEFINER_THREAD
        See Also:
        Constant Field Values
      • SPECIAL_CLASSES

        private static final java.util.List<java.lang.String> SPECIAL_CLASSES
        "Special" java classes that will be used to locate "special" JARs which should be included in the classpath. This is a replacement to old SPECIAL_FILES variable.
      • _modules

        private java.util.List<IModule> _modules
      • _defaultModule

        private IModule _defaultModule
      • _jreModule

        private IModule _jreModule
      • _rootModule

        private IModule _rootModule
      • _discretePackages

        private java.lang.String[] _discretePackages
      • counter

        private int counter
        Detect whether or not the jdwp agent is alive in this process, if so start a thread that wakes up every N seconds and checks to see if the ReloadClassesIndicator Java class has been redefined by a debugger. If so, it reloads Gosu classes that have changed.

        Why, you ask? Well since Gosu classes are not compiled to disk, the IDE hosting Gosu can't simply send the bytes in a conventional JDI redefineClasses() call. Yet it somehow needs to at least inform Gosu's type system in the target process that Gosu classes have changed. The JVMTI doesn't offer much help; there's no way to field an arbitrary call from the JDWP client, or for the client to send an arbitrary message. Nor is it possible to leverage the JVMTI's ability to handle method invocation etc. because the target thread must be suspended at a breakpoint, which is not necessarily the case during compilation, and certainly isn't the case for a thread dedicated to fielding such a call. What to do?

        We can leverage redefineClasses() after all. The idea is for the IDE compiler to redefine a class (via asm) designated as the "ReloadClassIndicator". This class lives inside Gosu's type system. It has a single method: public static long timestamp() and returns a literal value. If the target process is being debugged (jdwp agent detection), a thread in the target process starts immediately and waits a few seconds before calling the timestamp() method, it does this in a forever loop. If the timestamp value changes, we assume the IDE redefined the class with a new value to indicate classes have changed. In turn we find and reload changed classes. What could be more straight forward?

        An alternative approach would be for the IDE to establish an additional line of communication with the target process e.g., socket, memory, whatever. But that is messy (requires config on user's end) and error prone. One debug socket is plenty.

        Improvements to this strategy include supplying not only an indication that stuff has changed, but also the names of the classes that have changed. This would releive the target process from having to keep track timestamps on all loaded classes. This could be implemented by having the class return an array of names. An even better improvement would be to include not just the names, but also the source of the classes. This would enable the debuger to modify in memory the classes during a remote debugging session.

    • Constructor Detail

      • ExecutionEnvironment

        private ExecutionEnvironment​(IProject project)
    • Method Detail

      • initializeDefaultSingleModule

        public void initializeDefaultSingleModule​(java.util.List<? extends GosuPathEntry> pathEntries,
                                                  java.util.List<IDirectory> backingSourceEntries,
                                                  java.lang.String... discretePackages)
      • uninitializeDefaultSingleModule

        public void uninitializeDefaultSingleModule()
      • initializeMultipleModules

        public void initializeMultipleModules​(java.util.List<? extends IModule> modules)
      • uninitializeMultipleModules

        public void uninitializeMultipleModules()
      • initializeCompiler

        public void initializeCompiler​(GosucModule gosucModule)
      • uninitializeCompiler

        public void uninitializeCompiler()
      • initializeSimpleIde

        public void initializeSimpleIde​(GosucModule gosucModule)
      • uninitializeSimpleIde

        public void uninitializeSimpleIde()
      • checkForDuplicates

        void checkForDuplicates​(java.lang.String moduleName)
      • getDiscretePackages

        public java.lang.String[] getDiscretePackages()
      • setDiscretePackages

        public void setDiscretePackages​(java.lang.String[] discretePackages)
      • getJreModule

        public IModule getJreModule()
        Specified by:
        getJreModule in interface IExecutionEnvironment
        Returns:
        The module responsible for resolving JRE core classes e.g., java.lang.* etc. Note in default single module environment this is the single module, otherwise this is the module create by calling createJreModule(). This method will never return null but it will throw an NPE if the JRE module is null.
      • findRootModule

        public IModule findRootModule()
      • shutdown

        public void shutdown()
      • getCounter

        private int getCounter()
      • startSneakyDebugThread

        private void startSneakyDebugThread()
      • createDefaultClassPath

        public static java.util.List<IDirectory> createDefaultClassPath()
      • removeAllQuotes

        private static java.lang.String removeAllQuotes​(java.lang.String classpath)
      • expand

        private static java.util.List<IDirectory> expand​(java.util.List<java.lang.String> paths)
      • getJarsContainingSpecialClasses

        private static java.util.Set<java.lang.String> getJarsContainingSpecialClasses()
        This method is a hack to resolve "special" system-like classes provided by execution environment. This is the replacement of old addSpecialJars() method