-
-
Notifications
You must be signed in to change notification settings - Fork 353
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
review feat: Add Java9 CtModule to Spoon model #1730
Conversation
@monperrus @pvojtechovsky I'm starting to work on Java 9 modules to add the concept in Spoon model. Based on the JLS a module has the following definition (see: https://docs.oracle.com/javase/specs/jls/se9/html/jls-7.html#jls-7.7):
I don't know how to express properly the different directives, without creating new interfaces in the model to represent them. Could you give me a quick opinion about the first proposed hierarchy of classes: then I'll be able to work on it. |
A quick proposal:
|ModuleDirective: -> CtModule ModuleName -> get/setSimpleName PackageName -> CtPackageReference
TypeName -> CtTypeReference transitive static -> add transitive to ModifierKind|
|
@monperrus, I don't understand this language ;-)
Note: I have zero exprience with java9 modules. I just read some tutorials and specification linked by Simon. I have some questions ... Q1) May be we should introduce a concept of a Q2) Is CtModuleExport extends CtModuleMember {
CtPackageReference package;
Set<CtModuleReference> targetExport;
} Q3) Are comments allowed in module-info.java filed? If yes, then each CtModule member should NOT extend a |
@@ -39,6 +43,23 @@ | |||
|
|||
private static final long serialVersionUID = 1L; | |||
|
|||
@Override | |||
public <R extends CtElement> CtQuery filterChildren(Filter<R> filter) { | |||
return getUnnamedModule().getFactory().Query().createQuery(this.getAllModules().toArray()).filterChildren(filter); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
QueryFactory#createQuery
has actually parameter type Object, not Object[], so this code will not work as expected. We have to add new QueryFactory#createQuery(Object[]) method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My bad, that's because CtQueryImpl
constructor as an Object...
arg. I'll fix that.
@@ -40,6 +42,16 @@ | |||
/** returns all packages of the model */ | |||
Collection<CtPackage> getAllPackages(); | |||
|
|||
/** | |||
* returns the unnamed module |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
returns the unnamed module. Warning: if there are other modules, they are not contained in that unnamed root module.
|
||
/** returns the root package */ | ||
/** returns the root package of the unnamed module */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If there are several modules, it throws an exception
src/main/java/spoon/Metamodel.java
Outdated
@@ -146,6 +146,11 @@ private Metamodel() { } | |||
result.add(factory.Type().get(spoon.reflect.reference.CtUnboundVariableReference.class)); | |||
result.add(factory.Type().get(spoon.reflect.reference.CtVariableReference.class)); | |||
result.add(factory.Type().get(spoon.reflect.reference.CtWildcardReference.class)); | |||
result.add(factory.Type().get(spoon.reflect.declaration.CtModule.class)); | |||
result.add(factory.Type().get(spoon.reflect.declaration.CtModuleRequirement.class)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CtModuleProvidedService should be called CtProvidedService
add CtUsedService
no reaction here? Did you understood this problem? Will be spoon able to distinguish between these two cases? module com.example.foo {
requires com.example.foo.http;
exports com.example.foo.bar;
} and module com.example.foo {
exports com.example.foo.bar;
requires com.example.foo.http;
}
... one of the bigger problem which avoids using spoon productive is that it changes formatting of the pretty printed source files. So it cannot be easily applied to existing sources to let it change few things only.
I think it makes sense to keep order of module directives to minimize changes between origin and pretty printed code. |
That's a good idea. |
@@ -51,6 +52,8 @@ | |||
|
|||
void onSetAdd(CtElement currentElement, CtRole role, Set field, ModifierKind newValue); | |||
|
|||
void onSetAdd(CtElement currentElement, CtRole role, Set field, CtModuleRequirement.RequiresModifier newValue); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we really going to have copy of this method (and others) for each non CtElement type?
I am ok wit that idea. Optionaly we might just modify implementation of root package and
|
1 similar comment
I am ok wit that idea. Optionaly we might just modify implementation of root package and
|
if (allModules.containsKey(module.getSimpleName())) { | ||
return false; | ||
} | ||
allModules.put(module.getSimpleName(), module); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see one more problem. Later call of CtModule.setSimpleName(...) will cause inconsistency in modules, because the module is registered under old name.
May be we should store modules as List and getModule
should always search that list by name.
Then you can add the module into that list sooner directly in factory.Core().createModule()
call - at time when it has no name. WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see one more problem. Later call of CtModule.setSimpleName(...) will cause inconsistency in modules, because the module is registered under old name.
Agree that's a problem. I'll try to produce a test to show the bug, and fix it the way you propose.
CtModule ctModule = getUnnamedModule().getModule(moduleName); | ||
if (ctModule == null) { | ||
ctModule = factory.Core().createModule().setSimpleName(moduleName).setParent(getUnnamedModule()); | ||
getUnnamedModule().addModule(ctModule); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about move of this code getUnnamedModule().addModule(ctModule);
into factory.Core().createModule()
. WDYT?
@@ -41,6 +42,17 @@ | |||
/** returns all packages of the model */ | |||
Collection<CtPackage> getAllPackages(); | |||
|
|||
/** | |||
* Returns the unnamed module. | |||
* Warning: if there are other modules, they are not contained in that unnamed root module, use getAllModules instead. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it still true? I guess getUnnamedModule().getAllModules()
now works same like Model.getAllModules(). OR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No I think you're right.
|
||
@Override | ||
public Collection<CtModule> getAllModules() { | ||
return this.unnamedModule.getFactory().Module().getAllModules(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shorter return this.unnamedModule.getAllModules();
should return the same result now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed.
return (T) this; | ||
} | ||
if (this.moduleDirectives == CtElementImpl.<CtModuleDirective>emptyList()) { | ||
this.moduleDirectives = new SortedList<>(new CtLineElementComparator()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest to sort directives only once at time when CtModule is created by JDT builder. I think, later automatic sorting of client specific module directives by position is not expected by clients. WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't agree. I think we should document that the module directives are ordered by position. Now I think users don't put position attribute in the element when they're modifying the model. So they will use the method addModuleDirectiveAtPosition
when they want to specify a position.
if (!this.getSimpleName().equals(CtModule.TOP_LEVEL_MODULE_NAME) && parent != getFactory().Module().getUnnamedModule()) { | ||
throw new SpoonException("The parent of a module should necessarily be the unnamed module."); | ||
} | ||
return (T) super.setParent(parent); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May be we can mark setParent here as Derived property and ignore call of setParent here?
I guess there is no case when somebody should be able to set different parent then unnamedModule
...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, it will avoid unexpected behaviours there.
I'm ok with those idea. I'll try to implement it in that way. |
So I'm kind of stuck with two problems related to the JDT usage right now:
The master problem around those is that I don't find any proper documentation on how JDT handles modules. Actually I got some piece of information in JDT bug tracker, but I don't know if it's reliable and my experiments don't show many improvement: I suspect that the next release of JDT should add improvement on modules, but I'm not sure right now. I was even trying to compile myself JDT from the sources to see if it helps, but even then the documentation is either hidden either inexistent. WDYT @monperrus @pvojtechovsky? |
I agree that we should merge this work even if it is not 100% finished. |
So I added documentation and ignored the failing tests. For me it's ready for merging @pvojtechovsky @monperrus Once it's merged I'll create issues for the remaining unsupported features associated with modules. |
Detected changes by Revapi: 28. Old API: fr.inria.gforge.spoon:spoon-core:jar:6.1.0-20171201.110051-26 New API: fr.inria.gforge.spoon:spoon-core:jar:6.1.0-SNAPSHOT
|
excellent! if everybody agrees I'll merge it on Monday. |
I agree, Nice code and documentation! Thank You Simon! |
@monperrus it's ready for me too |
Thanks a lot for the hard and deep work! |
No description provided.