Skip to content

Create a shared implementation for Locations and Files #10592

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

Closed
wants to merge 1 commit into from

Conversation

erik-krogh
Copy link
Contributor

@erik-krogh erik-krogh commented Sep 27, 2022

This implementation is dependent on features that have been merged to main but has not been released into a stable release yet.
The tests should start passing when the nightly build includes the required features.

I'm only using the shared implementation in Ruby, even tough I have implemented the signature in both Ruby and JS, and I'm not planning on changing that.

In JavaScript the File class have a bunch of utility predicates that don't make sense in a shared pack.
I can create a new class that extends the shared File class, but I cannot make e.g. Folder::getAFile() from the shared library return that more specific implementation.
Not unless I extend every predicate from the shared pack that can (transitively) result in a File.
And at that point there's little point in a shared implementation.

A big motivation for this work is that a shared ReDoS pack will depend a location/file implementation, and it didn't seem right to include that in the signature for ReDoS.

@erik-krogh erik-krogh added the WIP This is a work-in-progress, do not merge yet! label Sep 27, 2022
@@ -0,0 +1,21 @@
private import codeql.utils.FileSystem as FS
Copy link
Contributor

Choose a reason for hiding this comment

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

This should probably be in a subfolder named internal , there is no reason external users should ever import this module. This is also why it was defined as a private module initially.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

A problem with that is that I want to use module FilesImpl elsewhere later, the module will not just be used for instantiating the shared File implementation.
So it feels wrong to put FilesImpl in a internal/ folder.

E.g. my shared ReDoS implementation will depend on FS::FilesSig (or something similar), so I will need the FilesImpl module when I instantiate the parameterised ReDoS implementation.
And it feels wrong to import codeql.files.internal.FilesImpl all the way from codeql.ruby.security.regexp.ExponentialBackTracking.

In the glorious future we might have many packs/modules from the shared/ folder that depend on each other, so this issue will only become bigger.

Another possible solution would be to create a ruby/ql/lib/codeql/ruby/utils folder, and that folder would be dedicated to implementations of signatures from the utils package.
(And the other languages would have a similar folder).
I kinda like that solution.
An alternative folder name would be impls/ or impl/, and that folder name could also be used elsewhere (e.g. I might create an impls/ folder under codeql.ruby.security.regexp).

Other implementations will hopefully be easier to place. E.g. the instantiations of the ReDoS library will just be in the same files that the current implementation sits in.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think it is OK for us to use internal libraries wherever we want, we just like to make sure that nobody else assumes stability of the internal ibraries.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I still think external developers should be able to use e.g. FileSystemImpl if they develop their own shared library.

However, it's easy to later move something out of an internal/ folder, but it's tricky to move some into an internal/ folder without breaking our deprecation policy.
So I'll move the implementations into an internal/ folder.

private import codeql.utils.FileSystem as FS

module FilesImpl implements FS::FilesSig {
class AtFile extends @file, AtContainer {
Copy link
Contributor

Choose a reason for hiding this comment

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

I see you changed FileSig to define proper classes instead of a bunch of predicates that are to be aliased to the matching database relations and classes that match directly to database @types.

Not sure which way is preferred. Your approach introduces an additional bunch of classes with the same hierarchy as in Make. Not sure if the optimizer can get rid of that. I think the more direct approach should result in the exact same code as the old-style hand-written implementations when the parametrised modules are instantiated.

Copy link
Contributor Author

@erik-krogh erik-krogh Sep 28, 2022

Choose a reason for hiding this comment

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

I think the optimizer will get rid of it once we reach the RA level, but I'm not too sure.
I'll do DCA experiments once I got a polished implementation and the compiler crash that's currently blocking me is fixed.

I like my approach (but maybe I should do something about the names).
You had both a files_ and folders_ predicate in your signature, which I've collapsed into a single getAbsolutePath() in AtContainer.
You likely chose to make those two predicate because the dbscheme has a files() and folders() relation, but that feels like an implementation detail.

override string getAbsolutePath() { files(this, result) }
}

abstract class AtContainer extends @container {
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's remove the At prefix, they were there to indicate that those types mapped to an @ type.

/**
* A shared library for reasoning about files and folders.
* This file contains 3 modules:
* - `BasicFileSig` is a basic interface that every language is expected to implement.
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't see the definition of BasicFileSig anywhere, does this comment need updating?

Copy link
Contributor Author

@erik-krogh erik-krogh Sep 28, 2022

Choose a reason for hiding this comment

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

does this comment need updating?

Yes it does.
When I wrote that comment I had 3 modules as described, but I removed the "full implementation as a signature" module.

Comment on lines 2 to 3
private import codeql.files.FilesImpl::FilesImpl as FSImpl
private import codeql.LocationsImpl::LocationsImpl as LImpl
Copy link
Contributor

Choose a reason for hiding this comment

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

Do these need to go into separate files, as opposed to just private modules in this file?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As explained above.
I need the implementations of the signatures elsewhere (e.g. in the glorious future when I'm instantiating the shared ReDoS library).

So LocationsImpl as a private module won't work.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also we get naming conflicts due to shadowing if I put them in the same file.

I can't have a LocationsImpl that declares a class Location, and also have the file export a Location class.
That would require that we rename the classes in the signature.

}

/** An entity representing an empty location. */
class EmptyLocation extends Location {
Copy link
Contributor

Choose a reason for hiding this comment

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

Not all languages may extract empty locations.

Copy link
Contributor

Choose a reason for hiding this comment

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

That's true, not sure if this is a problem though. For those languages the EmptyLocation class would simply be empty.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For those languages the EmptyLocation class would simply be empty.

But that would be confusing.
I'll move EmptyLocation into the language specific bits.

@erik-krogh
Copy link
Contributor Author

I no longer think this is needed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation JS Ruby WIP This is a work-in-progress, do not merge yet!
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants