@@ -519,9 +519,59 @@ 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+ // 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+
522564 @ GwtIncompatible ("java.io.Reader" )
523565 public static SourceFile fromProto (SourceFileProto protoSourceFile ) {
524566 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 ) {
525575 switch (protoSourceFile .getLoaderCase ()) {
526576 case PRELOADED_CONTENTS :
527577 return SourceFile .fromCode (
@@ -939,6 +989,8 @@ public SourceFileProto getProto() {
939989 .toProtoLocationBuilder (this .getName ())
940990 .setFilename (this .getName ())
941991 .setSourceKind (sourceKindToProto (this .getKind ()))
992+ .setNumLinesPlusOne (this .numLines + 1 )
993+ .setNumBytesPlusOne (this .numBytes + 1 )
942994 .build ();
943995 }
944996
0 commit comments