@@ -519,9 +519,66 @@ public static SourceFile fromCode(String fileName, String code) {
519519 return builder ().withPath (fileName ).withContent (code ).build ();
520520 }
521521
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+ checkState (
556+ this .numLines == -1 || protoNumLines == -1 || this .numLines == protoNumLines ,
557+ "Mismatch in numLines for %s. SourceFile: %s, SourceFileProto: %s" ,
558+ this .getName (),
559+ this .numLines ,
560+ protoNumLines );
561+ checkState (
562+ this .numBytes == -1 || protoNumBytes == -1 || this .numBytes == protoNumBytes ,
563+ "Mismatch in numBytes for %s. SourceFile: %s, SourceFileProto: %s" ,
564+ this .getName (),
565+ this .numBytes ,
566+ protoNumBytes );
567+ this .numLines = this .numLines != -1 ? this .numLines : protoNumLines ;
568+ this .numBytes = this .numBytes != -1 ? this .numBytes : protoNumBytes ;
569+ }
570+
522571 @ GwtIncompatible ("java.io.Reader" )
523572 public static SourceFile fromProto (SourceFileProto protoSourceFile ) {
524573 SourceKind sourceKind = getSourceKindFromProto (protoSourceFile );
574+ SourceFile sourceFile = fromProto (protoSourceFile , sourceKind );
575+ // Restore the number of lines/bytes, which are offset by 1 in the proto.
576+ sourceFile .numLines = protoSourceFile .getNumLinesPlusOne () - 1 ;
577+ sourceFile .numBytes = protoSourceFile .getNumBytesPlusOne () - 1 ;
578+ return sourceFile ;
579+ }
580+
581+ private static SourceFile fromProto (SourceFileProto protoSourceFile , SourceKind sourceKind ) {
525582 switch (protoSourceFile .getLoaderCase ()) {
526583 case PRELOADED_CONTENTS :
527584 return SourceFile .fromCode (
@@ -939,6 +996,8 @@ public SourceFileProto getProto() {
939996 .toProtoLocationBuilder (this .getName ())
940997 .setFilename (this .getName ())
941998 .setSourceKind (sourceKindToProto (this .getKind ()))
999+ .setNumLinesPlusOne (this .numLines + 1 )
1000+ .setNumBytesPlusOne (this .numBytes + 1 )
9421001 .build ();
9431002 }
9441003
0 commit comments