Introduce retarget_static_field

Support for LocalDateTime#EPOCH now possible

Bug: 222647019
Change-Id: I5eb6f81c2e3065120011b16fc71066c11b7e7f9b
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
index 29dc73f..ce94d5d 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
@@ -93,6 +93,12 @@
     return opcode == Opcodes.GETSTATIC;
   }
 
+  public boolean isStaticFieldPut() {
+    return opcode == Opcodes.PUTSTATIC;
+  }
+
+  public abstract CfFieldInstruction createWithField(DexField field);
+
   @Override
   public CfFieldInstruction asFieldInstruction() {
     return this;
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldRead.java b/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldRead.java
index 145bc94..2684c0d 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldRead.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldRead.java
@@ -22,6 +22,11 @@
   }
 
   @Override
+  public CfFieldInstruction createWithField(DexField otherField) {
+    return new CfInstanceFieldRead(otherField);
+  }
+
+  @Override
   void internalRegisterUse(
       UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) {
     registry.registerInstanceFieldReadInstruction(this);
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldWrite.java b/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldWrite.java
index 6d01f49..c9138bb 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldWrite.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInstanceFieldWrite.java
@@ -21,6 +21,11 @@
   }
 
   @Override
+  public CfFieldInstruction createWithField(DexField otherField) {
+    return new CfInstanceFieldWrite(otherField);
+  }
+
+  @Override
   void internalRegisterUse(
       UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) {
     registry.registerInstanceFieldWrite(getField());
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfStaticFieldRead.java b/src/main/java/com/android/tools/r8/cf/code/CfStaticFieldRead.java
index 66fc48e..30715e1 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfStaticFieldRead.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfStaticFieldRead.java
@@ -22,6 +22,11 @@
   }
 
   @Override
+  public CfFieldInstruction createWithField(DexField otherField) {
+    return new CfStaticFieldRead(otherField);
+  }
+
+  @Override
   void internalRegisterUse(
       UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) {
     registry.registerStaticFieldReadInstruction(this);
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfStaticFieldWrite.java b/src/main/java/com/android/tools/r8/cf/code/CfStaticFieldWrite.java
index 2cd0f26..24b5e6d 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfStaticFieldWrite.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfStaticFieldWrite.java
@@ -21,6 +21,11 @@
   }
 
   @Override
+  public CfFieldInstruction createWithField(DexField otherField) {
+    return new CfStaticFieldWrite(otherField);
+  }
+
+  @Override
   void internalRegisterUse(
       UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) {
     registry.registerStaticFieldWrite(getField());
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAmender.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAmender.java
index c943028..92bd91e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAmender.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAmender.java
@@ -8,8 +8,13 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexDefinitionSupplier;
+import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexLibraryClass;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.FieldAccessFlags;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.utils.Reporter;
 import java.util.Map;
@@ -28,20 +33,23 @@
   public static void run(AppView<?> appView) {
     run(
         appView.options().machineDesugaredLibrarySpecification.getAmendLibraryMethods(),
+        appView.options().machineDesugaredLibrarySpecification.getAmendLibraryFields(),
         appView,
         appView.options().reporter,
         appView.computedMinApiLevel());
   }
 
   public static void run(
-      Map<DexMethod, MethodAccessFlags> amendLibrary,
+      Map<DexMethod, MethodAccessFlags> amendLibraryMethod,
+      Map<DexField, FieldAccessFlags> amendLibraryField,
       DexDefinitionSupplier definitions,
       Reporter reporter,
       ComputedApiLevel minAPILevel) {
-    if (amendLibrary.isEmpty()) {
+    if (amendLibraryMethod.isEmpty() && amendLibraryField.isEmpty()) {
       return;
     }
-    new DesugaredLibraryAmender(definitions, reporter, minAPILevel).run(amendLibrary);
+    new DesugaredLibraryAmender(definitions, reporter, minAPILevel)
+        .run(amendLibraryMethod, amendLibraryField);
   }
 
   private DesugaredLibraryAmender(
@@ -51,22 +59,40 @@
     this.minAPILevel = minAPILevel;
   }
 
-  private void run(Map<DexMethod, MethodAccessFlags> amendLibrary) {
-    amendLibrary.forEach(this::amendLibraryMethod);
+  private void run(
+      Map<DexMethod, MethodAccessFlags> amendLibraryMethod,
+      Map<DexField, FieldAccessFlags> amendLibraryField) {
+    amendLibraryMethod.forEach(this::amendLibraryMethod);
+    amendLibraryField.forEach(this::amendLibraryField);
+  }
+
+  private void amendLibraryField(DexField field, FieldAccessFlags fieldAccessFlags) {
+    DexLibraryClass libClass = getLibraryClass(field);
+    if (libClass == null) {
+      return;
+    }
+    if (libClass.lookupField(field) != null) {
+      return;
+    }
+    DexEncodedField encodedField =
+        DexEncodedField.syntheticBuilder()
+            .setField(field)
+            .setAccessFlags(fieldAccessFlags)
+            .setApiLevel(minAPILevel)
+            .build();
+    if (fieldAccessFlags.isStatic()) {
+      libClass.appendStaticField(encodedField);
+    } else {
+      libClass.appendInstanceField(encodedField);
+    }
   }
 
   private void amendLibraryMethod(DexMethod method, MethodAccessFlags methodAccessFlags) {
-    DexClass dexClass = definitions.contextIndependentDefinitionFor(method.getHolderType());
-    if (dexClass == null || !dexClass.isLibraryClass()) {
-      // Consider just throwing an error.
-      reporter.warning(
-          "Desugared library: Cannot amend library method "
-              + method
-              + " because the holder is not a library class"
-              + (dexClass == null ? "(null)." : "."));
+    DexLibraryClass libClass = getLibraryClass(method);
+    if (libClass == null) {
       return;
     }
-    if (dexClass.lookupMethod(method) != null) {
+    if (libClass.lookupMethod(method) != null) {
       return;
     }
     DexEncodedMethod encodedMethod =
@@ -76,6 +102,20 @@
             .setCode(null)
             .setApiLevelForDefinition(minAPILevel)
             .build();
-    dexClass.getMethodCollection().addMethod(encodedMethod);
+    libClass.getMethodCollection().addMethod(encodedMethod);
+  }
+
+  private DexLibraryClass getLibraryClass(DexReference reference) {
+    DexClass dexClass = definitions.contextIndependentDefinitionFor(reference.getContextType());
+    if (dexClass == null || !dexClass.isLibraryClass()) {
+      // Consider just throwing an error.
+      reporter.warning(
+          "Desugared library: Cannot amend library reference "
+              + reference
+              + " because the holder is not a library class"
+              + (dexClass == null ? "(null)." : "."));
+      return null;
+    }
+    return dexClass.asLibraryClass();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/AbstractMethodParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/AbstractMethodParser.java
deleted file mode 100644
index d63efb6..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/AbstractMethodParser.java
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification;
-
-import com.android.tools.r8.dex.Constants;
-import com.android.tools.r8.errors.CompilationError;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.utils.DescriptorUtils;
-import com.google.common.collect.ImmutableMap;
-import java.util.Map;
-
-/** Parse methods of the form: modifiers* returnType holder#name(arg0, ..., argN) */
-public abstract class AbstractMethodParser {
-
-  private static final String SEPARATORS = "\\s+|,\\s+|#|\\(|\\)";
-
-  private static final Map<String, Integer> modifiers =
-      ImmutableMap.<String, Integer>builder()
-          .put("public", Constants.ACC_PUBLIC)
-          .put("private", Constants.ACC_PRIVATE)
-          .put("protected", Constants.ACC_PROTECTED)
-          .put("final", Constants.ACC_FINAL)
-          .put("abstract", Constants.ACC_ABSTRACT)
-          .put("static", Constants.ACC_STATIC)
-          .build();
-
-  final DexItemFactory factory;
-
-  protected AbstractMethodParser(DexItemFactory factory) {
-    this.factory = factory;
-  }
-
-  // TODO(b/218755060): It would be nice to avoid the split regexp and use a nextToken()
-  //  method instead, then add a TraversalContinuation.
-  public void parseMethod(String signature) {
-    String[] tokens = signature.split(SEPARATORS);
-    if (tokens.length < 3) {
-      throw new CompilationError("Desugared library: cannot parse method " + signature);
-    }
-    methodStart();
-    int first = parseModifiers(tokens);
-    returnType(stringTypeToDexType(tokens[first]));
-    holderType(stringTypeToDexType(tokens[first + 1]));
-    methodName(factory.createString(tokens[first + 1 + 1]));
-    for (int i = first + 3; i < tokens.length; i++) {
-      argType(stringTypeToDexType(tokens[i]));
-    }
-    methodEnd();
-  }
-
-  private DexType stringTypeToDexType(String stringType) {
-    return factory.createType(DescriptorUtils.javaTypeToDescriptor(stringType));
-  }
-
-  private int parseModifiers(String[] split) {
-    int index = 0;
-    while (modifiers.containsKey(split[index])) {
-      modifier(modifiers.get(split[index]));
-      index++;
-    }
-    return index;
-  }
-
-  protected abstract void methodStart();
-
-  protected abstract void methodEnd();
-
-  protected abstract void returnType(DexType type);
-
-  protected abstract void argType(DexType type);
-
-  protected abstract void modifier(int access);
-
-  protected abstract void holderType(DexType type);
-
-  protected abstract void methodName(DexString name);
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java
index 2eb550b..461fdbf 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java
@@ -8,10 +8,13 @@
 import static com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser.isHumanSpecification;
 
 import com.android.tools.r8.StringResource;
+import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.TopLevelFlagsBuilder;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.memberparser.HumanFieldParser;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.memberparser.HumanMethodParser;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.DescriptorUtils;
@@ -48,6 +51,7 @@
   static final String WRAPPER_CONVERSION_EXCLUDING_KEY = "wrapper_conversion_excluding";
   static final String CUSTOM_CONVERSION_KEY = "custom_conversion";
   static final String REWRITE_PREFIX_KEY = "rewrite_prefix";
+  static final String RETARGET_STATIC_FIELD_KEY = "retarget_static_field";
   static final String RETARGET_METHOD_KEY = "retarget_method";
   static final String RETARGET_METHOD_EMULATED_DISPATCH_KEY =
       "retarget_method_with_emulated_dispatch";
@@ -57,11 +61,13 @@
   static final String DONT_RETARGET_KEY = "dont_retarget";
   static final String BACKPORT_KEY = "backport";
   static final String AMEND_LIBRARY_METHOD_KEY = "amend_library_method";
+  static final String AMEND_LIBRARY_FIELD_KEY = "amend_library_field";
   static final String SHRINKER_CONFIG_KEY = "shrinker_config";
   static final String SUPPORT_ALL_CALLBACKS_FROM_LIBRARY_KEY = "support_all_callbacks_from_library";
 
   private final DexItemFactory dexItemFactory;
   private final HumanMethodParser methodParser;
+  private final HumanFieldParser fieldParser;
   private final Reporter reporter;
   private final boolean libraryCompilation;
   private final int minAPILevel;
@@ -76,6 +82,7 @@
       int minAPILevel) {
     this.dexItemFactory = dexItemFactory;
     this.methodParser = new HumanMethodParser(dexItemFactory);
+    this.fieldParser = new HumanFieldParser(dexItemFactory);
     this.reporter = reporter;
     this.minAPILevel = minAPILevel;
     this.libraryCompilation = libraryCompilation;
@@ -245,6 +252,14 @@
         }
       }
     }
+    if (jsonFlagSet.has(RETARGET_STATIC_FIELD_KEY)) {
+      for (Map.Entry<String, JsonElement> retarget :
+          jsonFlagSet.get(RETARGET_STATIC_FIELD_KEY).getAsJsonObject().entrySet()) {
+        builder.retargetStaticField(
+            parseField(retarget.getKey()),
+            stringDescriptorToDexType(retarget.getValue().getAsString()));
+      }
+    }
     if (jsonFlagSet.has(RETARGET_METHOD_KEY)) {
       for (Map.Entry<String, JsonElement> retarget :
           jsonFlagSet.get(RETARGET_METHOD_KEY).getAsJsonObject().entrySet()) {
@@ -317,6 +332,13 @@
         builder.amendLibraryMethod(methodParser.getMethod(), methodParser.getFlags());
       }
     }
+    if (jsonFlagSet.has(AMEND_LIBRARY_FIELD_KEY)) {
+      JsonArray amendLibraryMember = jsonFlagSet.get(AMEND_LIBRARY_FIELD_KEY).getAsJsonArray();
+      for (JsonElement amend : amendLibraryMember) {
+        fieldParser.parseField(amend.getAsString());
+        builder.amendLibraryField(fieldParser.getField(), fieldParser.getFlags());
+      }
+    }
   }
 
   private Set<DexMethod> parseMethods(JsonArray array) {
@@ -332,6 +354,11 @@
     return methodParser.getMethod();
   }
 
+  private DexField parseField(String signature) {
+    fieldParser.parseField(signature);
+    return fieldParser.getField();
+  }
+
   private DexType stringDescriptorToDexType(String stringClass) {
     return dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptor(stringClass));
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java
index 9eb3ed7..6d22218 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java
@@ -4,8 +4,10 @@
 
 package com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification;
 
+import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.FieldAccessFlags;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.Reporter;
@@ -26,6 +28,7 @@
   private final Map<String, String> rewritePrefix;
   private final Map<String, Map<String, String>> rewriteDerivedPrefix;
   private final Map<DexType, DexType> emulatedInterfaces;
+  private final Map<DexField, DexType> retargetStaticField;
   private final Map<DexMethod, DexType> retargetMethod;
   private final Map<DexMethod, DexType> retargetMethodEmulatedDispatch;
   private final Map<DexType, DexType> legacyBackport;
@@ -34,11 +37,13 @@
   private final Set<DexType> dontRetarget;
   private final Map<DexType, Set<DexMethod>> wrapperConversions;
   private final Map<DexMethod, MethodAccessFlags> amendLibraryMethod;
+  private final Map<DexField, FieldAccessFlags> amendLibraryField;
 
   HumanRewritingFlags(
       Map<String, String> rewritePrefix,
       Map<String, Map<String, String>> rewriteDerivedPrefix,
       Map<DexType, DexType> emulateLibraryInterface,
+      Map<DexField, DexType> retargetStaticField,
       Map<DexMethod, DexType> retargetMethod,
       Map<DexMethod, DexType> retargetMethodEmulatedDispatch,
       Map<DexType, DexType> legacyBackport,
@@ -46,10 +51,12 @@
       Set<DexMethod> dontRewriteInvocation,
       Set<DexType> dontRetarget,
       Map<DexType, Set<DexMethod>> wrapperConversion,
-      Map<DexMethod, MethodAccessFlags> amendLibraryMethod) {
+      Map<DexMethod, MethodAccessFlags> amendLibraryMethod,
+      Map<DexField, FieldAccessFlags> amendLibraryField) {
     this.rewritePrefix = rewritePrefix;
     this.rewriteDerivedPrefix = rewriteDerivedPrefix;
     this.emulatedInterfaces = emulateLibraryInterface;
+    this.retargetStaticField = retargetStaticField;
     this.retargetMethod = retargetMethod;
     this.retargetMethodEmulatedDispatch = retargetMethodEmulatedDispatch;
     this.legacyBackport = legacyBackport;
@@ -58,6 +65,7 @@
     this.dontRetarget = dontRetarget;
     this.wrapperConversions = wrapperConversion;
     this.amendLibraryMethod = amendLibraryMethod;
+    this.amendLibraryField = amendLibraryField;
   }
 
   public static HumanRewritingFlags empty() {
@@ -69,9 +77,11 @@
         ImmutableMap.of(),
         ImmutableMap.of(),
         ImmutableMap.of(),
+        ImmutableMap.of(),
         ImmutableSet.of(),
         ImmutableSet.of(),
         ImmutableMap.of(),
+        ImmutableMap.of(),
         ImmutableMap.of());
   }
 
@@ -86,6 +96,7 @@
         rewritePrefix,
         rewriteDerivedPrefix,
         emulatedInterfaces,
+        retargetStaticField,
         retargetMethod,
         retargetMethodEmulatedDispatch,
         legacyBackport,
@@ -93,7 +104,8 @@
         dontRewriteInvocation,
         dontRetarget,
         wrapperConversions,
-        amendLibraryMethod);
+        amendLibraryMethod,
+        amendLibraryField);
   }
 
   public Map<String, String> getRewritePrefix() {
@@ -108,6 +120,10 @@
     return emulatedInterfaces;
   }
 
+  public Map<DexField, DexType> getRetargetStaticField() {
+    return retargetStaticField;
+  }
+
   public Map<DexMethod, DexType> getRetargetMethod() {
     return retargetMethod;
   }
@@ -140,11 +156,17 @@
     return amendLibraryMethod;
   }
 
+  public Map<DexField, FieldAccessFlags> getAmendLibraryField() {
+    return amendLibraryField;
+  }
+
   public boolean isEmpty() {
     return rewritePrefix.isEmpty()
         && rewriteDerivedPrefix.isEmpty()
         && emulatedInterfaces.isEmpty()
-        && retargetMethod.isEmpty();
+        && retargetMethod.isEmpty()
+        && retargetMethodEmulatedDispatch.isEmpty()
+        && retargetStaticField.isEmpty();
   }
 
   public static class Builder {
@@ -155,6 +177,7 @@
     private final Map<String, String> rewritePrefix;
     private final Map<String, Map<String, String>> rewriteDerivedPrefix;
     private final Map<DexType, DexType> emulatedInterfaces;
+    private final Map<DexField, DexType> retargetStaticField;
     private final Map<DexMethod, DexType> retargetMethod;
     private final Map<DexMethod, DexType> retargetMethodEmulatedDispatch;
     private final Map<DexType, DexType> legacyBackport;
@@ -163,6 +186,7 @@
     private final Set<DexType> dontRetarget;
     private final Map<DexType, Set<DexMethod>> wrapperConversions;
     private final Map<DexMethod, MethodAccessFlags> amendLibraryMethod;
+    private final Map<DexField, FieldAccessFlags> amendLibraryField;
 
     Builder(Reporter reporter, Origin origin) {
       this(
@@ -175,9 +199,11 @@
           new IdentityHashMap<>(),
           new IdentityHashMap<>(),
           new IdentityHashMap<>(),
+          new IdentityHashMap<>(),
           Sets.newIdentityHashSet(),
           Sets.newIdentityHashSet(),
           new IdentityHashMap<>(),
+          new IdentityHashMap<>(),
           new IdentityHashMap<>());
     }
 
@@ -187,6 +213,7 @@
         Map<String, String> rewritePrefix,
         Map<String, Map<String, String>> rewriteDerivedPrefix,
         Map<DexType, DexType> emulateLibraryInterface,
+        Map<DexField, DexType> retargetStaticField,
         Map<DexMethod, DexType> retargetMethod,
         Map<DexMethod, DexType> retargetMethodEmulatedDispatch,
         Map<DexType, DexType> backportCoreLibraryMember,
@@ -194,12 +221,14 @@
         Set<DexMethod> dontRewriteInvocation,
         Set<DexType> dontRetargetLibMember,
         Map<DexType, Set<DexMethod>> wrapperConversions,
-        Map<DexMethod, MethodAccessFlags> amendLibrary) {
+        Map<DexMethod, MethodAccessFlags> amendLibraryMethod,
+        Map<DexField, FieldAccessFlags> amendLibraryField) {
       this.reporter = reporter;
       this.origin = origin;
       this.rewritePrefix = new HashMap<>(rewritePrefix);
       this.rewriteDerivedPrefix = new HashMap<>(rewriteDerivedPrefix);
       this.emulatedInterfaces = new IdentityHashMap<>(emulateLibraryInterface);
+      this.retargetStaticField = new IdentityHashMap<>(retargetStaticField);
       this.retargetMethod = new IdentityHashMap<>(retargetMethod);
       this.retargetMethodEmulatedDispatch = new IdentityHashMap<>(retargetMethodEmulatedDispatch);
       this.legacyBackport = new IdentityHashMap<>(backportCoreLibraryMember);
@@ -209,7 +238,8 @@
       this.dontRetarget = Sets.newIdentityHashSet();
       this.dontRetarget.addAll(dontRetargetLibMember);
       this.wrapperConversions = new IdentityHashMap<>(wrapperConversions);
-      this.amendLibraryMethod = new IdentityHashMap<>(amendLibrary);
+      this.amendLibraryMethod = new IdentityHashMap<>(amendLibraryMethod);
+      this.amendLibraryField = new IdentityHashMap<>(amendLibraryField);
     }
 
     // Utility to set values.
@@ -285,6 +315,15 @@
       return this;
     }
 
+    public Builder retargetStaticField(DexField key, DexType rewrittenType) {
+      put(
+          retargetStaticField,
+          key,
+          rewrittenType,
+          HumanDesugaredLibrarySpecificationParser.RETARGET_STATIC_FIELD_KEY);
+      return this;
+    }
+
     public Builder retargetMethodEmulatedDispatch(DexMethod key, DexType rewrittenType) {
       put(
           retargetMethodEmulatedDispatch,
@@ -318,12 +357,18 @@
       return this;
     }
 
+    public Builder amendLibraryField(DexField member, FieldAccessFlags flags) {
+      amendLibraryField.put(member, flags);
+      return this;
+    }
+
     public HumanRewritingFlags build() {
       validate();
       return new HumanRewritingFlags(
           ImmutableMap.copyOf(rewritePrefix),
           ImmutableMap.copyOf(rewriteDerivedPrefix),
           ImmutableMap.copyOf(emulatedInterfaces),
+          ImmutableMap.copyOf(retargetStaticField),
           ImmutableMap.copyOf(retargetMethod),
           ImmutableMap.copyOf(retargetMethodEmulatedDispatch),
           ImmutableMap.copyOf(legacyBackport),
@@ -331,7 +376,8 @@
           ImmutableSet.copyOf(dontRewriteInvocation),
           ImmutableSet.copyOf(dontRetarget),
           ImmutableMap.copyOf(wrapperConversions),
-          ImmutableMap.copyOf(amendLibraryMethod));
+          ImmutableMap.copyOf(amendLibraryMethod),
+          ImmutableMap.copyOf(amendLibraryField));
     }
 
     private void validate() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanTopLevelFlags.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanTopLevelFlags.java
index 6f1f435..5383b69 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanTopLevelFlags.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanTopLevelFlags.java
@@ -47,7 +47,7 @@
 
   public static HumanTopLevelFlags testing() {
     return new HumanTopLevelFlags(
-        AndroidApiLevel.R, "unused", "testing", null, true, ImmutableList.of());
+        AndroidApiLevel.O, "unused", "testing", null, true, ImmutableList.of());
   }
 
   public static Builder builder() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/memberparser/AbstractFieldParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/memberparser/AbstractFieldParser.java
new file mode 100644
index 0000000..8062063
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/memberparser/AbstractFieldParser.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.memberparser;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+
+/** Parse fields of the form: modifiers* fieldType holder#fieldName */
+public abstract class AbstractFieldParser extends AbstractMemberParser {
+
+  protected AbstractFieldParser(DexItemFactory factory) {
+    super(factory);
+  }
+
+  // TODO(b/218755060): It would be nice to avoid the split regexp and use a nextToken()
+  //  method instead, then add a TraversalContinuation.
+  public void parseField(String signature) {
+    String[] tokens = signature.split(SEPARATORS);
+    if (tokens.length < 3) {
+      throw new CompilationError("Desugared library: cannot parse field " + signature);
+    }
+    fieldStart();
+    int first = parseModifiers(tokens);
+    fieldType(stringTypeToDexType(tokens[first]));
+    holderType(stringTypeToDexType(tokens[first + 1]));
+    fieldName(factory.createString(tokens[first + 1 + 1]));
+    fieldEnd();
+  }
+
+  protected abstract void fieldStart();
+
+  protected abstract void fieldEnd();
+
+  protected abstract void fieldType(DexType type);
+
+  protected abstract void holderType(DexType type);
+
+  protected abstract void fieldName(DexString name);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/memberparser/AbstractMemberParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/memberparser/AbstractMemberParser.java
new file mode 100644
index 0000000..99475b2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/memberparser/AbstractMemberParser.java
@@ -0,0 +1,48 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.memberparser;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+
+public abstract class AbstractMemberParser {
+
+  static final String SEPARATORS = "\\s+|,\\s+|#|\\(|\\)";
+
+  static final Map<String, Integer> MODIFIERS =
+      ImmutableMap.<String, Integer>builder()
+          .put("public", Constants.ACC_PUBLIC)
+          .put("private", Constants.ACC_PRIVATE)
+          .put("protected", Constants.ACC_PROTECTED)
+          .put("final", Constants.ACC_FINAL)
+          .put("abstract", Constants.ACC_ABSTRACT)
+          .put("static", Constants.ACC_STATIC)
+          .build();
+
+  final DexItemFactory factory;
+
+  protected AbstractMemberParser(DexItemFactory factory) {
+    this.factory = factory;
+  }
+
+  DexType stringTypeToDexType(String stringType) {
+    return factory.createType(DescriptorUtils.javaTypeToDescriptor(stringType));
+  }
+
+  int parseModifiers(String[] split) {
+    int index = 0;
+    while (MODIFIERS.containsKey(split[index])) {
+      modifier(MODIFIERS.get(split[index]));
+      index++;
+    }
+    return index;
+  }
+
+  protected abstract void modifier(int access);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/memberparser/AbstractMethodParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/memberparser/AbstractMethodParser.java
new file mode 100644
index 0000000..1afff88
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/memberparser/AbstractMethodParser.java
@@ -0,0 +1,48 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.memberparser;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+
+/** Parse methods of the form: modifiers* returnType holder#name(arg0, ..., argN) */
+public abstract class AbstractMethodParser extends AbstractMemberParser {
+
+  protected AbstractMethodParser(DexItemFactory factory) {
+    super(factory);
+  }
+
+  // TODO(b/218755060): It would be nice to avoid the split regexp and use a nextToken()
+  //  method instead, then add a TraversalContinuation.
+  public void parseMethod(String signature) {
+    String[] tokens = signature.split(SEPARATORS);
+    if (tokens.length < 3) {
+      throw new CompilationError("Desugared library: cannot parse method " + signature);
+    }
+    methodStart();
+    int first = parseModifiers(tokens);
+    returnType(stringTypeToDexType(tokens[first]));
+    holderType(stringTypeToDexType(tokens[first + 1]));
+    methodName(factory.createString(tokens[first + 1 + 1]));
+    for (int i = first + 3; i < tokens.length; i++) {
+      argType(stringTypeToDexType(tokens[i]));
+    }
+    methodEnd();
+  }
+
+  protected abstract void methodStart();
+
+  protected abstract void methodEnd();
+
+  protected abstract void returnType(DexType type);
+
+  protected abstract void argType(DexType type);
+
+  protected abstract void holderType(DexType type);
+
+  protected abstract void methodName(DexString name);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/memberparser/HumanFieldParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/memberparser/HumanFieldParser.java
new file mode 100644
index 0000000..9a5a44d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/memberparser/HumanFieldParser.java
@@ -0,0 +1,81 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.memberparser;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.FieldAccessFlags;
+
+public class HumanFieldParser extends AbstractFieldParser {
+
+  // Values accumulated while parsing.
+  private FieldAccessFlags.Builder flagBuilder;
+  private DexType fieldType;
+  private DexType holder;
+  private DexString fieldName;
+  // Resulting values.
+  private DexField field;
+  private FieldAccessFlags flags;
+
+  public HumanFieldParser(DexItemFactory factory) {
+    super(factory);
+  }
+
+  private boolean parsingFinished() {
+    return field != null;
+  }
+
+  public DexField getField() {
+    assert parsingFinished();
+    return field;
+  }
+
+  public FieldAccessFlags getFlags() {
+    assert parsingFinished();
+    return flags;
+  }
+
+  @Override
+  protected void modifier(int access) {
+    assert !parsingFinished();
+    flagBuilder.set(access);
+  }
+
+  @Override
+  protected void holderType(DexType type) {
+    assert !parsingFinished();
+    holder = type;
+  }
+
+  @Override
+  protected void fieldName(DexString name) {
+    assert !parsingFinished();
+    fieldName = name;
+  }
+
+  @Override
+  protected void fieldStart() {
+    flagBuilder = FieldAccessFlags.builder();
+    fieldType = null;
+    holder = null;
+    fieldName = null;
+    field = null;
+    flags = null;
+  }
+
+  @Override
+  protected void fieldEnd() {
+    field = factory.createField(holder, fieldType, fieldName);
+    flags = flagBuilder.build();
+  }
+
+  @Override
+  protected void fieldType(DexType type) {
+    assert !parsingFinished();
+    fieldType = type;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanMethodParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/memberparser/HumanMethodParser.java
similarity index 96%
rename from src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanMethodParser.java
rename to src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/memberparser/HumanMethodParser.java
index b4bac60..d78267b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanMethodParser.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/memberparser/HumanMethodParser.java
@@ -2,7 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-package com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification;
+package com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.memberparser;
 
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
@@ -25,7 +25,7 @@
   private DexMethod method;
   private MethodAccessFlags flags;
 
-  protected HumanMethodParser(DexItemFactory factory) {
+  public HumanMethodParser(DexItemFactory factory) {
     super(factory);
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
index 1f52b3d..5216a03 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
@@ -4,9 +4,11 @@
 
 package com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification;
 
+import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.FieldAccessFlags;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.SemanticVersion;
@@ -86,6 +88,10 @@
     return rewritingFlags.getRewriteDerivedTypeOnly();
   }
 
+  public Map<DexField, DexField> getStaticFieldRetarget() {
+    return rewritingFlags.getStaticFieldRetarget();
+  }
+
   public Map<DexMethod, DexMethod> getStaticRetarget() {
     return rewritingFlags.getStaticRetarget();
   }
@@ -143,6 +149,10 @@
     return rewritingFlags.getAmendLibraryMethod();
   }
 
+  public Map<DexField, FieldAccessFlags> getAmendLibraryFields() {
+    return rewritingFlags.getAmendLibraryField();
+  }
+
   public boolean hasRetargeting() {
     return rewritingFlags.hasRetargeting();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
index 00a4747..bc566af3 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
@@ -4,8 +4,10 @@
 
 package com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification;
 
+import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.FieldAccessFlags;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
@@ -26,6 +28,7 @@
   MachineRewritingFlags(
       Map<DexType, DexType> rewriteType,
       Map<DexType, DexType> rewriteDerivedTypeOnly,
+      Map<DexField, DexField> staticFieldRetarget,
       Map<DexMethod, DexMethod> staticRetarget,
       Map<DexMethod, DexMethod> nonEmulatedVirtualRetarget,
       Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedVirtualRetarget,
@@ -35,9 +38,11 @@
       Map<DexType, DexType> legacyBackport,
       Set<DexType> dontRetarget,
       Map<DexType, CustomConversionDescriptor> customConversions,
-      Map<DexMethod, MethodAccessFlags> amendLibraryMethods) {
+      Map<DexMethod, MethodAccessFlags> amendLibraryMethods,
+      Map<DexField, FieldAccessFlags> amendLibraryFields) {
     this.rewriteType = rewriteType;
     this.rewriteDerivedTypeOnly = rewriteDerivedTypeOnly;
+    this.staticFieldRetarget = staticFieldRetarget;
     this.staticRetarget = staticRetarget;
     this.nonEmulatedVirtualRetarget = nonEmulatedVirtualRetarget;
     this.emulatedVirtualRetarget = emulatedVirtualRetarget;
@@ -49,6 +54,7 @@
     this.dontRetarget = dontRetarget;
     this.customConversions = customConversions;
     this.amendLibraryMethod = amendLibraryMethods;
+    this.amendLibraryField = amendLibraryFields;
   }
 
   // Rewrites all the references to the keys as well as synthetic types derived from any key.
@@ -56,7 +62,10 @@
   // Rewrites only synthetic types derived from any key.
   private final Map<DexType, DexType> rewriteDerivedTypeOnly;
 
-  // Static methods to retarget, duplicated to library boundaries.
+  // Fields to retarget.
+  private final Map<DexField, DexField> staticFieldRetarget;
+
+  // Static methods to retarget.
   private final Map<DexMethod, DexMethod> staticRetarget;
 
   // Virtual methods to retarget, which are guaranteed not to require emulated dispatch.
@@ -83,6 +92,7 @@
   private final Set<DexType> dontRetarget;
   private final Map<DexType, CustomConversionDescriptor> customConversions;
   private final Map<DexMethod, MethodAccessFlags> amendLibraryMethod;
+  private final Map<DexField, FieldAccessFlags> amendLibraryField;
 
   public Map<DexType, DexType> getRewriteType() {
     return rewriteType;
@@ -92,6 +102,10 @@
     return rewriteDerivedTypeOnly;
   }
 
+  public Map<DexField, DexField> getStaticFieldRetarget() {
+    return staticFieldRetarget;
+  }
+
   public Map<DexMethod, DexMethod> getStaticRetarget() {
     return staticRetarget;
   }
@@ -146,10 +160,15 @@
     return amendLibraryMethod;
   }
 
+  public Map<DexField, FieldAccessFlags> getAmendLibraryField() {
+    return amendLibraryField;
+  }
+
   public boolean hasRetargeting() {
     return !staticRetarget.isEmpty()
         || !nonEmulatedVirtualRetarget.isEmpty()
-        || !emulatedVirtualRetarget.isEmpty();
+        || !emulatedVirtualRetarget.isEmpty()
+        || !staticFieldRetarget.isEmpty();
   }
 
   public boolean isEmulatedInterfaceRewrittenType(DexType type) {
@@ -175,6 +194,8 @@
 
     private final Map<DexType, DexType> rewriteType = new IdentityHashMap<>();
     private final Map<DexType, DexType> rewriteDerivedTypeOnly = new IdentityHashMap<>();
+    private final ImmutableMap.Builder<DexField, DexField> staticFieldRetarget =
+        ImmutableMap.builder();
     private final ImmutableMap.Builder<DexMethod, DexMethod> staticRetarget =
         ImmutableMap.builder();
     private final ImmutableMap.Builder<DexMethod, DexMethod> nonEmulatedVirtualRetarget =
@@ -192,6 +213,8 @@
         ImmutableMap.builder();
     private final ImmutableMap.Builder<DexMethod, MethodAccessFlags> amendLibraryMethod =
         ImmutableMap.builder();
+    private final ImmutableMap.Builder<DexField, FieldAccessFlags> amendLibraryField =
+        ImmutableMap.builder();
 
     public void rewriteType(DexType src, DexType target) {
       assert src != null;
@@ -205,6 +228,10 @@
       rewriteDerivedTypeOnly.put(src, target);
     }
 
+    public void putStaticFieldRetarget(DexField src, DexField dest) {
+      staticFieldRetarget.put(src, dest);
+    }
+
     public void putStaticRetarget(DexMethod src, DexMethod dest) {
       staticRetarget.put(src, dest);
     }
@@ -245,6 +272,10 @@
       amendLibraryMethod.put(missingReference, flags);
     }
 
+    public void amendLibraryField(DexField missingReference, FieldAccessFlags flags) {
+      amendLibraryField.put(missingReference, flags);
+    }
+
     public DexType getRewrittenType(DexType type) {
       return rewriteType.get(type);
     }
@@ -253,6 +284,7 @@
       return new MachineRewritingFlags(
           rewriteType,
           rewriteDerivedTypeOnly,
+          staticFieldRetarget.build(),
           staticRetarget.build(),
           nonEmulatedVirtualRetarget.build(),
           emulatedVirtualRetarget.build(),
@@ -262,7 +294,8 @@
           legacyBackport.build(),
           dontRetarget.build(),
           customConversions.build(),
-          amendLibraryMethod.build());
+          amendLibraryMethod.build(),
+          amendLibraryField.build());
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java
index a4b984f..a929fb9 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java
@@ -6,12 +6,15 @@
 
 import static com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeter.InvokeRetargetingResult.NO_REWRITING;
 
+import com.android.tools.r8.cf.code.CfFieldInstruction;
 import com.android.tools.r8.cf.code.CfInstruction;
 import com.android.tools.r8.cf.code.CfInvoke;
 import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.MethodResolutionResult;
@@ -26,6 +29,7 @@
 import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeterSynthesizerEventConsumer.DesugaredLibraryRetargeterInstructionEventConsumer;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 import java.util.function.Consumer;
 import java.util.function.Function;
@@ -36,6 +40,7 @@
   private final AppView<?> appView;
   private final DesugaredLibraryRetargeterSyntheticHelper syntheticHelper;
 
+  private final Map<DexField, DexField> staticFieldRetarget;
   private final Map<DexMethod, DexMethod> staticRetarget;
   private final Map<DexMethod, DexMethod> nonEmulatedVirtualRetarget;
   private final Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedVirtualRetarget;
@@ -45,6 +50,7 @@
     this.syntheticHelper = new DesugaredLibraryRetargeterSyntheticHelper(appView);
     MachineDesugaredLibrarySpecification specification =
         appView.options().machineDesugaredLibrarySpecification;
+    staticFieldRetarget = specification.getStaticFieldRetarget();
     staticRetarget = specification.getStaticRetarget();
     nonEmulatedVirtualRetarget = specification.getNonEmulatedVirtualRetarget();
     emulatedVirtualRetarget = specification.getEmulatedVirtualRetarget();
@@ -67,20 +73,53 @@
       MethodProcessingContext methodProcessingContext,
       CfInstructionDesugaringCollection desugaringCollection,
       DexItemFactory dexItemFactory) {
-    InvokeRetargetingResult invokeRetargetingResult = computeNewInvokeTarget(instruction, context);
-
-    if (!invokeRetargetingResult.hasNewInvokeTarget()) {
-      return null;
+    if (instruction.isFieldInstruction() && needsDesugaring(instruction, context)) {
+      return desugarFieldInstruction(instruction.asFieldInstruction(), context);
+    } else if (instruction.isInvoke() && needsDesugaring(instruction, context)) {
+      return desugarInvoke(instruction.asInvoke(), eventConsumer, context);
     }
+    return null;
+  }
 
+  private Collection<CfInstruction> desugarFieldInstruction(
+      CfFieldInstruction fieldInstruction, ProgramMethod context) {
+    DexField fieldRetarget = fieldRetarget(fieldInstruction, context);
+    assert fieldRetarget != null;
+    assert fieldInstruction.isStaticFieldGet() || fieldInstruction.isStaticFieldPut();
+    return Collections.singletonList(fieldInstruction.createWithField(fieldRetarget));
+  }
+
+  private List<CfInstruction> desugarInvoke(
+      CfInvoke invoke, CfInstructionDesugaringEventConsumer eventConsumer, ProgramMethod context) {
+    InvokeRetargetingResult invokeRetargetingResult = computeNewInvokeTarget(invoke, context);
+    assert invokeRetargetingResult.hasNewInvokeTarget();
     DexMethod newInvokeTarget = invokeRetargetingResult.getNewInvokeTarget(eventConsumer);
     return Collections.singletonList(
-        new CfInvoke(Opcodes.INVOKESTATIC, newInvokeTarget, instruction.asInvoke().isInterface()));
+        new CfInvoke(Opcodes.INVOKESTATIC, newInvokeTarget, invoke.isInterface()));
   }
 
   @Override
   public boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) {
-    return computeNewInvokeTarget(instruction, context).hasNewInvokeTarget();
+    if (instruction.isFieldInstruction()) {
+      return fieldRetarget(instruction.asFieldInstruction(), context) != null;
+    } else if (instruction.isInvoke()) {
+      return computeNewInvokeTarget(instruction.asInvoke(), context).hasNewInvokeTarget();
+    }
+    return false;
+  }
+
+  private DexField fieldRetarget(CfFieldInstruction fieldInstruction, ProgramMethod context) {
+    DexEncodedField resolvedField =
+        appView
+            .appInfoForDesugaring()
+            .resolveField(fieldInstruction.getField(), context)
+            .getResolvedField();
+    if (resolvedField != null) {
+      assert resolvedField.isStatic()
+          || !staticFieldRetarget.containsKey(resolvedField.getReference());
+      return staticFieldRetarget.get(resolvedField.getReference());
+    }
+    return null;
   }
 
   InvokeRetargetingResult ensureInvokeRetargetingResult(DexMethod retarget) {
@@ -124,10 +163,7 @@
   }
 
   private InvokeRetargetingResult computeNewInvokeTarget(
-      CfInstruction instruction, ProgramMethod context) {
-    if (!instruction.isInvoke()) {
-      return NO_REWRITING;
-    }
+      CfInvoke instruction, ProgramMethod context) {
     if (appView
         .options()
         .machineDesugaredLibrarySpecification
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java
index f64a3d0..56af1fc 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java
@@ -6,7 +6,9 @@
 
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexReference;
@@ -26,7 +28,7 @@
 public class HumanToMachineRetargetConverter {
 
   private final AppInfoWithClassHierarchy appInfo;
-  private final Set<DexMethod> missingMethods = Sets.newIdentityHashSet();
+  private final Set<DexReference> missingReferences = Sets.newIdentityHashSet();
 
   public HumanToMachineRetargetConverter(AppInfoWithClassHierarchy appInfo) {
     this.appInfo = appInfo;
@@ -37,6 +39,9 @@
       MachineRewritingFlags.Builder builder,
       BiConsumer<String, Set<? extends DexReference>> warnConsumer) {
     rewritingFlags
+        .getRetargetStaticField()
+        .forEach((field, type) -> convertRetargetField(builder, field, type));
+    rewritingFlags
         .getRetargetMethod()
         .forEach((method, type) -> convertRetargetMethod(builder, method, type));
     rewritingFlags
@@ -44,7 +49,19 @@
         .forEach(
             (method, type) ->
                 convertRetargetMethodEmulatedDispatch(builder, rewritingFlags, method, type));
-    warnConsumer.accept("Cannot retarget missing methods: ", missingMethods);
+    warnConsumer.accept("Cannot retarget missing references: ", missingReferences);
+  }
+
+  private void convertRetargetField(
+      MachineRewritingFlags.Builder builder, DexField field, DexType type) {
+    DexClass holder = appInfo.definitionFor(field.holder);
+    DexEncodedField foundField = holder.lookupField(field);
+    if (foundField == null) {
+      missingReferences.add(field);
+      return;
+    }
+    builder.putStaticFieldRetarget(
+        field, appInfo.dexItemFactory().createField(type, field.type, field.name));
   }
 
   private void convertRetargetMethodEmulatedDispatch(
@@ -55,7 +72,7 @@
     DexClass holder = appInfo.definitionFor(method.holder);
     DexEncodedMethod foundMethod = holder.lookupMethod(method);
     if (foundMethod == null) {
-      missingMethods.add(method);
+      missingReferences.add(method);
       return;
     }
     if (foundMethod.isStatic()) {
@@ -83,7 +100,7 @@
     DexClass holder = appInfo.definitionFor(method.holder);
     DexEncodedMethod foundMethod = holder.lookupMethod(method);
     if (foundMethod == null) {
-      missingMethods.add(method);
+      missingReferences.add(method);
       return;
     }
     if (foundMethod.isStatic()) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
index 5ea225d..0cec776 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
@@ -86,8 +86,13 @@
     HumanRewritingFlags rewritingFlags = humanSpec.getRewritingFlags();
     MachineRewritingFlags.Builder builder = MachineRewritingFlags.builder();
     DesugaredLibraryAmender.run(
-        rewritingFlags.getAmendLibraryMethod(), appInfo, reporter, ComputedApiLevel.unknown());
+        rewritingFlags.getAmendLibraryMethod(),
+        rewritingFlags.getAmendLibraryField(),
+        appInfo,
+        reporter,
+        ComputedApiLevel.unknown());
     rewritingFlags.getAmendLibraryMethod().forEach(builder::amendLibraryMethod);
+    rewritingFlags.getAmendLibraryField().forEach(builder::amendLibraryField);
     new HumanToMachineRetargetConverter(appInfo)
         .convertRetargetFlags(rewritingFlags, builder, this::warnMissingReferences);
     new HumanToMachineEmulatedInterfaceConverter(appInfo)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LocalDateEpochTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LocalDateEpochTest.java
new file mode 100644
index 0000000..23a146e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LocalDateEpochTest.java
@@ -0,0 +1,132 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.desugaredlibrary;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.FieldAccessFlags;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanRewritingFlags;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanTopLevelFlags;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.transformers.MethodTransformer;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.time.LocalDate;
+import java.util.Collection;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class LocalDateEpochTest extends DesugaredLibraryTestBase {
+
+  private final TestParameters parameters;
+
+  private static final String EXPECTED_OUTPUT = StringUtils.lines("1970-01-01");
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters()
+        .withDexRuntimesStartingFromIncluding(Version.V8_1_0)
+        .withAllApiLevels()
+        .build();
+  }
+
+  public LocalDateEpochTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    testForD8(parameters.getBackend())
+        .addLibraryFiles(getLibraryFile())
+        .addProgramClasses(DesugarLocalDate.class)
+        .addProgramClassFileData(getMainClassFileData())
+        .setMinApi(parameters.getApiLevel())
+        .addOptionsModification(opt -> opt.setDesugaredLibrarySpecification(getSpecification(opt)))
+        .compile()
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    Assume.assumeTrue(parameters.isDexRuntime());
+    testForR8(parameters.getBackend())
+        .addLibraryFiles(getLibraryFile())
+        .addProgramClasses(DesugarLocalDate.class)
+        .addProgramClassFileData(getMainClassFileData())
+        .addKeepMainRule(Main.class)
+        .setMinApi(parameters.getApiLevel())
+        .addOptionsModification(opt -> opt.setDesugaredLibrarySpecification(getSpecification(opt)))
+        .compile()
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  private DesugaredLibrarySpecification getSpecification(InternalOptions options) {
+    DexType date = options.dexItemFactory().createType("Ljava/time/LocalDate;");
+    DexType desugarDate =
+        options
+            .dexItemFactory()
+            .createType("L" + DescriptorUtils.getClassBinaryName(DesugarLocalDate.class) + ";");
+    DexString epoch = options.dexItemFactory().createString("EPOCH");
+    DexField src = options.dexItemFactory().createField(date, date, epoch);
+    HumanRewritingFlags rewritingFlags =
+        HumanRewritingFlags.builder(options.reporter, Origin.unknown())
+            .retargetStaticField(src, desugarDate)
+            .amendLibraryField(
+                src,
+                FieldAccessFlags.fromSharedAccessFlags(
+                    Constants.ACC_PUBLIC | Constants.ACC_STATIC | Constants.ACC_FINAL))
+            .build();
+    return new HumanDesugaredLibrarySpecification(
+        HumanTopLevelFlags.testing(), rewritingFlags, false);
+  }
+
+  private Collection<byte[]> getMainClassFileData() throws IOException {
+    return ImmutableList.of(
+        transformer(Main.class)
+            .addMethodTransformer(
+                new MethodTransformer() {
+                  @Override
+                  public void visitFieldInsn(
+                      final int opcode,
+                      final String owner,
+                      final String name,
+                      final String descriptor) {
+                    if (name.equals("MIN")) {
+                      super.visitFieldInsn(opcode, owner, "EPOCH", descriptor);
+                    } else {
+                      super.visitFieldInsn(opcode, owner, name, descriptor);
+                    }
+                  }
+                })
+            .transform());
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      System.out.println(LocalDate.MIN);
+    }
+  }
+
+  static class DesugarLocalDate {
+
+    public static final LocalDate EPOCH = LocalDate.of(1970, 1, 1);
+  }
+}