Constructing records through reflection and module restrictions

Brian Goetz brian.goetz at oracle.com
Mon Dec 9 18:45:11 UTC 2024


The `opens` clause of a module descriptor indeed can be qualified, just 
as the exports clause can:

ModuleDirective:
     requires {RequiresModifier} ModuleName ;
     exports PackageName [to ModuleName {, ModuleName}] ;
     opens PackageName [to ModuleName {, ModuleName}] ;
     uses TypeName ;
     provides TypeName with TypeName {, TypeName} ;


On 12/9/2024 1:22 PM, Olexandr Rotan wrote:
>
> Little of topic here, but what I was lacking with opening is opens * 
> to X (similar to exports * to X). For example, I want my module to be 
> open to code that I trust like spring framework, but not accessible to 
> some 3rd party API wrappers. Something like this would be really 
> helpful in situations when working with sensitive user data, because 
> otherwise you either have to give up access control using open module 
> or open packages one by one to each module which is virtually 
> unmaintainable.
> Putting everything "open" is not really an option since like 95% of 
> code has to be open to reflection if working with IoC containers and ORM`s
>
>
> On Mon, Dec 9, 2024, 19:07 Brian Goetz <brian.goetz at oracle.com> wrote:
>
>     There is a better approach than passing around the Lookup (which
>     requires everyone to coordinate around the Lookup capability): use
>     open modules.  THe concept of "open module" was designed for
>     exactly this purpose: to allow packages to be "exported for
>     reflection" so that frameworks can reflect over them more
>     liberally than bytecode linkage would permit.  If you put your
>     domain objects in a package that is opened (either to the
>     framework, or to everyone), no need to play games with Lookup.
>
>
>     On 12/8/2024 11:52 AM, Florian Weimer wrote:
>>     * Remi Forax:
>>
>>>>     Syntax-wise, it is very tempting to use this with very localized
>>>>     record types for various kinds of custom deserialization scenarios.
>>>>     For example, to read a CSV file with two colums, something like this
>>>>     could be used:
>>>>
>>>>                 record Row(String name, int index) {}
>>>>                 var csv = CSVReader.newReader(r, Row.class);
>>>>                 while (true) {
>>>>                     var row = csv.readRow();
>>>>                     if (row == null)
>>>>                         break;
>>>>                     // Use the row variable.
>>>>                     ...
>>>>                 }
>>>>
>>>>     But it would equally work for SQL result set rows, binary structures,
>>>>     and so on.
>>>     Why not sending the lookup object here :
>>>
>>>     var csv = CSVReader.newReader(r, Row.class, MethodHandles.lookup());
>>     Thanks.  Not surpringly, this works for constructor access.  It should
>>     help with using defineHiddenClass() as well if I read the
>>     documentation correctly (although maybe I can get what I want just
>>     using method handles).  Syntax-wise, I think it's okay, although it
>>     will certainly look like magic to newcomers if this programming
>>     pattern ever catches on (and similarly in other APIs that use
>>     reflective access behind the covers).  But from a conceptual
>>     perspective, it's really clear.
>>
>>     It appears that MethodHandles.lookup() uses an intrisified
>>     getCallerClass(), so it's probably not necessary to turn this into
>>     MethodHandles::lookup, to the lookup when it is not needed.
>>
>>>>     I think some people use interfaces and java.lang.reflect.Proxy for a
>>>>     similar purpose, hoping that method declaration order matches
>>>>     reflection order (not a good idea, I know).  As far as I can see, the
>>>>     proxy mechanism does not perform a module encapsulation check and can
>>>>     create instaces at points where a regular class declaration with an
>>>>     implements clause could not reference to the interface.  Records would
>>>>     be a better replacement because they provide ordering of components.
>>>     If you use an abstract deconstructors/pattern method, you get the declaration order
>>>
>>>        interface Row {
>>>          abstract deconstructor (String name, int index) Row();   // or a similar syntax
>>>        }
>>>
>>>        ...
>>>        Row(var name, var index) = csv.readRow();
>>>        // use name and index here
>>>
>>>     and i suppose that j.l.r.Proxy will be updated to implement such pattern.
>>     Is this already under discussion somewhere?  I've seen some references
>>     to generalizing pattern matching to custom types, but nothing along
>>     what you describe.
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20241209/f87dd9bd/attachment.htm>


More information about the amber-dev mailing list