@@ -519,9 +519,59 @@ public static SourceFile fromCode(String fileName, String code) {
519
519
return builder ().withPath (fileName ).withContent (code ).build ();
520
520
}
521
521
522
+ /**
523
+ * Reconciles serialized state in a {@link SourceFileProto} with the existing state in this file.
524
+ *
525
+ * <p>This should be called whenever initializing a compilation based on TypedAST protos. For
526
+ * these compilations, the compiler initalization methods require creating SourceFiles before
527
+ * deserializing TypedAST protos, so we sometimes get two copies of the same SourceFile.)
528
+ */
529
+ public void restoreCachedStateFrom (SourceFileProto protoSourceFile ) {
530
+ checkState (
531
+ protoSourceFile .getFilename ().equals (this .getName ()),
532
+ "Cannot restore state for %s from %s" ,
533
+ this .getName (),
534
+ protoSourceFile .getFilename ());
535
+ // TypedAST proto information is expected to be more accurate for:
536
+ // 1) whether a SourceFile contains an @extern annotation or not. In non-TypedAST
537
+ // builds, we allow passing @extern files under the --js flag. For TypedAST builds, we
538
+ // could support this, but it's an uncommon pattern and trickier to support than ban.
539
+ // 2) tracking some extra state that is lazily computed in a SourceFile, like the number of
540
+ // lines and bytes in a file. SourceFile::restoreCachedStateFrom handles this case.
541
+ // Note: the state in the proto might be incorrect in other cases, since some state cannot be
542
+ // computed during library-level typechecking (e.g. what files are in the weak chunk)
543
+ if (protoSourceFile .getSourceKind () == SourceFileProto .SourceKind .EXTERN ) {
544
+ checkState (
545
+ this .getKind () == SourceKind .EXTERN ,
546
+ "TypedAST compilations must pass all extern files as externs, not js, but found %s" ,
547
+ this .getName ());
548
+ }
549
+
550
+ // Restore the number of lines/bytes from the proto, unless we already have cached
551
+ // numLines and numBytes. Offset by 1. the proto "unset" value is 0, where as in SourceFile we
552
+ // use "-1"
553
+ int protoNumLines = protoSourceFile .getNumLinesPlusOne () - 1 ;
554
+ int protoNumBytes = protoSourceFile .getNumBytesPlusOne () - 1 ;
555
+ // It's possible that this.numLines and protoNumLines are both not "-1" but contain
556
+ // conflicting values. This would happen if a file changes on disk after TypedAST
557
+ // proto serialization. We choose the proto value over this.numLines because this
558
+ // data is intended for metrics recording, which should care most about the size of a
559
+ // file when compilation began and parsing ran.
560
+ this .numLines = protoNumLines != -1 ? protoNumLines : this .numLines ;
561
+ this .numBytes = protoNumBytes != -1 ? protoNumBytes : this .numBytes ;
562
+ }
563
+
522
564
@ GwtIncompatible ("java.io.Reader" )
523
565
public static SourceFile fromProto (SourceFileProto protoSourceFile ) {
524
566
SourceKind sourceKind = getSourceKindFromProto (protoSourceFile );
567
+ SourceFile sourceFile = fromProto (protoSourceFile , sourceKind );
568
+ // Restore the number of lines/bytes, which are offset by 1 in the proto.
569
+ sourceFile .numLines = protoSourceFile .getNumLinesPlusOne () - 1 ;
570
+ sourceFile .numBytes = protoSourceFile .getNumBytesPlusOne () - 1 ;
571
+ return sourceFile ;
572
+ }
573
+
574
+ private static SourceFile fromProto (SourceFileProto protoSourceFile , SourceKind sourceKind ) {
525
575
switch (protoSourceFile .getLoaderCase ()) {
526
576
case PRELOADED_CONTENTS :
527
577
return SourceFile .fromCode (
@@ -939,6 +989,8 @@ public SourceFileProto getProto() {
939
989
.toProtoLocationBuilder (this .getName ())
940
990
.setFilename (this .getName ())
941
991
.setSourceKind (sourceKindToProto (this .getKind ()))
992
+ .setNumLinesPlusOne (this .numLines + 1 )
993
+ .setNumBytesPlusOne (this .numBytes + 1 )
942
994
.build ();
943
995
}
944
996
0 commit comments