Determining the registered DGM classes at runtime

classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|

Determining the registered DGM classes at runtime

Milles, Eric (TR Technology & Ops)

Is it possible to determine the available Category (aka DGM) extension classes at runtime?  I have been relying on DefaultGroovyMethods.DGM_LIKE_CLASSES, but this has been getting reduced with each release due to modularization.  And it was never really a good source to begin with.  I tried looking through ExtensionModuleRegstry, but I couldn't make out a good entry point for asking for the DGM classes.  I see STC using this stuff to ask if a given method name is a DGM.  But this does not meet the needs of content assist, which must suggest all method names for a given prefix.

Reply | Threaded
Open this post in threaded view
|

Re: Determining the registered DGM classes at runtime

Jochen Theodorou
On 06.04.2018 18:42, [hidden email] wrote:
> Is it possible to determine the available Category (aka DGM) extension
> classes at runtime?  I have been relying on
> DefaultGroovyMethods.DGM_LIKE_CLASSES, but this has been getting reduced
> with each release due to modularization.  And it was never really a good
> source to begin with.  I tried looking through ExtensionModuleRegstry,
> but I couldn't make out a good entry point for asking for the DGM
> classes.

you will have to build an index for this I think. You can ask for a list
of modules and also for the available methods in each. Or you simulate
the discovery mechanism and play it from there without instantiating any
of the Modules

bye Jochen

Reply | Threaded
Open this post in threaded view
|

RE: Determining the registered DGM classes at runtime

Milles, Eric (TR Technology & Ops)
What API can I call to get a list of modules?  I'm having trouble determining the actual entry point.

-----Original Message-----
From: Jochen Theodorou [mailto:[hidden email]]
Sent: Friday, April 06, 2018 1:36 PM
To: [hidden email]
Subject: Re: Determining the registered DGM classes at runtime

On 06.04.2018 18:42, [hidden email] wrote:
> Is it possible to determine the available Category (aka DGM) extension
> classes at runtime?  I have been relying on
> DefaultGroovyMethods.DGM_LIKE_CLASSES, but this has been getting
> reduced with each release due to modularization.  And it was never
> really a good source to begin with.  I tried looking through
> ExtensionModuleRegstry, but I couldn't make out a good entry point for
> asking for the DGM classes.

you will have to build an index for this I think. You can ask for a list of modules and also for the available methods in each. Or you simulate the discovery mechanism and play it from there without instantiating any of the Modules

bye Jochen

Reply | Threaded
Open this post in threaded view
|

Re: Determining the registered DGM classes at runtime

Jochen Theodorou
ah sorry, I was not explaining right...

I was going to suggest something similar to what you probably already
have found, which is
StaticTypeCheckingSupport.EXTENSION_METHOD_CACHE.getExtensionMethods(ClassLoader)
but for some reason I do not understand the constant is protected and
the type for it is a private static inner class... which means nobody
can use this really. bummer. So maybe patch that and make it public? See
GROOVY-8536 for this

In theory you could go through MetaClassImpl, but I think that will a
lot more than you want.

bye Jochen

On 06.04.2018 21:16, [hidden email] wrote:

> What API can I call to get a list of modules?  I'm having trouble determining the actual entry point.
>
> -----Original Message-----
> From: Jochen Theodorou [mailto:[hidden email]]
> Sent: Friday, April 06, 2018 1:36 PM
> To: [hidden email]
> Subject: Re: Determining the registered DGM classes at runtime
>
> On 06.04.2018 18:42, [hidden email] wrote:
>> Is it possible to determine the available Category (aka DGM) extension
>> classes at runtime?  I have been relying on
>> DefaultGroovyMethods.DGM_LIKE_CLASSES, but this has been getting
>> reduced with each release due to modularization.  And it was never
>> really a good source to begin with.  I tried looking through
>> ExtensionModuleRegstry, but I couldn't make out a good entry point for
>> asking for the DGM classes.
>
> you will have to build an index for this I think. You can ask for a list of modules and also for the available methods in each. Or you simulate the discovery mechanism and play it from there without instantiating any of the Modules
>
> bye Jochen
>

Reply | Threaded
Open this post in threaded view
|

Re: Determining the registered DGM classes at runtime

Cédric Champeau


2018-04-06 22:42 GMT+02:00 Jochen Theodorou <[hidden email]>:
ah sorry, I was not explaining right...

I was going to suggest something similar to what you probably already have found, which is StaticTypeCheckingSupport.EXTENSION_METHOD_CACHE.getExtensionMethods(ClassLoader)
but for some reason I do not understand the constant is protected and the type for it is a private static inner class... which means nobody can use this really. bummer. So maybe patch that and make it public? See GROOVY-8536 for this


It is private because it was never meant for external consumption. It's much easier to evolve an API/internals if things are kept private. Look at the nightmare of people using internal APIs in Gradle and you'll understand what I mean: the more the API is open, the bigger the surface is, and the higher the risk to break consumers when you want to change an implementation detail is.

So we _could_ make this API public, but then it means adding test coverage and making sure we stabilize the API, if it ever makes sense.

Reply | Threaded
Open this post in threaded view
|

Re: Determining the registered DGM classes at runtime

Jochen Theodorou
On 08.04.2018 18:51, Cédric Champeau wrote:

> 2018-04-06 22:42 GMT+02:00 Jochen Theodorou <[hidden email]
> <mailto:[hidden email]>>:
>
>     ah sorry, I was not explaining right...
>
>     I was going to suggest something similar to what you probably
>     already have found, which is
>     StaticTypeCheckingSupport.EXTENSION_METHOD_CACHE.getExtensionMethods(ClassLoader)
>     but for some reason I do not understand the constant is protected
>     and the type for it is a private static inner class... which means
>     nobody can use this really. bummer. So maybe patch that and make it
>     public? See GROOVY-8536 for this
>
>
> It is private because it was never meant for external consumption. It's
> much easier to evolve an API/internals if things are kept private.

sure, but then why make EXTENSION_METHOD_CACHE protected? This is
contradicting

> Look
> at the nightmare of people using internal APIs in Gradle and you'll
> understand what I mean: the more the API is open, the bigger the surface
> is, and the higher the risk to break consumers when you want to change
> an implementation detail is.
>
> So we _could_ make this API public, but then it means adding test
> coverage and making sure we stabilize the API, if it ever makes sense.

for IDE plugins having an infrastructure like there absolutely makes
sense. I can also see some joint compilation usages for this

bye Jochen


Reply | Threaded
Open this post in threaded view
|

RE: Determining the registered DGM classes at runtime

Milles, Eric (TR Technology & Ops)
Thanks for the tip.  Looking through StaticTypeCheckingSupport.EXTENSION_METHOD_CACHE.getExtensionMethods(ClassLoader) I was able to see that ExtensionModuleScanner is what I was looking for.  Here is my end result for discovering the configured DGM classes:


    public static class XYGroovyClassLoader extends GroovyClassLoader {

        public XYGroovyClassLoader(ClassLoader parent, CompilerConfiguration config) {
            super(parent, config);
        }

        ...

        private volatile Set<Class> defaultCategories;
        private volatile Set<Class> defaultStaticCategories;

        public Set<Class> getDefaultCategories() {
            if (defaultCategories == null) {
                synchronized (this) {
                    if (defaultCategories == null) {
                        defaultCategories = new LinkedHashSet<>(); defaultStaticCategories = new LinkedHashSet<>();
                        try {
                            Class dgm = loadClass("org.codehaus.groovy.runtime.DefaultGroovyMethods");
                            Class dgsm = loadClass("org.codehaus.groovy.runtime.DefaultGroovyStaticMethods");

                            Collections.addAll(defaultCategories, (Class[]) dgm.getField("DGM_LIKE_CLASSES").get(dgm));

                            defaultStaticCategories.add(dgsm);

                            new ExtensionModuleScanner(module ->  {
                                if (module instanceof SimpleExtensionModule) {
                                    defaultCategories.addAll(((SimpleExtensionModule) module).getInstanceMethodsExtensionClasses());
                                    defaultStaticCategories.addAll(((SimpleExtensionModule) module).getStaticMethodsExtensionClasses());
                                }
                            }, this).scanClasspathModules();

                            defaultCategories.addAll(defaultStaticCategories);

                        } catch (Exception e) {
                            Util.log(e, "Failed to find Default Groovy Methods with " + this);
                        }
                    }
                }
            }
            return Collections.unmodifiableSet(defaultCategories);
        }

        public boolean isDefaultStaticCategory(String name) {
            if (defaultStaticCategories == null) getDefaultCategories();
            return defaultStaticCategories.stream().map(Class::getName).anyMatch(name::equals);
        }
    }