Add context argument to ObjectCodec.{serialize,deserialize}

Context implementations are currently empty, just doing the plumbing in this
change. Once this is in we can start passing along the ObjectCodecRegistry, which
will allow runtime codec resolution for classes not known at compile time.
We'll also inevitably add some memoization helpers, allowing us to optimize the
serialization process further.

PiperOrigin-RevId: 185305674
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java b/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java
index 562a208..abeec41 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java
@@ -53,8 +53,10 @@
 import com.google.devtools.build.lib.events.EventHandler;
 import com.google.devtools.build.lib.packages.RuleClassProvider;
 import com.google.devtools.build.lib.packages.Target;
+import com.google.devtools.build.lib.skyframe.serialization.DeserializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.InjectingObjectCodec;
 import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec;
+import com.google.devtools.build.lib.skyframe.serialization.SerializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.SerializationException;
 import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
 import com.google.devtools.build.lib.skyframe.serialization.strings.StringCodecs;
@@ -2189,30 +2191,35 @@
 
     @Override
     public void serialize(
-        FileSystemProvider fsProvider, BuildConfiguration obj, CodedOutputStream codedOut)
+        FileSystemProvider fsProvider,
+        SerializationContext context,
+        BuildConfiguration obj,
+        CodedOutputStream codedOut)
         throws SerializationException, IOException {
-      BlazeDirectories.CODEC.serialize(fsProvider, obj.directories, codedOut);
+      BlazeDirectories.CODEC.serialize(fsProvider, context, obj.directories, codedOut);
       codedOut.writeInt32NoTag(obj.fragments.size());
       for (Fragment fragment : obj.fragments.values()) {
-        Fragment.CODEC.serialize(fsProvider, fragment, codedOut);
+        Fragment.CODEC.serialize(fsProvider, context, fragment, codedOut);
       }
-      BuildOptions.CODEC.serialize(obj.buildOptions, codedOut);
-      StringCodecs.asciiOptimized().serialize(obj.repositoryName, codedOut);
+      BuildOptions.CODEC.serialize(context, obj.buildOptions, codedOut);
+      StringCodecs.asciiOptimized().serialize(context, obj.repositoryName, codedOut);
     }
 
     @Override
-    public BuildConfiguration deserialize(FileSystemProvider fsProvider, CodedInputStream codedIn)
+    public BuildConfiguration deserialize(
+        FileSystemProvider fsProvider, DeserializationContext context, CodedInputStream codedIn)
         throws SerializationException, IOException {
-      BlazeDirectories blazeDirectories = BlazeDirectories.CODEC.deserialize(fsProvider, codedIn);
+      BlazeDirectories blazeDirectories =
+          BlazeDirectories.CODEC.deserialize(fsProvider, context, codedIn);
       int length = codedIn.readInt32();
       ImmutableSortedMap.Builder<Class<? extends Fragment>, Fragment> builder =
           new ImmutableSortedMap.Builder<>(lexicalFragmentSorter);
       for (int i = 0; i < length; ++i) {
-        Fragment fragment = Fragment.CODEC.deserialize(fsProvider, codedIn);
+        Fragment fragment = Fragment.CODEC.deserialize(fsProvider, context, codedIn);
         builder.put(fragment.getClass(), fragment);
       }
-      BuildOptions options = BuildOptions.CODEC.deserialize(codedIn);
-      String repositoryName = StringCodecs.asciiOptimized().deserialize(codedIn);
+      BuildOptions options = BuildOptions.CODEC.deserialize(context, codedIn);
+      String repositoryName = StringCodecs.asciiOptimized().deserialize(context, codedIn);
       return new BuildConfiguration(blazeDirectories, builder.build(), options, repositoryName);
     }
   }
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/config/BuildOptions.java b/src/main/java/com/google/devtools/build/lib/analysis/config/BuildOptions.java
index b1944f0..4a89121 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/config/BuildOptions.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/config/BuildOptions.java
@@ -22,7 +22,9 @@
 import com.google.common.collect.ImmutableSortedMap;
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.InvocationPolicy;
+import com.google.devtools.build.lib.skyframe.serialization.DeserializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec;
+import com.google.devtools.build.lib.skyframe.serialization.SerializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.SerializationException;
 import com.google.devtools.build.lib.util.Fingerprint;
 import com.google.devtools.common.options.InvocationPolicyEnforcer;
@@ -337,22 +339,23 @@
     }
 
     @Override
-    public void serialize(BuildOptions buildOptions, CodedOutputStream codedOut)
+    public void serialize(
+        SerializationContext context, BuildOptions buildOptions, CodedOutputStream codedOut)
         throws IOException, SerializationException {
       Collection<FragmentOptions> fragmentOptions = buildOptions.getOptions();
       codedOut.writeInt32NoTag(fragmentOptions.size());
       for (FragmentOptions options : buildOptions.getOptions()) {
-        FragmentOptions.CODEC.serialize(options, codedOut);
+        FragmentOptions.CODEC.serialize(context, options, codedOut);
       }
     }
 
     @Override
-    public BuildOptions deserialize(CodedInputStream codedIn)
+    public BuildOptions deserialize(DeserializationContext context, CodedInputStream codedIn)
         throws IOException, SerializationException {
       BuildOptions.Builder builder = BuildOptions.builder();
       int length = codedIn.readInt32();
       for (int i = 0; i < length; ++i) {
-        builder.add(FragmentOptions.CODEC.deserialize(codedIn));
+        builder.add(FragmentOptions.CODEC.deserialize(context, codedIn));
       }
       return builder.build();
     }
diff --git a/src/main/java/com/google/devtools/build/lib/cmdline/LabelCodec.java b/src/main/java/com/google/devtools/build/lib/cmdline/LabelCodec.java
index cda83db..4153d5e 100644
--- a/src/main/java/com/google/devtools/build/lib/cmdline/LabelCodec.java
+++ b/src/main/java/com/google/devtools/build/lib/cmdline/LabelCodec.java
@@ -14,7 +14,9 @@
 
 package com.google.devtools.build.lib.cmdline;
 
+import com.google.devtools.build.lib.skyframe.serialization.DeserializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec;
+import com.google.devtools.build.lib.skyframe.serialization.SerializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.SerializationException;
 import com.google.devtools.build.lib.skyframe.serialization.strings.StringCodecs;
 import com.google.protobuf.CodedInputStream;
@@ -35,16 +37,17 @@
   }
 
   @Override
-  public void serialize(Label label, CodedOutputStream codedOut)
+  public void serialize(SerializationContext context, Label label, CodedOutputStream codedOut)
       throws IOException, SerializationException {
-    packageIdCodec.serialize(label.getPackageIdentifier(), codedOut);
-    stringCodec.serialize(label.getName(), codedOut);
+    packageIdCodec.serialize(context, label.getPackageIdentifier(), codedOut);
+    stringCodec.serialize(context, label.getName(), codedOut);
   }
 
   @Override
-  public Label deserialize(CodedInputStream codedIn) throws SerializationException, IOException {
-    PackageIdentifier packageId = packageIdCodec.deserialize(codedIn);
-    String name = stringCodec.deserialize(codedIn);
+  public Label deserialize(DeserializationContext context, CodedInputStream codedIn)
+      throws SerializationException, IOException {
+    PackageIdentifier packageId = packageIdCodec.deserialize(context, codedIn);
+    String name = stringCodec.deserialize(context, codedIn);
     return Label.createUnvalidated(packageId, name);
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/cmdline/PackageIdentifierCodec.java b/src/main/java/com/google/devtools/build/lib/cmdline/PackageIdentifierCodec.java
index dc94ea8..1dcb78f 100644
--- a/src/main/java/com/google/devtools/build/lib/cmdline/PackageIdentifierCodec.java
+++ b/src/main/java/com/google/devtools/build/lib/cmdline/PackageIdentifierCodec.java
@@ -14,7 +14,9 @@
 
 package com.google.devtools.build.lib.cmdline;
 
+import com.google.devtools.build.lib.skyframe.serialization.DeserializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec;
+import com.google.devtools.build.lib.skyframe.serialization.SerializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.SerializationException;
 import com.google.devtools.build.lib.vfs.PathFragment;
 import com.google.protobuf.CodedInputStream;
@@ -32,17 +34,18 @@
   }
 
   @Override
-  public void serialize(PackageIdentifier pkgId, CodedOutputStream codedOut)
+  public void serialize(
+      SerializationContext context, PackageIdentifier pkgId, CodedOutputStream codedOut)
       throws IOException, SerializationException {
-    repoNameCodec.serialize(pkgId.getRepository(), codedOut);
-    PathFragment.CODEC.serialize(pkgId.getPackageFragment(), codedOut);
+    repoNameCodec.serialize(context, pkgId.getRepository(), codedOut);
+    PathFragment.CODEC.serialize(context, pkgId.getPackageFragment(), codedOut);
   }
 
   @Override
-  public PackageIdentifier deserialize(CodedInputStream codedIn)
+  public PackageIdentifier deserialize(DeserializationContext context, CodedInputStream codedIn)
       throws IOException, SerializationException {
-    RepositoryName repoName = repoNameCodec.deserialize(codedIn);
-    PathFragment pathFragment = PathFragment.CODEC.deserialize(codedIn);
+    RepositoryName repoName = repoNameCodec.deserialize(context, codedIn);
+    PathFragment pathFragment = PathFragment.CODEC.deserialize(context, codedIn);
     return PackageIdentifier.create(repoName, pathFragment);
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/cmdline/RepositoryNameCodec.java b/src/main/java/com/google/devtools/build/lib/cmdline/RepositoryNameCodec.java
index 67a6c55..4949b2f 100644
--- a/src/main/java/com/google/devtools/build/lib/cmdline/RepositoryNameCodec.java
+++ b/src/main/java/com/google/devtools/build/lib/cmdline/RepositoryNameCodec.java
@@ -14,7 +14,9 @@
 
 package com.google.devtools.build.lib.cmdline;
 
+import com.google.devtools.build.lib.skyframe.serialization.DeserializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec;
+import com.google.devtools.build.lib.skyframe.serialization.SerializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.SerializationException;
 import com.google.protobuf.ByteString;
 import com.google.protobuf.CodedInputStream;
@@ -30,7 +32,9 @@
   }
 
   @Override
-  public void serialize(RepositoryName repoName, CodedOutputStream codedOut) throws IOException {
+  public void serialize(
+      SerializationContext context, RepositoryName repoName, CodedOutputStream codedOut)
+      throws IOException {
     boolean isMain = repoName.isMain();
     // Main is by far the most common. Use boolean to short-circuit string encoding on
     // serialization and byte[]/ByteString creation on deserialization.
@@ -41,7 +45,7 @@
   }
 
   @Override
-  public RepositoryName deserialize(CodedInputStream codedIn)
+  public RepositoryName deserialize(DeserializationContext context, CodedInputStream codedIn)
       throws SerializationException, IOException {
     boolean isMain = codedIn.readBool();
     if (isMain) {
diff --git a/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSetCodec.java b/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSetCodec.java
index 072b936..475e93b 100644
--- a/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSetCodec.java
+++ b/src/main/java/com/google/devtools/build/lib/collect/nestedset/NestedSetCodec.java
@@ -16,10 +16,12 @@
 import com.google.common.base.Preconditions;
 import com.google.common.hash.Hashing;
 import com.google.common.hash.HashingOutputStream;
+import com.google.devtools.build.lib.skyframe.serialization.DeserializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.EnumCodec;
 import com.google.devtools.build.lib.skyframe.serialization.InjectingObjectCodec;
 import com.google.devtools.build.lib.skyframe.serialization.InjectingObjectCodecAdapter;
 import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec;
+import com.google.devtools.build.lib.skyframe.serialization.SerializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.SerializationException;
 import com.google.protobuf.ByteString;
 import com.google.protobuf.CodedInputStream;
@@ -61,20 +63,20 @@
   }
 
   @Override
-  public void serialize(NestedSet<T> obj, CodedOutputStream codedOut)
+  public void serialize(SerializationContext context, NestedSet<T> obj, CodedOutputStream codedOut)
       throws SerializationException, IOException {
     // Topo sort the nested set to ensure digests are available for children at time of writing
     Collection<Object> topoSortedChildren = getTopologicallySortedChildren(obj);
     Map<Object, byte[]> childToDigest = new IdentityHashMap<>();
     codedOut.writeInt32NoTag(topoSortedChildren.size());
-    orderCodec.serialize(obj.getOrder(), codedOut);
+    orderCodec.serialize(context, obj.getOrder(), codedOut);
     for (Object children : topoSortedChildren) {
-      serializeOneNestedSet(children, codedOut, childToDigest);
+      serializeOneNestedSet(context, children, codedOut, childToDigest);
     }
   }
 
   @Override
-  public NestedSet<T> deserialize(CodedInputStream codedIn)
+  public NestedSet<T> deserialize(DeserializationContext context, CodedInputStream codedIn)
       throws SerializationException, IOException {
     Map<ByteString, Object> digestToChild = new HashMap<>();
     int nestedSetCount = codedIn.readInt32();
@@ -82,17 +84,20 @@
         nestedSetCount >= 1,
         "Should have at least serialized one nested set, got: %d",
         nestedSetCount);
-    Order order = orderCodec.deserialize(codedIn);
+    Order order = orderCodec.deserialize(context, codedIn);
     Object children = null;
     for (int i = 0; i < nestedSetCount; ++i) {
       // Update var, the last one in the list is the top level nested set
-      children = deserializeOneNestedSet(codedIn, digestToChild);
+      children = deserializeOneNestedSet(context, codedIn, digestToChild);
     }
     return createNestedSet(order, children);
   }
 
   private void serializeOneNestedSet(
-      Object children, CodedOutputStream codedOut, Map<Object, byte[]> childToDigest)
+      SerializationContext context,
+      Object children,
+      CodedOutputStream codedOut,
+      Map<Object, byte[]> childToDigest)
       throws IOException, SerializationException {
     // Serialize nested set into an inner byte array so we can take its digest
     ByteArrayOutputStream childOutputStream = new ByteArrayOutputStream();
@@ -100,9 +105,9 @@
         new HashingOutputStream(Hashing.md5(), childOutputStream);
     CodedOutputStream childCodedOut = CodedOutputStream.newInstance(hashingOutputStream);
     if (children instanceof Object[]) {
-      serializeMultiItemChildArray((Object[]) children, childToDigest, childCodedOut);
+      serializeMultiItemChildArray(context, (Object[]) children, childToDigest, childCodedOut);
     } else if (children != NestedSet.EMPTY_CHILDREN) {
-      serializeSingleItemChildArray(children, childCodedOut);
+      serializeSingleItemChildArray(context, children, childCodedOut);
     } else {
       // Empty set
       childCodedOut.writeInt32NoTag(0);
@@ -116,7 +121,10 @@
   }
 
   private void serializeMultiItemChildArray(
-      Object[] children, Map<Object, byte[]> childToDigest, CodedOutputStream childCodedOut)
+      SerializationContext context,
+      Object[] children,
+      Map<Object, byte[]> childToDigest,
+      CodedOutputStream childCodedOut)
       throws IOException, SerializationException {
     childCodedOut.writeInt32NoTag(children.length);
     for (Object child : children) {
@@ -129,20 +137,23 @@
         childCodedOut.writeByteArrayNoTag(digest);
       } else {
         childCodedOut.writeBoolNoTag(false);
-        objectCodec.serialize(cast(child), childCodedOut);
+        objectCodec.serialize(context, cast(child), childCodedOut);
       }
     }
   }
 
-  private void serializeSingleItemChildArray(Object children, CodedOutputStream childCodedOut)
+  private void serializeSingleItemChildArray(
+      SerializationContext context, Object children, CodedOutputStream childCodedOut)
       throws IOException, SerializationException {
     childCodedOut.writeInt32NoTag(1);
     T singleChild = cast(children);
-    objectCodec.serialize(singleChild, childCodedOut);
+    objectCodec.serialize(context, singleChild, childCodedOut);
   }
 
   private Object deserializeOneNestedSet(
-      CodedInputStream codedIn, Map<ByteString, Object> digestToChild)
+      DeserializationContext context,
+      CodedInputStream codedIn,
+      Map<ByteString, Object> digestToChild)
       throws SerializationException, IOException {
     ByteString digest = codedIn.readBytes();
     CodedInputStream childCodedIn = codedIn.readBytes().newCodedInput();
@@ -150,9 +161,9 @@
     int childCount = childCodedIn.readInt32();
     final Object result;
     if (childCount > 1) {
-      result = deserializeMultipleItemChildArray(digestToChild, childCodedIn, childCount);
+      result = deserializeMultipleItemChildArray(context, digestToChild, childCodedIn, childCount);
     } else if (childCount == 1) {
-      result = objectCodec.deserialize(childCodedIn);
+      result = objectCodec.deserialize(context, childCodedIn);
     } else {
       result = NestedSet.EMPTY_CHILDREN;
     }
@@ -161,7 +172,10 @@
   }
 
   private Object deserializeMultipleItemChildArray(
-      Map<ByteString, Object> digestToChild, CodedInputStream childCodedIn, int childCount)
+      DeserializationContext context,
+      Map<ByteString, Object> digestToChild,
+      CodedInputStream childCodedIn,
+      int childCount)
       throws IOException, SerializationException {
     Object[] children = new Object[childCount];
     for (int i = 0; i < childCount; ++i) {
@@ -171,7 +185,7 @@
         children[i] =
             Preconditions.checkNotNull(digestToChild.get(digest), "Transitive nested set missing");
       } else {
-        children[i] = objectCodec.deserialize(childCodedIn);
+        children[i] = objectCodec.deserialize(context, childCodedIn);
       }
     }
     return children;
diff --git a/src/main/java/com/google/devtools/build/lib/packages/Package.java b/src/main/java/com/google/devtools/build/lib/packages/Package.java
index 8a1f02a..1d7ee3a 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/Package.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/Package.java
@@ -1584,13 +1584,19 @@
 
     @Override
     public void serialize(
-        PackageCodecDependencies codecDeps, Package input, CodedOutputStream codedOut)
+        PackageCodecDependencies codecDeps,
+        com.google.devtools.build.lib.skyframe.serialization.SerializationContext context,
+        Package input,
+        CodedOutputStream codedOut)
         throws IOException, SerializationException {
       codecDeps.getPackageSerializer().serialize(input, codedOut);
     }
 
     @Override
-    public Package deserialize(PackageCodecDependencies codecDeps, CodedInputStream codedIn)
+    public Package deserialize(
+        PackageCodecDependencies codecDeps,
+        com.google.devtools.build.lib.skyframe.serialization.DeserializationContext context,
+        CodedInputStream codedIn)
         throws SerializationException, IOException {
       try {
         return codecDeps.getPackageDeserializer().deserialize(codedIn);
diff --git a/src/main/java/com/google/devtools/build/lib/packages/SkylarkSemanticsCodec.java b/src/main/java/com/google/devtools/build/lib/packages/SkylarkSemanticsCodec.java
index fe7e4d9..249bc91 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/SkylarkSemanticsCodec.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/SkylarkSemanticsCodec.java
@@ -14,7 +14,9 @@
 
 package com.google.devtools.build.lib.packages;
 
+import com.google.devtools.build.lib.skyframe.serialization.DeserializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec;
+import com.google.devtools.build.lib.skyframe.serialization.SerializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.SerializationException;
 import com.google.devtools.build.lib.syntax.SkylarkSemantics;
 import com.google.protobuf.CodedInputStream;
@@ -37,7 +39,8 @@
   }
 
   @Override
-  public void serialize(SkylarkSemantics semantics, CodedOutputStream codedOut)
+  public void serialize(
+      SerializationContext context, SkylarkSemantics semantics, CodedOutputStream codedOut)
       throws SerializationException, IOException {
     // <== Add new options here in alphabetic order ==>
     codedOut.writeBoolNoTag(semantics.incompatibleBzlDisallowLoadAfterStatement());
@@ -59,7 +62,7 @@
   }
 
   @Override
-  public SkylarkSemantics deserialize(CodedInputStream codedIn)
+  public SkylarkSemantics deserialize(DeserializationContext context, CodedInputStream codedIn)
       throws SerializationException, IOException {
     SkylarkSemantics.Builder builder = SkylarkSemantics.builder();
 
diff --git a/src/main/java/com/google/devtools/build/lib/rules/apple/AppleCommandLineOptions.java b/src/main/java/com/google/devtools/build/lib/rules/apple/AppleCommandLineOptions.java
index 3bf0afc..bd7a0b7 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/apple/AppleCommandLineOptions.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/apple/AppleCommandLineOptions.java
@@ -24,8 +24,10 @@
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
 import com.google.devtools.build.lib.rules.apple.AppleConfiguration.ConfigurationDistinguisher;
 import com.google.devtools.build.lib.rules.apple.ApplePlatform.PlatformType;
+import com.google.devtools.build.lib.skyframe.serialization.DeserializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.EnumCodec;
 import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec;
+import com.google.devtools.build.lib.skyframe.serialization.SerializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.SerializationException;
 import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
@@ -508,13 +510,14 @@
     return host;
   }
 
-  void serialize(CodedOutputStream out) throws IOException, SerializationException {
-    CODEC.serialize(this, out);
+  void serialize(SerializationContext context, CodedOutputStream out)
+      throws IOException, SerializationException {
+    CODEC.serialize(context, this, out);
   }
 
-  static AppleCommandLineOptions deserialize(CodedInputStream in)
+  static AppleCommandLineOptions deserialize(DeserializationContext context, CodedInputStream in)
       throws IOException, SerializationException {
-    return CODEC.deserialize(in);
+    return CODEC.deserialize(context, in);
   }
 
   /** Converter for the Apple configuration distinguisher. */
diff --git a/src/main/java/com/google/devtools/build/lib/rules/apple/DottedVersion.java b/src/main/java/com/google/devtools/build/lib/rules/apple/DottedVersion.java
index 569c8e1..12e236c 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/apple/DottedVersion.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/apple/DottedVersion.java
@@ -21,7 +21,9 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Ordering;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
+import com.google.devtools.build.lib.skyframe.serialization.DeserializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec;
+import com.google.devtools.build.lib.skyframe.serialization.SerializationContext;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory;
@@ -161,9 +163,12 @@
   }
 
   @Override
-  @SkylarkCallable(name = "compare_to", 
-    doc = "Compares based on most signifigant (first) not-matching version component. "
-        + "So, for example, 1.2.3 < 1.2.4")
+  @SkylarkCallable(
+    name = "compare_to",
+    doc =
+        "Compares based on most signifigant (first) not-matching version component. "
+            + "So, for example, 1.2.3 < 1.2.4"
+  )
   public int compareTo(DottedVersion other) {
     int maxComponents = Math.max(components.size(), other.components.size());
     for (int componentIndex = 0; componentIndex < maxComponents; componentIndex++) {
@@ -180,16 +185,16 @@
   /**
    * Returns the string representation of this dotted version, padded to a minimum number of
    * components if the string representation does not already contain that many components.
-   * 
+   *
    * <p>For example, a dotted version of "7.3" will return "7.3" with either one or two components
    * requested, "7.3.0" if three are requested, and "7.3.0.0" if four are requested.
-   * 
-   * <p>Trailing zero components at the end of a string representation will not be removed. For
-   * example, a dotted version of "1.0.0" will return "1.0.0" if only one or two components
-   * are requested.
    *
-   * @param numMinComponents the minimum number of dot-separated numbers that should be present
-   *     in the returned string representation
+   * <p>Trailing zero components at the end of a string representation will not be removed. For
+   * example, a dotted version of "1.0.0" will return "1.0.0" if only one or two components are
+   * requested.
+   *
+   * @param numMinComponents the minimum number of dot-separated numbers that should be present in
+   *     the returned string representation
    */
   public String toStringWithMinimumComponents(int numMinComponents) {
     ImmutableList.Builder<Component> stringComponents = ImmutableList.builder();
@@ -260,7 +265,9 @@
   public static final ObjectCodec<DottedVersion> CODEC =
       new ObjectCodec<DottedVersion>() {
         @Override
-        public void serialize(DottedVersion obj, CodedOutputStream codedOut) throws IOException {
+        public void serialize(
+            SerializationContext context, DottedVersion obj, CodedOutputStream codedOut)
+            throws IOException {
           codedOut.writeInt32NoTag(obj.components.size());
           for (Component component : obj.components) {
             component.serialize(codedOut);
@@ -270,7 +277,8 @@
         }
 
         @Override
-        public DottedVersion deserialize(CodedInputStream codedIn) throws IOException {
+        public DottedVersion deserialize(DeserializationContext context, CodedInputStream codedIn)
+            throws IOException {
           int numComponents = codedIn.readInt32();
           // TODO(janakr: Presize this if/when https://github.com/google/guava/issues/196 is
           // resolved.
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/AspectValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/AspectValue.java
index 0aca54b..cf12d62 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/AspectValue.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/AspectValue.java
@@ -35,8 +35,10 @@
 import com.google.devtools.build.lib.packages.Package;
 import com.google.devtools.build.lib.skyframe.BuildConfigurationValue.Key;
 import com.google.devtools.build.lib.skyframe.ConfiguredTargetKey.KeyAndHost;
+import com.google.devtools.build.lib.skyframe.serialization.DeserializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.ImmutableListCodec;
 import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec;
+import com.google.devtools.build.lib.skyframe.serialization.SerializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.SerializationException;
 import com.google.devtools.build.lib.syntax.SkylarkImport;
 import com.google.devtools.build.skyframe.SkyFunctionName;
@@ -277,25 +279,25 @@
     }
 
     @Override
-    public void serialize(AspectKey obj, CodedOutputStream codedOut)
+    public void serialize(SerializationContext context, AspectKey obj, CodedOutputStream codedOut)
         throws SerializationException, IOException {
-      Label.CODEC.serialize(obj.label, codedOut);
-      ConfiguredTargetKey.CODEC.serialize(obj.baseConfiguredTargetKey, codedOut);
-      listCodec.serialize(obj.baseKeys, codedOut);
-      AspectDescriptor.CODEC.serialize(obj.aspectDescriptor, codedOut);
-      Key.CODEC.serialize(obj.aspectConfigurationKey, codedOut);
+      Label.CODEC.serialize(context, obj.label, codedOut);
+      ConfiguredTargetKey.CODEC.serialize(context, obj.baseConfiguredTargetKey, codedOut);
+      listCodec.serialize(context, obj.baseKeys, codedOut);
+      AspectDescriptor.CODEC.serialize(context, obj.aspectDescriptor, codedOut);
+      Key.CODEC.serialize(context, obj.aspectConfigurationKey, codedOut);
       codedOut.writeBoolNoTag(obj.aspectConfigurationIsHost());
     }
 
     @Override
-    public AspectKey deserialize(CodedInputStream codedIn)
+    public AspectKey deserialize(DeserializationContext context, CodedInputStream codedIn)
         throws SerializationException, IOException {
       return createAspectKey(
-          Label.CODEC.deserialize(codedIn),
-          ConfiguredTargetKey.CODEC.deserialize(codedIn),
-          listCodec.deserialize(codedIn),
-          AspectDescriptor.CODEC.deserialize(codedIn),
-          Key.CODEC.deserialize(codedIn),
+          Label.CODEC.deserialize(context, codedIn),
+          ConfiguredTargetKey.CODEC.deserialize(context, codedIn),
+          listCodec.deserialize(context, codedIn),
+          AspectDescriptor.CODEC.deserialize(context, codedIn),
+          Key.CODEC.deserialize(context, codedIn),
           codedIn.readBool());
     }
   }
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/BuildConfigurationValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/BuildConfigurationValue.java
index da75352..76e8cbb 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/BuildConfigurationValue.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/BuildConfigurationValue.java
@@ -21,8 +21,10 @@
 import com.google.devtools.build.lib.analysis.config.FragmentClassSet;
 import com.google.devtools.build.lib.concurrent.BlazeInterners;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
+import com.google.devtools.build.lib.skyframe.serialization.DeserializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.InjectingObjectCodec;
 import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec;
+import com.google.devtools.build.lib.skyframe.serialization.SerializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.SerializationException;
 import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
 import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec.VisibleForSerialization;
@@ -134,20 +136,21 @@
       }
 
       @Override
-      public void serialize(Key obj, CodedOutputStream codedOut)
+      public void serialize(SerializationContext context, Key obj, CodedOutputStream codedOut)
           throws SerializationException, IOException {
-        BuildOptions.CODEC.serialize(obj.buildOptions, codedOut);
+        BuildOptions.CODEC.serialize(context, obj.buildOptions, codedOut);
         codedOut.writeInt32NoTag(obj.fragments.fragmentClasses().size());
         for (Class<? extends BuildConfiguration.Fragment> fragment :
             obj.fragments.fragmentClasses()) {
-          StringCodecs.asciiOptimized().serialize(fragment.getName(), codedOut);
+          StringCodecs.asciiOptimized().serialize(context, fragment.getName(), codedOut);
         }
       }
 
       @Override
       @SuppressWarnings("unchecked") // Class<? extends...> cast
-      public Key deserialize(CodedInputStream codedIn) throws SerializationException, IOException {
-        BuildOptions buildOptions = BuildOptions.CODEC.deserialize(codedIn);
+      public Key deserialize(DeserializationContext context, CodedInputStream codedIn)
+          throws SerializationException, IOException {
+        BuildOptions buildOptions = BuildOptions.CODEC.deserialize(context, codedIn);
         int fragmentsSize = codedIn.readInt32();
         ImmutableSortedSet.Builder<Class<? extends BuildConfiguration.Fragment>> fragmentsBuilder =
             ImmutableSortedSet.orderedBy(BuildConfiguration.lexicalFragmentSorter);
@@ -155,7 +158,7 @@
           try {
             fragmentsBuilder.add(
                 (Class<? extends BuildConfiguration.Fragment>)
-                    Class.forName(StringCodecs.asciiOptimized().deserialize(codedIn)));
+                    Class.forName(StringCodecs.asciiOptimized().deserialize(context, codedIn)));
           } catch (ClassNotFoundException e) {
             throw new SerializationException(
                 "Couldn't deserialize BuildConfigurationValue$Key fragment class", e);
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ConfigurationFragmentValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/ConfigurationFragmentValue.java
index 3ec99db..c81f29c 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ConfigurationFragmentValue.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ConfigurationFragmentValue.java
@@ -23,8 +23,10 @@
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
 import com.google.devtools.build.lib.packages.RuleClassProvider;
+import com.google.devtools.build.lib.skyframe.serialization.DeserializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.InjectingObjectCodec;
 import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec;
+import com.google.devtools.build.lib.skyframe.serialization.SerializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.SerializationException;
 import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
 import com.google.devtools.build.lib.skyframe.serialization.strings.StringCodecs;
@@ -137,22 +139,24 @@
       }
 
       @Override
-      public void serialize(ConfigurationFragmentKey obj, CodedOutputStream codedOut)
+      public void serialize(
+          SerializationContext context, ConfigurationFragmentKey obj, CodedOutputStream codedOut)
           throws SerializationException, IOException {
-        BuildOptions.CODEC.serialize(obj.buildOptions, codedOut);
-        StringCodecs.asciiOptimized().serialize(obj.fragmentType.getName(), codedOut);
+        BuildOptions.CODEC.serialize(context, obj.buildOptions, codedOut);
+        StringCodecs.asciiOptimized().serialize(context, obj.fragmentType.getName(), codedOut);
       }
 
       @SuppressWarnings("unchecked") // Cast to Class<? extends Fragment>.
       @Override
-      public ConfigurationFragmentKey deserialize(CodedInputStream codedIn)
+      public ConfigurationFragmentKey deserialize(
+          DeserializationContext context, CodedInputStream codedIn)
           throws SerializationException, IOException {
 
         try {
           return of(
-              BuildOptions.CODEC.deserialize(codedIn),
+              BuildOptions.CODEC.deserialize(context, codedIn),
               (Class<? extends Fragment>)
-                  Class.forName(StringCodecs.asciiOptimized().deserialize(codedIn)));
+                  Class.forName(StringCodecs.asciiOptimized().deserialize(context, codedIn)));
         } catch (ClassNotFoundException e) {
           throw new SerializationException("Couldn't deserialize ConfigurationFragmentKey", e);
         }
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetKey.java b/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetKey.java
index 166119f..5cdf67c 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetKey.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetKey.java
@@ -22,7 +22,9 @@
 import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.concurrent.BlazeInterners;
+import com.google.devtools.build.lib.skyframe.serialization.DeserializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec;
+import com.google.devtools.build.lib.skyframe.serialization.SerializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.SerializationException;
 import com.google.devtools.build.skyframe.SkyFunctionName;
 import com.google.protobuf.CodedInputStream;
@@ -211,24 +213,27 @@
     }
 
     @Override
-    public void serialize(ConfiguredTargetKey obj, CodedOutputStream codedOut)
+    public void serialize(
+        SerializationContext context, ConfiguredTargetKey obj, CodedOutputStream codedOut)
         throws SerializationException, IOException {
-      Label.CODEC.serialize(obj.label, codedOut);
+      Label.CODEC.serialize(context, obj.label, codedOut);
       if (obj.configurationKey == null) {
         codedOut.writeBoolNoTag(false);
       } else {
         codedOut.writeBoolNoTag(true);
-        BuildConfigurationValue.Key.CODEC.serialize(obj.configurationKey, codedOut);
+        BuildConfigurationValue.Key.CODEC.serialize(context, obj.configurationKey, codedOut);
       }
       codedOut.writeBoolNoTag(obj.isHostConfiguration());
     }
 
     @Override
-    public ConfiguredTargetKey deserialize(CodedInputStream codedIn)
+    public ConfiguredTargetKey deserialize(DeserializationContext context, CodedInputStream codedIn)
         throws SerializationException, IOException {
       return of(
-          Label.CODEC.deserialize(codedIn),
-          codedIn.readBool() ? BuildConfigurationValue.Key.CODEC.deserialize(codedIn) : null,
+          Label.CODEC.deserialize(context, codedIn),
+          codedIn.readBool()
+              ? BuildConfigurationValue.Key.CODEC.deserialize(context, codedIn)
+              : null,
           codedIn.readBool());
     }
   }
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/GlobDescriptor.java b/src/main/java/com/google/devtools/build/lib/skyframe/GlobDescriptor.java
index 53c1aad..01db160 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/GlobDescriptor.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/GlobDescriptor.java
@@ -19,7 +19,9 @@
 import com.google.devtools.build.lib.cmdline.PackageIdentifierCodec;
 import com.google.devtools.build.lib.concurrent.BlazeInterners;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
+import com.google.devtools.build.lib.skyframe.serialization.DeserializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec;
+import com.google.devtools.build.lib.skyframe.serialization.SerializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.SerializationException;
 import com.google.devtools.build.lib.skyframe.serialization.strings.StringCodecs;
 import com.google.devtools.build.lib.util.StringCanonicalizer;
@@ -181,22 +183,23 @@
     }
 
     @Override
-    public void serialize(GlobDescriptor globDesc, CodedOutputStream codedOut)
+    public void serialize(
+        SerializationContext context, GlobDescriptor globDesc, CodedOutputStream codedOut)
         throws IOException, SerializationException {
-      packageIdCodec.serialize(globDesc.getPackageId(), codedOut);
-      rootCodec.serialize(globDesc.getPackageRoot(), codedOut);
-      PathFragment.CODEC.serialize(globDesc.getSubdir(), codedOut);
-      stringCodec.serialize(globDesc.getPattern(), codedOut);
+      packageIdCodec.serialize(context, globDesc.getPackageId(), codedOut);
+      rootCodec.serialize(context, globDesc.getPackageRoot(), codedOut);
+      PathFragment.CODEC.serialize(context, globDesc.getSubdir(), codedOut);
+      stringCodec.serialize(context, globDesc.getPattern(), codedOut);
       codedOut.writeBoolNoTag(globDesc.excludeDirs());
     }
 
     @Override
-    public GlobDescriptor deserialize(CodedInputStream codedIn)
+    public GlobDescriptor deserialize(DeserializationContext context, CodedInputStream codedIn)
         throws SerializationException, IOException {
-      PackageIdentifier packageId = packageIdCodec.deserialize(codedIn);
-      Root packageRoot = rootCodec.deserialize(codedIn);
-      PathFragment pathFragment = PathFragment.CODEC.deserialize(codedIn);
-      String pattern = stringCodec.deserialize(codedIn);
+      PackageIdentifier packageId = packageIdCodec.deserialize(context, codedIn);
+      Root packageRoot = rootCodec.deserialize(context, codedIn);
+      PathFragment pathFragment = PathFragment.CODEC.deserialize(context, codedIn);
+      String pattern = stringCodec.deserialize(context, codedIn);
       boolean excludeDirs = codedIn.readBool();
       return GlobDescriptor.create(packageId, packageRoot, pathFragment, pattern, excludeDirs);
     }
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/PrecomputedValueCodec.java b/src/main/java/com/google/devtools/build/lib/skyframe/PrecomputedValueCodec.java
index 2128edb..1fc108d 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/PrecomputedValueCodec.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/PrecomputedValueCodec.java
@@ -15,8 +15,10 @@
 package com.google.devtools.build.lib.skyframe;
 
 import com.google.common.base.Preconditions;
+import com.google.devtools.build.lib.skyframe.serialization.DeserializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec;
 import com.google.devtools.build.lib.skyframe.serialization.ObjectCodecs;
+import com.google.devtools.build.lib.skyframe.serialization.SerializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.SerializationException;
 import com.google.protobuf.CodedInputStream;
 import com.google.protobuf.CodedOutputStream;
@@ -42,7 +44,8 @@
   }
 
   @Override
-  public void serialize(PrecomputedValue obj, CodedOutputStream codedOut)
+  public void serialize(
+      SerializationContext context, PrecomputedValue obj, CodedOutputStream codedOut)
       throws SerializationException, IOException {
     ObjectCodecs objectCodecs = objectCodecsSupplier.get();
     Object val = obj.get();
@@ -55,7 +58,7 @@
   }
 
   @Override
-  public PrecomputedValue deserialize(CodedInputStream codedIn)
+  public PrecomputedValue deserialize(DeserializationContext context, CodedInputStream codedIn)
       throws SerializationException, IOException {
     ObjectCodecs objectCodecs = objectCodecsSupplier.get();
     Object val = objectCodecs.deserialize(codedIn.readBytes(), codedIn);
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/DeserializationContext.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/DeserializationContext.java
new file mode 100644
index 0000000..8b7d435
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/DeserializationContext.java
@@ -0,0 +1,34 @@
+// Copyright 2018 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.lib.skyframe.serialization;
+
+/** Stateful class for providing additional context to a single deserialization "session". */
+// TODO(bazel-team): This class is just a shell, fill in.
+public class DeserializationContext {
+  // TODO(bazel-team): Replace with real stateless implementation when we start adding
+  // functionality.
+  private static final DeserializationContext EMPTY_STATELESS = new DeserializationContext();
+
+  public static DeserializationContext create() {
+    return new DeserializationContext();
+  }
+
+  /** Returns an empty instance which doesn't retain any state. */
+  public static DeserializationContext stateless() {
+    return EMPTY_STATELESS;
+  }
+
+  private DeserializationContext() {}
+}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/EnumCodec.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/EnumCodec.java
index 523620e..9363fb8 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/EnumCodec.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/EnumCodec.java
@@ -41,13 +41,15 @@
   }
 
   @Override
-  public void serialize(T value, CodedOutputStream codedOut) throws IOException {
+  public void serialize(SerializationContext context, T value, CodedOutputStream codedOut)
+      throws IOException {
     Preconditions.checkNotNull(value, "Enum value for %s is null", enumClass);
     codedOut.writeEnumNoTag(value.ordinal());
   }
 
   @Override
-  public T deserialize(CodedInputStream codedIn) throws SerializationException, IOException {
+  public T deserialize(DeserializationContext context, CodedInputStream codedIn)
+      throws SerializationException, IOException {
     int ordinal = codedIn.readEnum();
     try {
       return values.get(ordinal);
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/ImmutableListCodec.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/ImmutableListCodec.java
index 1b54d1f..0c67c38 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/ImmutableListCodec.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/ImmutableListCodec.java
@@ -37,16 +37,17 @@
   }
 
   @Override
-  public void serialize(ImmutableList<T> list, CodedOutputStream codedOut)
+  public void serialize(
+      SerializationContext context, ImmutableList<T> list, CodedOutputStream codedOut)
       throws SerializationException, IOException {
     codedOut.writeInt32NoTag(list.size());
     for (T item : list) {
-      codec.serialize(item, codedOut);
+      codec.serialize(context, item, codedOut);
     }
   }
 
   @Override
-  public ImmutableList<T> deserialize(CodedInputStream codedIn)
+  public ImmutableList<T> deserialize(DeserializationContext context, CodedInputStream codedIn)
       throws SerializationException, IOException {
     int length = codedIn.readInt32();
     if (length < 0) {
@@ -55,7 +56,7 @@
 
     ImmutableList.Builder<T> builder = ImmutableList.builder();
     for (int i = 0; i < length; i++) {
-      builder.add(codec.deserialize(codedIn));
+      builder.add(codec.deserialize(context, codedIn));
     }
     return builder.build();
   }
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/InjectingObjectCodec.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/InjectingObjectCodec.java
index 14f56c7..abd9d4d 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/InjectingObjectCodec.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/InjectingObjectCodec.java
@@ -31,7 +31,7 @@
    * @throws SerializationException on failure to serialize
    * @throws IOException on {@link IOException} during serialization
    */
-  void serialize(D dependency, T obj, CodedOutputStream codedOut)
+  void serialize(D dependency, SerializationContext context, T obj, CodedOutputStream codedOut)
       throws SerializationException, IOException;
 
   /**
@@ -43,5 +43,6 @@
    * @throws SerializationException on failure to deserialize
    * @throws IOException on {@link IOException} during deserialization
    */
-  T deserialize(D dependency, CodedInputStream codedIn) throws SerializationException, IOException;
+  T deserialize(D dependency, DeserializationContext context, CodedInputStream codedIn)
+      throws SerializationException, IOException;
 }
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/InjectingObjectCodecAdapter.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/InjectingObjectCodecAdapter.java
index b6b398e..ae44f24 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/InjectingObjectCodecAdapter.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/InjectingObjectCodecAdapter.java
@@ -41,13 +41,14 @@
   }
 
   @Override
-  public void serialize(T obj, CodedOutputStream codedOut)
+  public void serialize(SerializationContext context, T obj, CodedOutputStream codedOut)
       throws SerializationException, IOException {
-    codec.serialize(dependency, obj, codedOut);
+    codec.serialize(dependency, context, obj, codedOut);
   }
 
   @Override
-  public T deserialize(CodedInputStream codedIn) throws SerializationException, IOException {
-    return codec.deserialize(dependency, codedIn);
+  public T deserialize(DeserializationContext context, CodedInputStream codedIn)
+      throws SerializationException, IOException {
+    return codec.deserialize(dependency, context, codedIn);
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/JavaSerializableCodec.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/JavaSerializableCodec.java
index f3ce577..e6e4bd6 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/JavaSerializableCodec.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/JavaSerializableCodec.java
@@ -33,7 +33,7 @@
   }
 
   @Override
-  public void serialize(Object obj, CodedOutputStream codedOut)
+  public void serialize(SerializationContext context, Object obj, CodedOutputStream codedOut)
       throws SerializationException, IOException {
     ByteString.Output out = ByteString.newOutput();
     ObjectOutputStream objOut = new ObjectOutputStream(out);
@@ -49,7 +49,8 @@
   }
 
   @Override
-  public Object deserialize(CodedInputStream codedIn) throws SerializationException, IOException {
+  public Object deserialize(DeserializationContext context, CodedInputStream codedIn)
+      throws SerializationException, IOException {
     try {
       // Get the ByteBuffer as it is potentially a view of the underlying bytes (not a copy), which
       // is more efficient.
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/ObjectCodec.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/ObjectCodec.java
index b700923..4b9afbd 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/ObjectCodec.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/ObjectCodec.java
@@ -25,23 +25,29 @@
 public interface ObjectCodec<T> extends BaseCodec<T> {
 
   /**
-   * Serializes {@code obj}, inverse of {@link #deserialize(CodedInputStream)}.
+   * Serializes {@code obj}, inverse of {@link #deserialize}.
    *
+   * @param context {@link SerializationContext} providing additional information to the
+   *     serialization process
    * @param obj the object to serialize
    * @param codedOut the {@link CodedOutputStream} to write this object into. Implementations need
    *     not call {@link CodedOutputStream#flush()}, this should be handled by the caller.
    * @throws SerializationException on failure to serialize
    * @throws IOException on {@link IOException} during serialization
    */
-  void serialize(T obj, CodedOutputStream codedOut) throws SerializationException, IOException;
+  void serialize(SerializationContext context, T obj, CodedOutputStream codedOut)
+      throws SerializationException, IOException;
 
   /**
-   * Deserializes from {@code codedIn}, inverse of {@link #serialize(Object, CodedOutputStream)}.
+   * Deserializes from {@code codedIn}, inverse of {@link #serialize}.
    *
+   * @param context {@link DeserialiationContext} for providing additional information to the
+   *     deserialization process.
    * @param codedIn the {@link CodedInputStream} to read the serialized object from
    * @return the object deserialized from {@code codedIn}
    * @throws SerializationException on failure to deserialize
    * @throws IOException on {@link IOException} during deserialization
    */
-  T deserialize(CodedInputStream codedIn) throws SerializationException, IOException;
+  T deserialize(DeserializationContext context, CodedInputStream codedIn)
+      throws SerializationException, IOException;
 }
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/ObjectCodecs.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/ObjectCodecs.java
index c1a0d59..8eca0b5 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/ObjectCodecs.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/ObjectCodecs.java
@@ -94,7 +94,7 @@
     // in some situations, bypassing a copy.
     codedIn.enableAliasing(true);
     try {
-      Object result = codec.deserialize(codedIn);
+      Object result = codec.deserialize(DeserializationContext.create(), codedIn);
       if (result == null) {
         throw new NullPointerException(
             "ObjectCodec " + codec + " for " + classifier.toStringUtf8() + " returned null");
@@ -110,7 +110,8 @@
       String classifier, ObjectCodec<T> codec, Object subject, CodedOutputStream codedOut)
       throws SerializationException, IOException {
     try {
-      codec.serialize(codec.getEncodedClass().cast(subject), codedOut);
+      codec.serialize(
+          SerializationContext.create(), codec.getEncodedClass().cast(subject), codedOut);
     } catch (ClassCastException e) {
       throw new SerializationException(
           "Codec "
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/PolymorphicHelper.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/PolymorphicHelper.java
index 11689ba..a92c1c5 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/PolymorphicHelper.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/PolymorphicHelper.java
@@ -36,6 +36,7 @@
    */
   @SuppressWarnings("unchecked")
   public static void serialize(
+      SerializationContext context,
       Object input,
       Class<?> baseClass,
       CodedOutputStream codedOut,
@@ -47,15 +48,16 @@
         Class<?> clazz = classAndCodec.clazz;
         Object codec = classAndCodec.codec;
         codedOut.writeBoolNoTag(true);
-        StringCodecs.asciiOptimized().serialize(clazz.getName(), codedOut);
+        StringCodecs.asciiOptimized().serialize(context, clazz.getName(), codedOut);
         if (codec instanceof ObjectCodec) {
-          ((ObjectCodec) codec).serialize(input, codedOut);
+          ((ObjectCodec) codec).serialize(context, input, codedOut);
         } else if (codec instanceof InjectingObjectCodec) {
           if (dependency == null) {
             throw new SerializationException(
                 clazz.getCanonicalName() + " serialize parent class lacks required dependency.");
           }
-          ((InjectingObjectCodec) codec).serialize(dependency.orElse(null), input, codedOut);
+          ((InjectingObjectCodec) codec)
+              .serialize(dependency.orElse(null), context, input, codedOut);
         } else {
           throw new SerializationException(
               clazz.getCanonicalName()
@@ -78,21 +80,23 @@
    *     dependency itself is null (as opposed to non-existent).
    */
   @SuppressWarnings("unchecked")
-  public static Object deserialize(CodedInputStream codedIn, @Nullable Optional<?> dependency)
+  public static Object deserialize(
+      DeserializationContext context, CodedInputStream codedIn, @Nullable Optional<?> dependency)
       throws IOException, SerializationException {
     Object deserialized = null;
     if (codedIn.readBool()) {
-      String className = StringCodecs.asciiOptimized().deserialize(codedIn);
+      String className = StringCodecs.asciiOptimized().deserialize(context, codedIn);
       try {
         Object codec = getCodec(Class.forName(className));
         if (codec instanceof ObjectCodec) {
-          return ((ObjectCodec) codec).deserialize(codedIn);
+          return ((ObjectCodec) codec).deserialize(context, codedIn);
         } else if (codec instanceof InjectingObjectCodec) {
           if (dependency == null) {
             throw new SerializationException(
                 className + " deserialize parent class lacks required dependency.");
           }
-          return ((InjectingObjectCodec) codec).deserialize(dependency.orElse(null), codedIn);
+          return ((InjectingObjectCodec) codec)
+              .deserialize(dependency.orElse(null), context, codedIn);
         } else {
           throw new SerializationException(
               className + ".CODEC has unexpected type " + codec.getClass().getCanonicalName());
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/SerializationCommonUtils.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/SerializationCommonUtils.java
deleted file mode 100644
index b5baec0..0000000
--- a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/SerializationCommonUtils.java
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2017 The Bazel Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.devtools.build.lib.skyframe.serialization;
-
-import com.google.devtools.build.lib.skyframe.serialization.strings.StringCodecs;
-import com.google.protobuf.CodedInputStream;
-import com.google.protobuf.CodedOutputStream;
-import java.io.IOException;
-
-/** Common utilities for serialization. */
-public class SerializationCommonUtils {
-  public static final ImmutableListCodec<String> STRING_LIST_CODEC =
-      new ImmutableListCodec<>(StringCodecs.asciiOptimized());
-
-  public static <T> void serializeNullable(T obj, CodedOutputStream out, ObjectCodec<T> codec)
-      throws IOException, SerializationException {
-    if (obj == null) {
-      out.writeBoolNoTag(false);
-    } else {
-      out.writeBoolNoTag(true);
-      codec.serialize(obj, out);
-    }
-  }
-
-  public static <T> T deserializeNullable(CodedInputStream in, ObjectCodec<T> codec)
-      throws IOException, SerializationException {
-    return in.readBool() ? codec.deserialize(in) : null;
-  }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/SerializationContext.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/SerializationContext.java
new file mode 100644
index 0000000..234d049
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/SerializationContext.java
@@ -0,0 +1,34 @@
+// Copyright 2018 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.lib.skyframe.serialization;
+
+/** Stateful class for providing additional context to a single serialization "session". */
+// TODO(bazel-team): This class is just a shell, fill in.
+public class SerializationContext {
+  // TODO(bazel-team): Replace with real stateless implementation when we start adding
+  // functionality.
+  private static final SerializationContext EMPTY_STATELESS = new SerializationContext();
+
+  public static SerializationContext create() {
+    return stateless();
+  }
+
+  /** Returns an empty instance which doesn't retain any state. */
+  public static SerializationContext stateless() {
+    return EMPTY_STATELESS;
+  }
+
+  private SerializationContext() {}
+}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/SerializerAdapter.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/SerializerAdapter.java
index 0000407..a6243cf 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/SerializerAdapter.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/SerializerAdapter.java
@@ -37,7 +37,8 @@
     try {
       ByteArrayOutputStream byteOutput = new ByteArrayOutputStream();
       CodedOutputStream codedOut = CodedOutputStream.newInstance(byteOutput);
-      codec.serialize(object, codedOut);
+      // TODO(shahan): Determine if there's any context we can/should pass along from kryo.
+      codec.serialize(SerializationContext.create(), object, codedOut);
       codedOut.flush();
       byte[] byteData = byteOutput.toByteArray();
       output.writeInt(byteData.length, true);
@@ -51,7 +52,9 @@
   public T read(Kryo kryo, Input input, Class<T> unusedClass) {
     try {
       byte[] byteData = input.readBytes(input.readInt(true));
-      return codec.deserialize(CodedInputStream.newInstance(byteData));
+      // TODO(shahan): Determine if there's any context we can/should pass along from kryo.
+      return codec.deserialize(
+          DeserializationContext.create(), CodedInputStream.newInstance(byteData));
     } catch (SerializationException | IOException e) {
       throw new KryoException(e);
     }
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/SingletonCodec.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/SingletonCodec.java
index 3dbf328..7175aad 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/SingletonCodec.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/SingletonCodec.java
@@ -53,14 +53,16 @@
   }
 
   @Override
-  public void serialize(T t, CodedOutputStream codedOut) throws IOException {
+  public void serialize(SerializationContext context, T t, CodedOutputStream codedOut)
+      throws IOException {
     // TODO(michajlo): See how usefuly mnemonic actually winds up being for debugging, we may
     // want to just toss it and trust that the classifier for this value is good enough.
     codedOut.writeByteArrayNoTag(mnemonic);
   }
 
   @Override
-  public T deserialize(CodedInputStream codedIn) throws SerializationException, IOException {
+  public T deserialize(DeserializationContext context, CodedInputStream codedIn)
+      throws SerializationException, IOException {
     // Get ByteBuffer instead of raw bytes, as it may be a direct view of the data and not a copy,
     // which is much more efficient.
     ByteBuffer readMnemonic = codedIn.readByteBuffer();
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/AutoCodecProcessor.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/AutoCodecProcessor.java
index ff99931..2fc4096 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/AutoCodecProcessor.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/AutoCodecProcessor.java
@@ -411,8 +411,7 @@
           break;
         case DECLARED:
           marshallers.writeSerializationCode(
-              new Marshaller.Context(
-                  serializeBuilder, (DeclaredType) parameter.asType(), paramAccessor));
+              new Marshaller.Context(serializeBuilder, parameter.asType(), paramAccessor));
           break;
         default:
           throw new UnsupportedOperationException("Unimplemented or invalid kind: " + typeKind);
@@ -446,7 +445,7 @@
           break;
         case DECLARED:
           marshallers.writeDeserializationCode(
-              new Marshaller.Context(builder, (DeclaredType) parameter.asType(), paramName));
+              new Marshaller.Context(builder, parameter.asType(), paramName));
           break;
         default:
           throw new IllegalArgumentException("Unimplemented or invalid kind: " + typeKind);
@@ -616,10 +615,12 @@
     TypeName polyClass = TypeName.get(env.getTypeUtils().erasure(encodedType.asType()));
     if (dependency == null) {
       builder.addStatement(
-          "$T.serialize(input, $T.class, codedOut, null)", PolymorphicHelper.class, polyClass);
+          "$T.serialize(context, input, $T.class, codedOut, null)",
+          PolymorphicHelper.class,
+          polyClass);
     } else {
       builder.addStatement(
-          "$T.serialize(input, $T.class, codedOut, $T.ofNullable(dependency))",
+          "$T.serialize(context, input, $T.class, codedOut, $T.ofNullable(dependency))",
           PolymorphicHelper.class,
           polyClass,
           Optional.class);
@@ -633,12 +634,12 @@
         AutoCodecUtil.initializeDeserializeMethodBuilder(encodedType, dependency);
     if (dependency == null) {
       builder.addStatement(
-          "return ($T) $T.deserialize(codedIn, null)",
+          "return ($T) $T.deserialize(context, codedIn, null)",
           TypeName.get(encodedType.asType()),
           PolymorphicHelper.class);
     } else {
       builder.addStatement(
-          "return ($T) $T.deserialize(codedIn, $T.ofNullable(dependency))",
+          "return ($T) $T.deserialize(context, codedIn, $T.ofNullable(dependency))",
           TypeName.get(encodedType.asType()),
           PolymorphicHelper.class,
           Optional.class);
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/AutoCodecUtil.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/AutoCodecUtil.java
index 2edee1f..4f54092 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/AutoCodecUtil.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/AutoCodecUtil.java
@@ -15,8 +15,10 @@
 package com.google.devtools.build.lib.skyframe.serialization.autocodec;
 
 import com.google.common.collect.ImmutableList;
+import com.google.devtools.build.lib.skyframe.serialization.DeserializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.InjectingObjectCodec;
 import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec;
+import com.google.devtools.build.lib.skyframe.serialization.SerializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.SerializationException;
 import com.google.protobuf.CodedInputStream;
 import com.google.protobuf.CodedOutputStream;
@@ -106,6 +108,7 @@
       builder.addParameter(TypeName.get(dependency.asType()), "dependency");
     }
     return builder
+        .addParameter(SerializationContext.class, "context")
         .addParameter(TypeName.get(encodedType.asType()), "input")
         .addParameter(CodedOutputStream.class, "codedOut");
   }
@@ -136,7 +139,9 @@
     if (dependency != null) {
       builder.addParameter(TypeName.get(dependency.asType()), "dependency");
     }
-    return builder.addParameter(CodedInputStream.class, "codedIn");
+    return builder
+        .addParameter(DeserializationContext.class, "context")
+        .addParameter(CodedInputStream.class, "codedIn");
   }
 
   /**
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/Marshallers.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/Marshallers.java
index b625d34..541b1e3 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/Marshallers.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/Marshallers.java
@@ -255,13 +255,17 @@
         @Override
         public void addSerializationCode(Context context) {
           context.builder.addStatement(
-              "$T.asciiOptimized().serialize($L, codedOut)", StringCodecs.class, context.name);
+              "$T.asciiOptimized().serialize(context, $L, codedOut)",
+              StringCodecs.class,
+              context.name);
         }
 
         @Override
         public void addDeserializationCode(Context context) {
           context.builder.addStatement(
-              "$L = $T.asciiOptimized().deserialize(codedIn)", context.name, StringCodecs.class);
+              "$L = $T.asciiOptimized().deserialize(context, codedIn)",
+              context.name,
+              StringCodecs.class);
         }
       };
 
@@ -381,10 +385,9 @@
     // Writes the target count to the stream so deserialization knows when to stop.
     context.builder.addStatement(
         "codedOut.writeInt32NoTag($T.size($L))", Iterables.class, context.name);
-          Context repeated =
-              context.with(
-                  (DeclaredType) context.getDeclaredType().getTypeArguments().get(0),
-                  context.makeName("repeated"));
+    Context repeated =
+        context.with(
+            context.getDeclaredType().getTypeArguments().get(0), context.makeName("repeated"));
           context.builder.beginControlFlow(
               "for ($T $L : $L)", repeated.getTypeName(), repeated.name, context.name);
           writeSerializationCode(repeated);
@@ -394,8 +397,7 @@
   private void addDeserializationCodeForIterable(Context context) {
     Context repeated =
         context.with(
-            (DeclaredType) context.getDeclaredType().getTypeArguments().get(0),
-            context.makeName("repeated"));
+            context.getDeclaredType().getTypeArguments().get(0), context.makeName("repeated"));
           String builderName = context.makeName("builder");
           context.builder.addStatement(
               "$T<$T> $L = new $T<>()",
@@ -478,7 +480,7 @@
         public void addDeserializationCode(Context context) {
           Context repeated =
               context.with(
-                  (DeclaredType) context.getDeclaredType().getTypeArguments().get(0),
+                  context.getDeclaredType().getTypeArguments().get(0),
                   context.makeName("repeated"));
           String builderName = context.makeName("builder");
           context.builder.addStatement(
@@ -507,7 +509,7 @@
         public void addDeserializationCode(Context context) {
           Context repeated =
               context.with(
-                  (DeclaredType) context.getDeclaredType().getTypeArguments().get(0),
+                  context.getDeclaredType().getTypeArguments().get(0),
                   context.makeName("repeated"));
           String builderName = context.makeName("builder");
           context.builder.addStatement(
@@ -534,12 +536,10 @@
           String entryName = context.makeName("entry");
           Context key =
               context.with(
-                  (DeclaredType) context.getDeclaredType().getTypeArguments().get(0),
-                  entryName + ".getKey()");
+                  context.getDeclaredType().getTypeArguments().get(0), entryName + ".getKey()");
           Context value =
               context.with(
-                  (DeclaredType) context.getDeclaredType().getTypeArguments().get(1),
-                  entryName + ".getValue()");
+                  context.getDeclaredType().getTypeArguments().get(1), entryName + ".getValue()");
           context.builder.beginControlFlow(
               "for ($T<$T, $T> $L : $L.entrySet())",
               Map.Entry.class,
@@ -637,13 +637,10 @@
       Context context, MapBuilderInitializer mapBuilderInitializer, Consumer<String> finisher) {
     String builderName = context.makeName("builder");
     Context key =
-        context.with(
-            (DeclaredType) context.getDeclaredType().getTypeArguments().get(0),
-            context.makeName("key"));
+        context.with(context.getDeclaredType().getTypeArguments().get(0), context.makeName("key"));
     Context value =
         context.with(
-            (DeclaredType) context.getDeclaredType().getTypeArguments().get(1),
-            context.makeName("value"));
+            context.getDeclaredType().getTypeArguments().get(1), context.makeName("value"));
     mapBuilderInitializer.initialize(builderName, key, value);
     String lengthName = context.makeName("length");
     context.builder.addStatement("int $L = codedIn.readInt32()", lengthName);
@@ -671,12 +668,10 @@
           String entryName = context.makeName("entry");
           Context key =
               context.with(
-                  (DeclaredType) context.getDeclaredType().getTypeArguments().get(0),
-                  entryName + ".getKey()");
+                  context.getDeclaredType().getTypeArguments().get(0), entryName + ".getKey()");
           Context value =
               context.with(
-                  (DeclaredType) context.getDeclaredType().getTypeArguments().get(1),
-                  entryName + ".getValue()");
+                  context.getDeclaredType().getTypeArguments().get(1), entryName + ".getValue()");
           context.builder.beginControlFlow(
               "for ($T<$T, $T> $L : $L.entries())",
               Map.Entry.class,
@@ -693,12 +688,10 @@
         public void addDeserializationCode(Context context) {
           Context key =
               context.with(
-                  (DeclaredType) context.getDeclaredType().getTypeArguments().get(0),
-                  context.makeName("key"));
+                  context.getDeclaredType().getTypeArguments().get(0), context.makeName("key"));
           Context value =
               context.with(
-                  (DeclaredType) context.getDeclaredType().getTypeArguments().get(1),
-                  context.makeName("value"));
+                  context.getDeclaredType().getTypeArguments().get(1), context.makeName("value"));
           String builderName = context.makeName("builder");
           context.builder.addStatement(
               "$T<$T, $T> $L = new $T<>()",
@@ -731,7 +724,7 @@
         @Override
         public void addSerializationCode(Context context) {
           context.builder.addStatement(
-              "$T.asciiOptimized().serialize($L.pattern(), codedOut)",
+              "$T.asciiOptimized().serialize(context, $L.pattern(), codedOut)",
               StringCodecs.class,
               context.name);
           context.builder.addStatement("codedOut.writeInt32NoTag($L.flags())", context.name);
@@ -740,7 +733,9 @@
         @Override
         public void addDeserializationCode(Context context) {
           context.builder.addStatement(
-              "$L = $T.compile($T.asciiOptimized().deserialize(codedIn), codedIn.readInt32())",
+              "$L = $T.compile("
+                  + "$T.asciiOptimized().deserialize(context, codedIn), "
+                  + "codedIn.readInt32())",
               context.name,
               Pattern.class,
               StringCodecs.class);
@@ -830,7 +825,7 @@
                 typeParameter);
           }
     context.builder.addStatement(
-        "$L.serialize(($T<$T>) $L, codedOut)",
+        "$L.serialize(context, ($T<$T>) $L, codedOut)",
         nestedSetCodec,
         NestedSet.class,
         typeParameter,
@@ -871,8 +866,8 @@
                 NestedSetCodec.class,
                 typeParameter);
           }
-          context.builder.addStatement(
-              "$L = $L.deserialize(codedIn)", context.name, nestedSetCodec);
+    context.builder.addStatement(
+        "$L = $L.deserialize(context, codedIn)", context.name, nestedSetCodec);
   }
 
   private final Marshaller nestedSetMarshaller =
@@ -910,10 +905,10 @@
           TypeMirror codecType = getCodec(context.getDeclaredType()).get().asType();
           if (isSubtypeErased(codecType, ObjectCodec.class)) {
             context.builder.addStatement(
-                "$T.CODEC.serialize($L, codedOut)", context.getTypeName(), context.name);
+                "$T.CODEC.serialize(context, $L, codedOut)", context.getTypeName(), context.name);
           } else if (isSubtypeErased(codecType, InjectingObjectCodec.class)) {
             context.builder.addStatement(
-                "$T.CODEC.serialize(dependency, $L, codedOut)",
+                "$T.CODEC.serialize(dependency, context, $L, codedOut)",
                 context.getTypeName(),
                 context.name);
           } else {
@@ -929,10 +924,10 @@
           TypeMirror codecType = getCodec(context.getDeclaredType()).get().asType();
           if (isSubtypeErased(codecType, ObjectCodec.class)) {
             context.builder.addStatement(
-                "$L = $T.CODEC.deserialize(codedIn)", context.name, context.getTypeName());
+                "$L = $T.CODEC.deserialize(context, codedIn)", context.name, context.getTypeName());
           } else if (isSubtypeErased(codecType, InjectingObjectCodec.class)) {
             context.builder.addStatement(
-                "$L = $T.CODEC.deserialize(dependency, codedIn)",
+                "$L = $T.CODEC.deserialize(dependency, context, codedIn)",
                 context.name,
                 context.getTypeName());
           } else {
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/strings/FastStringCodec.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/strings/FastStringCodec.java
index f4b44c1..8e1a8fc 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/strings/FastStringCodec.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/strings/FastStringCodec.java
@@ -15,7 +15,9 @@
 package com.google.devtools.build.lib.skyframe.serialization.strings;
 
 import com.google.common.base.Preconditions;
+import com.google.devtools.build.lib.skyframe.serialization.DeserializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec;
+import com.google.devtools.build.lib.skyframe.serialization.SerializationContext;
 import com.google.protobuf.CodedInputStream;
 import com.google.protobuf.CodedOutputStream;
 import java.io.IOException;
@@ -63,12 +65,14 @@
   }
 
   @Override
-  public void serialize(String string, CodedOutputStream codedOut) throws IOException {
+  public void serialize(SerializationContext context, String string, CodedOutputStream codedOut)
+      throws IOException {
     codedOut.writeStringNoTag(string);
   }
 
   @Override
-  public String deserialize(CodedInputStream codedIn) throws IOException {
+  public String deserialize(DeserializationContext context, CodedInputStream codedIn)
+      throws IOException {
     int length = codedIn.readInt32();
     if (length == 0) {
       return EMPTY_STRING;
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/strings/StringCodec.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/strings/StringCodec.java
index 8aaba60..30901ed 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/strings/StringCodec.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/strings/StringCodec.java
@@ -14,7 +14,9 @@
 
 package com.google.devtools.build.lib.skyframe.serialization.strings;
 
+import com.google.devtools.build.lib.skyframe.serialization.DeserializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec;
+import com.google.devtools.build.lib.skyframe.serialization.SerializationContext;
 import com.google.protobuf.CodedInputStream;
 import com.google.protobuf.CodedOutputStream;
 import java.io.IOException;
@@ -28,12 +30,14 @@
   }
 
   @Override
-  public void serialize(String str, CodedOutputStream codedOut) throws IOException {
+  public void serialize(SerializationContext context, String str, CodedOutputStream codedOut)
+      throws IOException {
     codedOut.writeStringNoTag(str);
   }
 
   @Override
-  public String deserialize(CodedInputStream codedIn) throws IOException {
+  public String deserialize(DeserializationContext context, CodedInputStream codedIn)
+      throws IOException {
     return codedIn.readString();
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/testutils/ObjectCodecTester.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/testutils/ObjectCodecTester.java
index 3f7071d..82d4c36 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/testutils/ObjectCodecTester.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/testutils/ObjectCodecTester.java
@@ -20,6 +20,7 @@
 import com.google.common.base.Preconditions;
 import com.google.common.base.Stopwatch;
 import com.google.common.collect.ImmutableList;
+import com.google.devtools.build.lib.skyframe.serialization.DeserializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec;
 import com.google.devtools.build.lib.skyframe.serialization.SerializationException;
 import com.google.protobuf.CodedInputStream;
@@ -109,7 +110,9 @@
   /** Runs junk-data recognition tests. */
   void testDeserializeJunkData() {
     try {
-      underTest.deserialize(CodedInputStream.newInstance("junk".getBytes(StandardCharsets.UTF_8)));
+      underTest.deserialize(
+          DeserializationContext.create(),
+          CodedInputStream.newInstance("junk".getBytes(StandardCharsets.UTF_8)));
       fail("Expected exception");
     } catch (SerializationException | IOException e) {
       // Expected.
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/testutils/TestUtils.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/testutils/TestUtils.java
index 8eafe7f..8e87e46 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/testutils/TestUtils.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/testutils/TestUtils.java
@@ -16,7 +16,9 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import com.google.devtools.build.lib.skyframe.serialization.DeserializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec;
+import com.google.devtools.build.lib.skyframe.serialization.SerializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.SerializationException;
 import com.google.devtools.build.lib.skyframe.serialization.strings.StringCodecs;
 import com.google.devtools.build.lib.syntax.Environment.Frame;
@@ -35,7 +37,7 @@
       throws IOException, SerializationException {
     ByteArrayOutputStream bytes = new ByteArrayOutputStream();
     CodedOutputStream codedOut = CodedOutputStream.newInstance(bytes);
-    codec.serialize(value, codedOut);
+    codec.serialize(SerializationContext.create(), value, codedOut);
     codedOut.flush();
     return bytes.toByteArray();
   }
@@ -43,7 +45,7 @@
   /** Deserialize a value from a byte array. */
   public static <T> T fromBytes(ObjectCodec<T> codec, byte[] bytes)
       throws SerializationException, IOException {
-    return codec.deserialize(CodedInputStream.newInstance(bytes));
+    return codec.deserialize(DeserializationContext.create(), CodedInputStream.newInstance(bytes));
   }
 
   /**
@@ -77,15 +79,15 @@
     }
 
     @Override
-    public void serialize(String value, CodedOutputStream codedOut)
+    public void serialize(SerializationContext context, String value, CodedOutputStream codedOut)
         throws SerializationException, IOException {
-      stringCodec.serialize("dummy", codedOut);
+      stringCodec.serialize(context, "dummy", codedOut);
     }
 
     @Override
-    public String deserialize(CodedInputStream codedIn)
+    public String deserialize(DeserializationContext context, CodedInputStream codedIn)
         throws SerializationException, IOException {
-      stringCodec.deserialize(codedIn);
+      stringCodec.deserialize(context, codedIn);
       return "dummy";
     }
   }
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/Path.java b/src/main/java/com/google/devtools/build/lib/vfs/Path.java
index 075c47b..649eea9 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/Path.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/Path.java
@@ -15,8 +15,10 @@
 
 import com.google.common.base.Preconditions;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
+import com.google.devtools.build.lib.skyframe.serialization.DeserializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.InjectingObjectCodec;
 import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec;
+import com.google.devtools.build.lib.skyframe.serialization.SerializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.SerializationException;
 import com.google.devtools.build.lib.skyframe.serialization.strings.StringCodecs;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkPrintable;
@@ -915,17 +917,22 @@
     }
 
     @Override
-    public void serialize(FileSystemProvider fsProvider, Path path, CodedOutputStream codedOut)
+    public void serialize(
+        FileSystemProvider fsProvider,
+        SerializationContext context,
+        Path path,
+        CodedOutputStream codedOut)
         throws IOException, SerializationException {
       Preconditions.checkArgument(path.getFileSystem() == fsProvider.getFileSystem());
-      stringCodec.serialize(path.getPathString(), codedOut);
+      stringCodec.serialize(context, path.getPathString(), codedOut);
     }
 
     @Override
-    public Path deserialize(FileSystemProvider fsProvider, CodedInputStream codedIn)
+    public Path deserialize(
+        FileSystemProvider fsProvider, DeserializationContext context, CodedInputStream codedIn)
         throws IOException, SerializationException {
       return Path.createAlreadyNormalized(
-          stringCodec.deserialize(codedIn), fsProvider.getFileSystem());
+          stringCodec.deserialize(context, codedIn), fsProvider.getFileSystem());
     }
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/PathCodec.java b/src/main/java/com/google/devtools/build/lib/vfs/PathCodec.java
index c7644ed..6180572b 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/PathCodec.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/PathCodec.java
@@ -15,7 +15,9 @@
 package com.google.devtools.build.lib.vfs;
 
 import com.google.common.base.Preconditions;
+import com.google.devtools.build.lib.skyframe.serialization.DeserializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec;
+import com.google.devtools.build.lib.skyframe.serialization.SerializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.SerializationException;
 import com.google.devtools.build.lib.skyframe.serialization.strings.StringCodecs;
 import com.google.protobuf.CodedInputStream;
@@ -39,7 +41,7 @@
   }
 
   @Override
-  public void serialize(Path path, CodedOutputStream codedOut)
+  public void serialize(SerializationContext context, Path path, CodedOutputStream codedOut)
       throws IOException, SerializationException {
     Preconditions.checkState(
         path.getFileSystem() == fileSystem,
@@ -47,11 +49,12 @@
         path.getFileSystem(),
         fileSystem,
         path);
-    stringCodec.serialize(path.getPathString(), codedOut);
+    stringCodec.serialize(context, path.getPathString(), codedOut);
   }
 
   @Override
-  public Path deserialize(CodedInputStream codedIn) throws IOException, SerializationException {
-    return fileSystem.getPath(stringCodec.deserialize(codedIn));
+  public Path deserialize(DeserializationContext context, CodedInputStream codedIn)
+      throws IOException, SerializationException {
+    return fileSystem.getPath(stringCodec.deserialize(context, codedIn));
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/PathFragment.java b/src/main/java/com/google/devtools/build/lib/vfs/PathFragment.java
index 88ae732..2a5eed8 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/PathFragment.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/PathFragment.java
@@ -16,7 +16,9 @@
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import com.google.devtools.build.lib.analysis.actions.CommandLineItem;
+import com.google.devtools.build.lib.skyframe.serialization.DeserializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec;
+import com.google.devtools.build.lib.skyframe.serialization.SerializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.SerializationException;
 import com.google.devtools.build.lib.skyframe.serialization.strings.StringCodecs;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkPrintable;
@@ -732,15 +734,16 @@
     }
 
     @Override
-    public void serialize(PathFragment pathFragment, CodedOutputStream codedOut)
+    public void serialize(
+        SerializationContext context, PathFragment pathFragment, CodedOutputStream codedOut)
         throws IOException, SerializationException {
-      stringCodec.serialize(pathFragment.getPathString(), codedOut);
+      stringCodec.serialize(context, pathFragment.getPathString(), codedOut);
     }
 
     @Override
-    public PathFragment deserialize(CodedInputStream codedIn)
+    public PathFragment deserialize(DeserializationContext context, CodedInputStream codedIn)
         throws IOException, SerializationException {
-      return PathFragment.createAlreadyNormalized(stringCodec.deserialize(codedIn));
+      return PathFragment.createAlreadyNormalized(stringCodec.deserialize(context, codedIn));
     }
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/Root.java b/src/main/java/com/google/devtools/build/lib/vfs/Root.java
index 0df98c0..73ff687 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/Root.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/Root.java
@@ -15,7 +15,9 @@
 
 import com.google.common.base.Objects;
 import com.google.common.base.Preconditions;
+import com.google.devtools.build.lib.skyframe.serialization.DeserializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.InjectingObjectCodec;
+import com.google.devtools.build.lib.skyframe.serialization.SerializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.SerializationException;
 import com.google.protobuf.CodedInputStream;
 import com.google.protobuf.CodedOutputStream;
@@ -263,11 +265,15 @@
     }
 
     @Override
-    public void serialize(FileSystemProvider dependency, Root obj, CodedOutputStream codedOut)
+    public void serialize(
+        FileSystemProvider dependency,
+        SerializationContext context,
+        Root obj,
+        CodedOutputStream codedOut)
         throws SerializationException, IOException {
       if (obj instanceof PathRoot) {
         codedOut.writeBoolNoTag(false);
-        Path.CODEC.serialize(dependency, ((PathRoot) obj).path, codedOut);
+        Path.CODEC.serialize(dependency, context, ((PathRoot) obj).path, codedOut);
       } else if (obj instanceof AbsoluteRoot) {
         Preconditions.checkArgument(((AbsoluteRoot) obj).fileSystem == dependency.getFileSystem());
         codedOut.writeBoolNoTag(true);
@@ -277,13 +283,14 @@
     }
 
     @Override
-    public Root deserialize(FileSystemProvider dependency, CodedInputStream codedIn)
+    public Root deserialize(
+        FileSystemProvider dependency, DeserializationContext context, CodedInputStream codedIn)
         throws SerializationException, IOException {
       boolean isAbsolute = codedIn.readBool();
       if (isAbsolute) {
         return dependency.getFileSystem().getAbsoluteRoot();
       } else {
-        Path path = Path.CODEC.deserialize(dependency, codedIn);
+        Path path = Path.CODEC.deserialize(dependency, context, codedIn);
         return new PathRoot(path);
       }
     }
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/RootedPath.java b/src/main/java/com/google/devtools/build/lib/vfs/RootedPath.java
index 82335c7..aa31534 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/RootedPath.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/RootedPath.java
@@ -14,8 +14,10 @@
 package com.google.devtools.build.lib.vfs;
 
 import com.google.common.base.Preconditions;
+import com.google.devtools.build.lib.skyframe.serialization.DeserializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.InjectingObjectCodecAdapter;
 import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec;
+import com.google.devtools.build.lib.skyframe.serialization.SerializationContext;
 import com.google.devtools.build.lib.skyframe.serialization.SerializationException;
 import com.google.protobuf.CodedInputStream;
 import com.google.protobuf.CodedOutputStream;
@@ -138,17 +140,18 @@
     }
 
     @Override
-    public void serialize(RootedPath rootedPath, CodedOutputStream codedOut)
+    public void serialize(
+        SerializationContext context, RootedPath rootedPath, CodedOutputStream codedOut)
         throws IOException, SerializationException {
-      rootCodec.serialize(rootedPath.getRoot(), codedOut);
-      PathFragment.CODEC.serialize(rootedPath.getRootRelativePath(), codedOut);
+      rootCodec.serialize(context, rootedPath.getRoot(), codedOut);
+      PathFragment.CODEC.serialize(context, rootedPath.getRootRelativePath(), codedOut);
     }
 
     @Override
-    public RootedPath deserialize(CodedInputStream codedIn)
+    public RootedPath deserialize(DeserializationContext context, CodedInputStream codedIn)
         throws IOException, SerializationException {
-      Root root = rootCodec.deserialize(codedIn);
-      PathFragment rootRelativePath = PathFragment.CODEC.deserialize(codedIn);
+      Root root = rootCodec.deserialize(context, codedIn);
+      PathFragment rootRelativePath = PathFragment.CODEC.deserialize(context, codedIn);
       return toRootedPath(root, rootRelativePath);
     }
   }