Skip to content

Commit 52abaaa

Browse files
author
Stu Hood
committed
Intern strings during deserialization.
1 parent 899d7bd commit 52abaaa

File tree

2 files changed

+34
-22
lines changed

2 files changed

+34
-22
lines changed

internal/zinc-persist/src/main/scala/sbt/internal/inc/binary/BinaryAnalysisFormat.scala

+3-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import xsbti.compile.{ CompileAnalysis, MiniSetup }
1616
final class BinaryAnalysisFormat(mappers: ReadWriteMappers) {
1717
private final val CurrentVersion = schema.Version.V1
1818
private final val protobufWriters = new ProtobufWriters(mappers.getWriteMapper)
19-
private final val protobufReaders = new ProtobufReaders(mappers.getReadMapper)
19+
private final val readMapper = mappers.getReadMapper
2020

2121
def write(writer: CodedOutputStream, analysis0: CompileAnalysis, miniSetup: MiniSetup): Unit = {
2222
val analysis = analysis0 match { case analysis: Analysis => analysis }
@@ -34,13 +34,15 @@ final class BinaryAnalysisFormat(mappers: ReadWriteMappers) {
3434

3535
def read(reader: CodedInputStream): (CompileAnalysis, MiniSetup) = {
3636
val protobufFile = schema.AnalysisFile.parseFrom(reader)
37+
val protobufReaders = new ProtobufReaders(readMapper)
3738
val (analysis, miniSetup, _) = protobufReaders.fromAnalysisFile(protobufFile)
3839
analysis -> miniSetup
3940
}
4041

4142
def readAPIs(reader: CodedInputStream, analysis0: CompileAnalysis): CompileAnalysis = {
4243
val analysis = analysis0 match { case analysis: Analysis => analysis }
4344
val protobufAPIsFile = schema.APIsFile.parseFrom(reader)
45+
val protobufReaders = new ProtobufReaders(readMapper)
4446
val (apis, _) = protobufReaders.fromApisFile(protobufAPIsFile)
4547
analysis.copy(apis = apis)
4648
}

internal/zinc-persist/src/main/scala/sbt/internal/inc/binary/converters/ProtobufReaders.scala

+31-21
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
package sbt.internal.inc.binary.converters
99

1010
import java.io.File
11+
import scala.collection.mutable
1112

1213
import sbt.internal.inc.Relations.ClassDependencies
1314
import sbt.internal.inc._
@@ -21,9 +22,18 @@ import sbt.internal.inc.binary.converters.ProtobufDefaults.{ Classes, ReadersCon
2122
import sbt.internal.util.Relation
2223
import xsbti.api._
2324

25+
/**
26+
* Each instance of this class will intern (most) deserialized strings in one (instance-level)
27+
* pool, so it's recommended to use a new instance per deserialized top-level object (generally
28+
* `AnalysisFile`).
29+
*/
2430
final class ProtobufReaders(mapper: ReadMapper) {
31+
private[this] val internMap = mutable.Map[String, String]()
32+
33+
private[this] def intern(str: String): String = internMap.getOrElseUpdate(str, str)
34+
2535
def fromPathString(path: String): File = {
26-
java.nio.file.Paths.get(path).toFile
36+
java.nio.file.Paths.get(intern(path)).toFile
2737
}
2838

2939
def fromStampType(stampType: schema.Stamps.StampType): Stamp = {
@@ -117,7 +127,7 @@ final class ProtobufReaders(mapper: ReadMapper) {
117127
def fromPosition(position: schema.Position): Position = {
118128
import ProtobufDefaults.{ MissingString, MissingInt }
119129
def fromString(value: String): Option[String] =
120-
if (value == MissingString) None else Some(value)
130+
if (value == MissingString) None else Some(intern(value))
121131
def fromInt(value: Int): Option[Integer] =
122132
if (value == MissingInt) None else Some(value)
123133
InterfaceUtil.position(
@@ -151,7 +161,7 @@ final class ProtobufReaders(mapper: ReadMapper) {
151161
}
152162

153163
def fromSourceInfo(sourceInfo: schema.SourceInfo): SourceInfo = {
154-
val mainClasses = sourceInfo.mainClasses
164+
val mainClasses = sourceInfo.mainClasses.map(intern)
155165
val reportedProblems = sourceInfo.reportedProblems.map(fromProblem)
156166
val unreportedProblems = sourceInfo.unreportedProblems.map(fromProblem)
157167
SourceInfos.makeInfo(reported = reportedProblems,
@@ -240,7 +250,7 @@ final class ProtobufReaders(mapper: ReadMapper) {
240250
import SchemaPath.{ Component => SchemaComponent }
241251
import Classes.{ Component, PathComponent }
242252
pathComponent.component match {
243-
case SchemaComponent.Id(c) => Id.of(c.id)
253+
case SchemaComponent.Id(c) => Id.of(intern(c.id))
244254
case SchemaComponent.Super(c) =>
245255
Super.of(c.qualifier.read(fromPath, ExpectedPathInSuper))
246256
case SchemaComponent.This(_) => ReadersConstants.This
@@ -253,8 +263,8 @@ final class ProtobufReaders(mapper: ReadMapper) {
253263

254264
def fromAnnotation(annotation: schema.Annotation): Annotation = {
255265
def fromAnnotationArgument(argument: schema.AnnotationArgument): AnnotationArgument = {
256-
val name = argument.name
257-
val value = argument.value
266+
val name = intern(argument.name)
267+
val value = intern(argument.value)
258268
AnnotationArgument.of(name, value)
259269
}
260270

@@ -274,7 +284,7 @@ final class ProtobufReaders(mapper: ReadMapper) {
274284
def fromType(`type`: schema.Type): Type = {
275285
import ReadersFeedback.expectedBaseIn
276286
def fromParameterRef(tpe: schema.Type.ParameterRef): ParameterRef = {
277-
ParameterRef.of(tpe.id)
287+
ParameterRef.of(intern(tpe.id))
278288
}
279289

280290
def fromParameterized(tpe: schema.Type.Parameterized): Parameterized = {
@@ -291,7 +301,7 @@ final class ProtobufReaders(mapper: ReadMapper) {
291301

292302
def fromConstant(tpe: schema.Type.Constant): Constant = {
293303
val baseType = tpe.baseType.read(fromType, expectedBaseIn(Classes.Constant))
294-
val value = tpe.value
304+
val value = intern(tpe.value)
295305
Constant.of(baseType, value)
296306
}
297307

@@ -307,7 +317,7 @@ final class ProtobufReaders(mapper: ReadMapper) {
307317
}
308318

309319
def fromProjection(tpe: schema.Type.Projection): Projection = {
310-
val id = tpe.id
320+
val id = intern(tpe.id)
311321
val prefix = tpe.prefix.read(fromType, ReadersFeedback.ExpectedPrefixInProjection)
312322
Projection.of(prefix, id)
313323
}
@@ -340,7 +350,7 @@ final class ProtobufReaders(mapper: ReadMapper) {
340350
def fromQualifier(qualifier: schema.Qualifier): Qualifier = {
341351
import schema.Qualifier.{ Type => QualifierType }
342352
qualifier.`type` match {
343-
case QualifierType.IdQualifier(q) => IdQualifier.of(q.value)
353+
case QualifierType.IdQualifier(q) => IdQualifier.of(intern(q.value))
344354
case QualifierType.ThisQualifier(_) => ReadersConstants.ThisQualifier
345355
case QualifierType.Unqualified(_) => ReadersConstants.Unqualified
346356
case QualifierType.Empty => ReadersFeedback.ExpectedNonEmptyQualifier.!!
@@ -376,7 +386,7 @@ final class ProtobufReaders(mapper: ReadMapper) {
376386
ExpectedUpperBoundInTypeDeclaration
377387
}
378388

379-
val name = classDefinition.name
389+
val name = intern(classDefinition.name)
380390
val access = classDefinition.access.read(fromAccess, MissingAccessInDef)
381391
val modifiers = classDefinition.modifiers.read(fromModifiers, MissingModifiersInDef)
382392
val annotations = classDefinition.annotations.toZincArray(fromAnnotation)
@@ -392,7 +402,7 @@ final class ProtobufReaders(mapper: ReadMapper) {
392402
ReadersFeedback.UnrecognizedParamModifier.!!
393403
}
394404
}
395-
val name = methodParameter.name
405+
val name = intern(methodParameter.name)
396406
val hasDefault = methodParameter.hasDefault
397407
val `type` = methodParameter.`type`.read(fromType, expectedTypeIn(Classes.MethodParameter))
398408
val modifier = fromParameterModifier(methodParameter.modifier)
@@ -463,7 +473,7 @@ final class ProtobufReaders(mapper: ReadMapper) {
463473
}
464474

465475
import ReadersFeedback.{ ExpectedLowerBoundInTypeParameter, ExpectedUpperBoundInTypeParameter }
466-
val id = typeParameter.id
476+
val id = intern(typeParameter.id)
467477
val annotations = typeParameter.annotations.toZincArray(fromAnnotation)
468478
val typeParameters = typeParameter.typeParameters.toZincArray(fromTypeParameter)
469479
val variance = fromVariance(typeParameter.variance)
@@ -475,7 +485,7 @@ final class ProtobufReaders(mapper: ReadMapper) {
475485
def fromClassLike(classLike: schema.ClassLike): ClassLike = {
476486
def expectedMsg(msg: String) = ReadersFeedback.expected(msg, Classes.ClassLike)
477487
def expected(clazz: Class[_]) = expectedMsg(clazz.getName)
478-
val name = classLike.name
488+
val name = intern(classLike.name)
479489
val access = classLike.access.read(fromAccess, expected(Classes.Access))
480490
val modifiers = classLike.modifiers.read(fromModifiers, expected(Classes.Modifiers))
481491
val annotations = classLike.annotations.toZincArray(fromAnnotation)
@@ -484,7 +494,7 @@ final class ProtobufReaders(mapper: ReadMapper) {
484494
val definitionType = fromDefinitionType(classLike.definitionType)
485495
val selfType = mkLazy(classLike.selfType.read(fromType, expectedMsg("self type")))
486496
val structure = mkLazy(classLike.structure.read(fromStructure, expected(Classes.Structure)))
487-
val savedAnnotations = classLike.savedAnnotations.toArray
497+
val savedAnnotations = classLike.savedAnnotations.map(intern).toArray
488498
val childrenOfSealedClass = classLike.childrenOfSealedClass.toZincArray(fromType)
489499
val topLevel = classLike.topLevel
490500
val typeParameters = classLike.typeParameters.toZincArray(fromTypeParameter)
@@ -521,7 +531,7 @@ final class ProtobufReaders(mapper: ReadMapper) {
521531
}
522532

523533
def fromNameHash(nameHash: schema.NameHash): NameHash = {
524-
val name = nameHash.name
534+
val name = intern(nameHash.name)
525535
val hash = nameHash.hash
526536
val scope = fromUseScope(nameHash.scope)
527537
NameHash.of(name, scope, hash)
@@ -530,15 +540,15 @@ final class ProtobufReaders(mapper: ReadMapper) {
530540
import SafeLazyProxy.{ strict => mkLazy }
531541
import ReadersFeedback.ExpectedCompanionsInAnalyzedClass
532542
val compilationTimestamp = analyzedClass.compilationTimestamp
533-
val name = analyzedClass.name
543+
val name = intern(analyzedClass.name)
534544
val api = mkLazy(analyzedClass.api.read(fromCompanions, ExpectedCompanionsInAnalyzedClass))
535545
val apiHash = analyzedClass.apiHash
536546
val nameHashes = analyzedClass.nameHashes.toZincArray(fromNameHash)
537547
val hasMacro = analyzedClass.hasMacro
538548
AnalyzedClass.of(compilationTimestamp, name, api, apiHash, nameHashes, hasMacro)
539549
}
540550

541-
private final val stringId = identity[String] _
551+
private final val stringId = intern _
542552
private final val stringToFile = (path: String) => fromPathString(path)
543553
def fromRelations(relations: schema.Relations): Relations = {
544554
def fromMap[K, V](map: Map[String, schema.Values],
@@ -559,7 +569,7 @@ final class ProtobufReaders(mapper: ReadMapper) {
559569
}
560570

561571
def fromUsedName(usedName: schema.UsedName): UsedName = {
562-
val name = usedName.name
572+
val name = intern(usedName.name)
563573
val scopes = usedName.scopes.iterator.map(fromUseScope).toIterable
564574
UsedName.apply(name, scopes)
565575
}
@@ -608,8 +618,8 @@ final class ProtobufReaders(mapper: ReadMapper) {
608618
}
609619

610620
def fromApis(apis: schema.APIs): APIs = {
611-
val internal = apis.internal.mapValues(fromAnalyzedClass)
612-
val external = apis.external.mapValues(fromAnalyzedClass)
621+
val internal = apis.internal.map { case (k, v) => intern(k) -> fromAnalyzedClass(v) }
622+
val external = apis.external.map { case (k, v) => intern(k) -> fromAnalyzedClass(v) }
613623
APIs(internal = internal, external = external)
614624
}
615625

0 commit comments

Comments
 (0)