|
|
Chromium Code Reviews|
Created:
4 years, 1 month ago by scheglov Modified:
4 years, 1 month ago CC:
reviews_dartlang.org Target Ref:
refs/heads/master Visibility:
Public. |
DescriptionMix file: and package: URIs during analysis.
R=brianwilkerson@google.com, paulberry@google.com
BUG=
Committed: https://github.com/dart-lang/sdk/commit/af23652ad179dcbc787dbb691d81511124ecd7f3
Patch Set 1 #Patch Set 2 : Create separate FileState instances for separate URIs for the same path. #
Total comments: 2
Patch Set 3 : Refresh all FileState instances for a path. #
Messages
Total messages: 15 (1 generated)
If we hit the cache with a package: URI this will optimize a later access using a file: URI, but not the other way around. That seems kind of strange. How big of a performance improvement is this? Is this necessary? If so, the I guess it lgtm, but unless we know it helps the asymmetry raises red flags.
The asymmetry feels weird to me too. Is it necessary to store the unlinked summary at both URIs in order to get correct behavior, or is it just a speed optimization? If it's just a speed optimization, I'm curious how much it helps. (My intuition is that in practical projects, files inside "lib" are always accessed through "package:" URIs, and files outside "lib" are always accessed through "file:" URIs, so it shouldn't actually make a difference). If it's necessary for correctness, I would like to understand why.
On 2016/11/02 22:16:20, Paul Berry wrote: > The asymmetry feels weird to me too. Is it necessary to store the unlinked > summary at both URIs in order to get correct behavior, or is it just a speed > optimization? > > If it's just a speed optimization, I'm curious how much it helps. (My intuition > is that in practical projects, files inside "lib" are always accessed through > "package:" URIs, and files outside "lib" are always accessed through "file:" > URIs, so it shouldn't actually make a difference). > > If it's necessary for correctness, I would like to understand why. Yes, this necessary for correctness. When we have a situation like in the added test, we have one file with the path /test/lib/a.dart, with the "primary" URI package:test/a.dart. But /test/test/c.dart (which has the URI file:///test/test/c.dart) references a.dart using relative URI so during linking will attempt to get unlinked unit with URI file:///test/lib/a.dart. If we don't put a.dart with the file: URI, linking of c.dart fails.
> If we don't put a.dart with the file: URI, linking of c.dart fails. I probably just don't understand linking well enough, but why would it fail? Wouldn't we just look for (and create if necessary) a summary for a different URI? As for the symmetry, it sounds like we only need the file: URI for files in the lib directory. Is that true? Out of curiosity, would it solve the problem if we always used the file path rather than the URI to cache the unlinked unit? Or, alternatively, could we just fix the test to use a package: URI?
On 2016/11/02 22:41:00, Brian Wilkerson wrote:
> > If we don't put a.dart with the file: URI, linking of c.dart fails.
>
> I probably just don't understand linking well enough, but why would it fail?
> Wouldn't we just look for (and create if necessary) a summary for a different
> URI?
>
> As for the symmetry, it sounds like we only need the file: URI for files in
the
> lib directory. Is that true?
>
> Out of curiosity, would it solve the problem if we always used the file path
> rather than the URI to cache the unlinked unit?
>
> Or, alternatively, could we just fix the test to use a package: URI?
Ok, I think I understand what's going on now, and I think this patch is working
around a latent bug.
The issue is that we keep just one FileState object per file, but the resolution
of imports, exports, and part declarations depends on the URI by which the file
was reached. In a Dart program written by a sane person, it doesn't matter.
But there are some insane corner cases where it does. And I think getting the
corner cases right is important, because it helps keep the analysis server
faithful to how the program is going to be interpreted by the VM. For example,
here is an insane program where my_pkg/lib/c.dart is interpreted differently
depending on how you got to it. Assume strong mode is enabled:
my_pkg/.packages:
my_pkg:lib
my_pkg/bin/a.dart:
import 'package:my_pkg/c.dart';
main() {}
int x = y;
my_pkg/bin/b.dart:
import '../lib/c.dart';
main() => foo();
int x = y;
my_pkg/lib/c.dart:
import '../test/d.dart';
var y = z;
my_pkg/test/d.dart:
String z = "string";
When analysis server analyzes my_pkg/lib/c.dart, it should assume the file is
being visited via the URI "package:my_pkg/c.dart", so it should produce an error
on the import statement, because you can't go up a directory from the root of a
package.
When analysis server analyzer my_pkg/bin/b.dart, it should produce the error "A
value of type 'String' can't be assigned to a variable of type 'int'", because
file:///my_pkg/bin/b.dart imports file:///my_pkg/lib/c.dart, which successfully
imports file:///my_pkg/test/d.dart, causing y to have an inferred type of
String.
When analysis server analyzes my_pkg/bin/a.dart, it should produce no error,
because file:///my_pkg/bin/a.dart imports package:my_pkg/c.dart, and
package:my_pkg/c.dart's import is erroneous, causing y's reference to z to be
unresolved (and therefore have type dynamic).
It's crazy, but analyzer (and analysis server as currently implemented) get it
right. I think the new driver should too.
I think achieving that is going to require a little bit of re-architecting.
Instead of storing a map from path to FileState, we should have a map from URI
to FileState, so that a given file can have different states depending on the
URI that is used to access it. When we get a changeFile notification we'll need
to invalidate *all* FileStates that resolve to the given path.
And we should put my example above (or something like it) into the unit tests to
make sure we don't regress.
On 2016/11/03 13:35:30, Paul Berry wrote:
> On 2016/11/02 22:41:00, Brian Wilkerson wrote:
> > > If we don't put a.dart with the file: URI, linking of c.dart fails.
> >
> > I probably just don't understand linking well enough, but why would it fail?
> > Wouldn't we just look for (and create if necessary) a summary for a
different
> > URI?
> >
> > As for the symmetry, it sounds like we only need the file: URI for files in
> the
> > lib directory. Is that true?
> >
> > Out of curiosity, would it solve the problem if we always used the file path
> > rather than the URI to cache the unlinked unit?
> >
> > Or, alternatively, could we just fix the test to use a package: URI?
>
> Ok, I think I understand what's going on now, and I think this patch is
working
> around a latent bug.
>
> The issue is that we keep just one FileState object per file, but the
resolution
> of imports, exports, and part declarations depends on the URI by which the
file
> was reached. In a Dart program written by a sane person, it doesn't matter.
> But there are some insane corner cases where it does. And I think getting the
> corner cases right is important, because it helps keep the analysis server
> faithful to how the program is going to be interpreted by the VM. For
example,
> here is an insane program where my_pkg/lib/c.dart is interpreted differently
> depending on how you got to it. Assume strong mode is enabled:
>
> my_pkg/.packages:
> my_pkg:lib
>
> my_pkg/bin/a.dart:
> import 'package:my_pkg/c.dart';
> main() {}
> int x = y;
>
> my_pkg/bin/b.dart:
> import '../lib/c.dart';
> main() => foo();
> int x = y;
>
> my_pkg/lib/c.dart:
> import '../test/d.dart';
> var y = z;
>
> my_pkg/test/d.dart:
> String z = "string";
>
> When analysis server analyzes my_pkg/lib/c.dart, it should assume the file is
> being visited via the URI "package:my_pkg/c.dart", so it should produce an
error
> on the import statement, because you can't go up a directory from the root of
a
> package.
>
> When analysis server analyzer my_pkg/bin/b.dart, it should produce the error
"A
> value of type 'String' can't be assigned to a variable of type 'int'", because
> file:///my_pkg/bin/b.dart imports file:///my_pkg/lib/c.dart, which
successfully
> imports file:///my_pkg/test/d.dart, causing y to have an inferred type of
> String.
>
> When analysis server analyzes my_pkg/bin/a.dart, it should produce no error,
> because file:///my_pkg/bin/a.dart imports package:my_pkg/c.dart, and
> package:my_pkg/c.dart's import is erroneous, causing y's reference to z to be
> unresolved (and therefore have type dynamic).
>
> It's crazy, but analyzer (and analysis server as currently implemented) get it
> right. I think the new driver should too.
>
> I think achieving that is going to require a little bit of re-architecting.
> Instead of storing a map from path to FileState, we should have a map from URI
> to FileState, so that a given file can have different states depending on the
> URI that is used to access it. When we get a changeFile notification we'll
need
> to invalidate *all* FileStates that resolve to the given path.
>
> And we should put my example above (or something like it) into the unit tests
to
> make sure we don't regress.
On further reflection, maybe it would be better to separate FileState into two
data structures: FileState to keep track of the state of files (which would
include the content, content hash, unlinked summary, and API signature) and a
new data structure to keep track of the import graph. FileState would continue
to be keyed by file path (As it is now), whereas the new data structure would be
keyed by URI.
On 2016/11/03 14:41:28, Paul Berry wrote:
> On 2016/11/03 13:35:30, Paul Berry wrote:
> > On 2016/11/02 22:41:00, Brian Wilkerson wrote:
> > > > If we don't put a.dart with the file: URI, linking of c.dart fails.
> > >
> > > I probably just don't understand linking well enough, but why would it
fail?
> > > Wouldn't we just look for (and create if necessary) a summary for a
> different
> > > URI?
> > >
> > > As for the symmetry, it sounds like we only need the file: URI for files
in
> > the
> > > lib directory. Is that true?
> > >
> > > Out of curiosity, would it solve the problem if we always used the file
path
> > > rather than the URI to cache the unlinked unit?
> > >
> > > Or, alternatively, could we just fix the test to use a package: URI?
> >
> > Ok, I think I understand what's going on now, and I think this patch is
> working
> > around a latent bug.
> >
> > The issue is that we keep just one FileState object per file, but the
> resolution
> > of imports, exports, and part declarations depends on the URI by which the
> file
> > was reached. In a Dart program written by a sane person, it doesn't matter.
> > But there are some insane corner cases where it does. And I think getting
the
> > corner cases right is important, because it helps keep the analysis server
> > faithful to how the program is going to be interpreted by the VM. For
> example,
> > here is an insane program where my_pkg/lib/c.dart is interpreted differently
> > depending on how you got to it. Assume strong mode is enabled:
> >
> > my_pkg/.packages:
> > my_pkg:lib
> >
> > my_pkg/bin/a.dart:
> > import 'package:my_pkg/c.dart';
> > main() {}
> > int x = y;
> >
> > my_pkg/bin/b.dart:
> > import '../lib/c.dart';
> > main() => foo();
> > int x = y;
> >
> > my_pkg/lib/c.dart:
> > import '../test/d.dart';
> > var y = z;
> >
> > my_pkg/test/d.dart:
> > String z = "string";
> >
> > When analysis server analyzes my_pkg/lib/c.dart, it should assume the file
is
> > being visited via the URI "package:my_pkg/c.dart", so it should produce an
> error
> > on the import statement, because you can't go up a directory from the root
of
> a
> > package.
> >
> > When analysis server analyzer my_pkg/bin/b.dart, it should produce the error
> "A
> > value of type 'String' can't be assigned to a variable of type 'int'",
because
> > file:///my_pkg/bin/b.dart imports file:///my_pkg/lib/c.dart, which
> successfully
> > imports file:///my_pkg/test/d.dart, causing y to have an inferred type of
> > String.
> >
> > When analysis server analyzes my_pkg/bin/a.dart, it should produce no error,
> > because file:///my_pkg/bin/a.dart imports package:my_pkg/c.dart, and
> > package:my_pkg/c.dart's import is erroneous, causing y's reference to z to
be
> > unresolved (and therefore have type dynamic).
> >
> > It's crazy, but analyzer (and analysis server as currently implemented) get
it
> > right. I think the new driver should too.
> >
> > I think achieving that is going to require a little bit of re-architecting.
> > Instead of storing a map from path to FileState, we should have a map from
URI
> > to FileState, so that a given file can have different states depending on
the
> > URI that is used to access it. When we get a changeFile notification we'll
> need
> > to invalidate *all* FileStates that resolve to the given path.
> >
> > And we should put my example above (or something like it) into the unit
tests
> to
> > make sure we don't regress.
>
> On further reflection, maybe it would be better to separate FileState into two
> data structures: FileState to keep track of the state of files (which would
> include the content, content hash, unlinked summary, and API signature) and a
> new data structure to keep track of the import graph. FileState would
continue
> to be keyed by file path (As it is now), whereas the new data structure would
be
> keyed by URI.
Paul, I agree.
I thought so too, but want to delay this separation a bit.
Separation between FileState and UriState (or UnitState) could improve even more
sharing.
Currently we keep separate copies of UnlinkedBundle and content for each
analysis root.
With introduction of shared FileState (pure files) we could share bundles and
content.
But I think this sharing is not critical for initial version, so I would like to
do it after many other things are done.
PTAL
lgtm
https://codereview.chromium.org/2469323004/diff/20001/pkg/analyzer/lib/src/da... File pkg/analyzer/lib/src/dart/analysis/driver.dart (right): https://codereview.chromium.org/2469323004/diff/20001/pkg/analyzer/lib/src/da... pkg/analyzer/lib/src/dart/analysis/driver.dart:686: bool apiChanged = file.refresh(); Don't we need to refresh all files that are associated with [path]? Otherwise, if a file's import/export/part declarations are changed, only the FileState corresponding to its canonical URI will get updated, and the import graph for its other URIs will still be old.
PTAL https://codereview.chromium.org/2469323004/diff/20001/pkg/analyzer/lib/src/da... File pkg/analyzer/lib/src/dart/analysis/driver.dart (right): https://codereview.chromium.org/2469323004/diff/20001/pkg/analyzer/lib/src/da... pkg/analyzer/lib/src/dart/analysis/driver.dart:686: bool apiChanged = file.refresh(); On 2016/11/03 21:39:34, Paul Berry wrote: > Don't we need to refresh all files that are associated with [path]? > > Otherwise, if a file's import/export/part declarations are changed, only the > FileState corresponding to its canonical URI will get updated, and the import > graph for its other URIs will still be old. Done.
lgtm
Description was changed from ========== Mix file: and package: URIs during analysis. R=brianwilkerson@google.com, paulberry@google.com BUG= ========== to ========== Mix file: and package: URIs during analysis. R=brianwilkerson@google.com, paulberry@google.com BUG= Committed: https://github.com/dart-lang/sdk/commit/af23652ad179dcbc787dbb691d81511124ecd7f3 ==========
Message was sent while issue was closed.
Committed patchset #3 (id:40001) manually as af23652ad179dcbc787dbb691d81511124ecd7f3 (presubmit successful). |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
