Skip to content

[java] Rewrite symbol table#2601

Merged
adangel merged 42 commits into
pmd:java-grammarfrom
oowekyala:sym-optimize-symtables
Jul 19, 2020
Merged

[java] Rewrite symbol table#2601
adangel merged 42 commits into
pmd:java-grammarfrom
oowekyala:sym-optimize-symtables

Conversation

@oowekyala

Copy link
Copy Markdown
Member

Describe the PR

Rewrites the newer symbol table for maintainability and performance.
This extracts the following abstractions from the current symbol table implementation:

  • NameResolver: this is a strategy that can map a name to a symbol, eg get it from a map, or ask a classloader. These were implemented ad-hoc in each symbol table implementation, now they're factorized into reusable units
  • ShadowChain: this is the API that symbol tables offered (resolve a name), but broken down, one chain for each kind of name. This is a linked list of NameResolvers, each node shadows the next one. Symbol tables previously were a single linked list for all kind of names, so this was a possible performance bottleneck

This also unifies the API to resolve method, type and variable names (previously resolveMethod returned a List, the others returned a ResolveResult). Now all may return a List. This handles name ambiguity and duplicate declarations for variables and type names, so it is more general than the previous mechanism. It also reuses more code.

In this new framework, JSymbolTable is retained as just a simple tuple of ShadowChains, for ease of use (but it's not a central concept anymore).

This also adds a "MostlySingularMultimap" type, a kind of multimap that is optimised for the case where there are actually only at most one value per key. So the implementation is optimised for the most common case.


Possibly, the ShadowChain API could be lifted up to pmd-core. I'm waiting for a use case before doing this, but I implemented it as if it was in another module, so it has no dependencies on pmd-java


That was the "rewrite" part. The PR also adds support for inherited member resolution and a lot of tests to cover corner cases (hiding of inherited declarations).

This is the last piece needed before we proceed to the type resolution revamp.

Ready?

  • Added unit tests for fixed bug/feature
  • Passing all unit tests
  • Complete build ./mvnw clean verify passes (checked automatically by travis)
  • Added (in-code) documentation (if needed)

@oowekyala oowekyala added the in:symbol-table Affects the symbol table code label Jun 18, 2020
@oowekyala oowekyala added this to the 7.0.0 milestone Jun 18, 2020
@ghost

ghost commented Jun 18, 2020

Copy link
Copy Markdown
1 Message
📖 No java rules are changed!

Generated by 🚫 Danger

@oowekyala oowekyala marked this pull request as ready for review June 18, 2020 16:12
@adangel adangel self-assigned this Jul 19, 2020

@adangel adangel left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry that it took so long, will merge soon...

package net.sourceforge.pmd.util;

/** Represents a boolean that may not be present. Use as a non-null type. */
public enum OptionalBool {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be public? It sounds like an implementation helper....

Hm... seems to be used in the shadow chain API. Any reason, why you didn't use Optional<Boolean> instead?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm... seems to be used in the shadow chain API.

It's also used in some parts of the upcoming typeres API too. It's a common pattern I think, one that is sometimes replaced with a nullable boxed Boolean, which I think is... meh

Any reason, why you didn't use Optional instead?

All three values are known and there's no need to create a new optional every time

* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/

package net.sourceforge.pmd.lang.java.symbols.table.coreimpl;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the package name "coreimpl" sounds a bit unusual. That's the part, you made independent of pmd-java and could potentially be moved to core, right?
Maybe we should rename the sub-package with something like "shadow" or "nameshadow" or so... or maybe something with "hierarchy" in it?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's the part, you made independent of pmd-java and could potentially be moved to core, right?

Indeed, which is why the packaging is a bit awkward... Maybe chain would be a better name?

ShadowChain is a bit awkward, but I wanted to avoid the name clash with the current Scope classes we have. I think in a world where Scope doesn't exist, ShadowChain could be renamed to ScopeChain, and placed eg in the package nspmd.symbols.scopes?

@adangel adangel merged commit 6c3f8c4 into pmd:java-grammar Jul 19, 2020
@oowekyala oowekyala deleted the sym-optimize-symtables branch July 19, 2020 17:59
@adangel

adangel commented Jul 19, 2020

Copy link
Copy Markdown
Member

@oowekyala I've merged (and pushed) pmd/7.0.x into java-grammar. There are the following failing tests:

[ERROR] Failures: 
[ERROR]   Not a child class of the current context (ReferenceCtx{packageName='com.foo', enclosingClass=ast:class(com.foo.Foo)}): ast:class(com.foo.Foo$7$AnonMember)
[ERROR]   Not a child class of the current context (ReferenceCtx{packageName='com.foo', enclosingClass=ast:class(com.foo.Foo)}): ast:class(com.foo.Foo$7$AnonMember)
[ERROR]   Not a child class of the current context (ReferenceCtx{packageName='com.foo', enclosingClass=ast:class(com.foo.Foo)}): ast:class(com.foo.Foo$1$1Locc)
[ERROR]   Not a child class of the current context (ReferenceCtx{packageName='com.foo', enclosingClass=ast:class(com.foo.Foo)}): ast:class(com.foo.Foo$1$1Locc)
[ERROR]   JavaQualifiedNameTest.testLocalInAnonymousClass:231->getNodes:27 Not a child class of the current context (ReferenceCtx{packageName='', enclosingClass=ast:class(Bzaz)}): ast:class(Bzaz$1$1FooRunnable)

Are these related to the symbol table? I've run out of time for today....

@oowekyala

Copy link
Copy Markdown
Member Author

Thanks for doing this! They're not directly related, they happen because JavaVisitorBase doesn't delegate visit(ASTAnonymousClass) to visit(ASTAnyTypeDeclaration). I'll push a fix

@adangel adangel mentioned this pull request Jan 23, 2023
55 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

in:symbol-table Affects the symbol table code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants