Merge "Collect non-null parameter hints from Kotlin metadata."
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 33531da..58e22b7 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexApplication.Builder;
+import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
@@ -51,6 +52,7 @@
 import com.android.tools.r8.ir.optimize.lambda.LambdaMerger;
 import com.android.tools.r8.ir.regalloc.LinearScanRegisterAllocator;
 import com.android.tools.r8.ir.regalloc.RegisterAllocator;
+import com.android.tools.r8.kotlin.KotlinInfo;
 import com.android.tools.r8.logging.Log;
 import com.android.tools.r8.naming.IdentifierNameStringMarker;
 import com.android.tools.r8.shaking.protolite.ProtoLitePruner;
@@ -647,9 +649,10 @@
     printC1VisualizerHeader(method);
     printMethod(code, "Initial IR (SSA)");
 
-    if (method.getCode() != null && method.getCode().isJarCode() &&
-        appInfo.definitionFor(method.method.holder).hasKotlinInfo()) {
-      computeKotlinNotNullParamHints(feedback, method, code);
+    DexClass holder = appInfo.definitionFor(method.method.holder);
+    if (method.getCode() != null && method.getCode().isJarCode()
+        && holder.hasKotlinInfo()) {
+      computeKotlinNotNullParamHints(feedback, holder.getKotlinInfo(), method, code);
     }
 
     if (options.canHaveArtStringNewInitBug()) {
@@ -807,8 +810,19 @@
   }
 
   private void computeKotlinNotNullParamHints(
-      OptimizationFeedback feedback, DexEncodedMethod method, IRCode code) {
-    // Try to infer Kotlin non-null parameter check to use it as a hint.
+      OptimizationFeedback feedback, KotlinInfo kotlinInfo, DexEncodedMethod method, IRCode code) {
+    // Use non-null parameter hints in Kotlin metadata if available.
+    if (kotlinInfo.hasNonNullParameterHints()) {
+      BitSet hintFromMetadata = kotlinInfo.lookupNonNullParameterHint(
+          method.method.name.toString(), method.method.proto.toDescriptorString());
+      if (hintFromMetadata != null) {
+        if (hintFromMetadata.length() > 0) {
+          feedback.setKotlinNotNullParamHints(method, hintFromMetadata);
+        }
+        return;
+      }
+    }
+    // Otherwise, fall back to inspecting the code.
     List<Value> arguments = code.collectArguments(true);
     BitSet paramsCheckedForNull = new BitSet();
     DexMethod checkParameterIsNotNull =
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
index 74184a5..d15ed5a 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
@@ -4,7 +4,12 @@
 
 package com.android.tools.r8.kotlin;
 
+import static kotlinx.metadata.Flag.Property.IS_VAR;
+
 import kotlinx.metadata.KmClassVisitor;
+import kotlinx.metadata.KmConstructorVisitor;
+import kotlinx.metadata.KmFunctionVisitor;
+import kotlinx.metadata.KmPropertyVisitor;
 import kotlinx.metadata.jvm.KotlinClassMetadata;
 
 public class KotlinClass extends KotlinInfo<KotlinClassMetadata.Class> {
@@ -20,13 +25,30 @@
   }
 
   @Override
-  void validateMetadata(KotlinClassMetadata.Class metadata) {
-    ClassMetadataVisitor visitor = new ClassMetadataVisitor();
+  void processMetadata(KotlinClassMetadata.Class metadata) {
     // To avoid lazy parsing/verifying metadata.
-    metadata.accept(visitor);
+    metadata.accept(new ClassVisitorForNonNullParameterHints());
   }
 
-  private static class ClassMetadataVisitor extends KmClassVisitor {
+  private class ClassVisitorForNonNullParameterHints extends KmClassVisitor {
+    @Override
+    public KmFunctionVisitor visitFunction(int functionFlags, String functionName) {
+      return new NonNullParameterHintCollector.FunctionVisitor(nonNullparamHints);
+    }
+
+    @Override
+    public KmConstructorVisitor visitConstructor(int ctorFlags) {
+      return new NonNullParameterHintCollector.ConstructorVisitor(nonNullparamHints);
+    }
+
+    @Override
+    public KmPropertyVisitor visitProperty(
+        int propertyFlags, String name, int getterFlags, int setterFlags) {
+      if (IS_VAR.invoke(propertyFlags)) {
+        return new NonNullParameterHintCollector.PropertyVisitor(nonNullparamHints);
+      }
+      return null;
+    }
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassFacade.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassFacade.java
index 62db3d1..de8a12a 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassFacade.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassFacade.java
@@ -20,7 +20,7 @@
   }
 
   @Override
-  void validateMetadata(KotlinClassMetadata.MultiFileClassFacade metadata) {
+  void processMetadata(KotlinClassMetadata.MultiFileClassFacade metadata) {
     // No worries about lazy parsing/verifying, since no API to explore metadata details.
   }
 
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java
index da66e6c..225e63a 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java
@@ -4,7 +4,11 @@
 
 package com.android.tools.r8.kotlin;
 
+import static kotlinx.metadata.Flag.Property.IS_VAR;
+
+import kotlinx.metadata.KmFunctionVisitor;
 import kotlinx.metadata.KmPackageVisitor;
+import kotlinx.metadata.KmPropertyVisitor;
 import kotlinx.metadata.jvm.KotlinClassMetadata;
 
 public final class KotlinClassPart extends KotlinInfo<KotlinClassMetadata.MultiFileClassPart> {
@@ -21,12 +25,25 @@
   }
 
   @Override
-  void validateMetadata(KotlinClassMetadata.MultiFileClassPart metadata) {
+  void processMetadata(KotlinClassMetadata.MultiFileClassPart metadata) {
     // To avoid lazy parsing/verifying metadata.
-    metadata.accept(new MultiFileClassPartMetadataVisitor());
+    metadata.accept(new PackageVisitorForNonNullParameterHints());
   }
 
-  private static class MultiFileClassPartMetadataVisitor extends KmPackageVisitor {
+  private class PackageVisitorForNonNullParameterHints extends KmPackageVisitor {
+    @Override
+    public KmFunctionVisitor visitFunction(int functionFlags, String functionName) {
+      return new NonNullParameterHintCollector.FunctionVisitor(nonNullparamHints);
+    }
+
+    @Override
+    public KmPropertyVisitor visitProperty(
+        int propertyFlags, String name, int getterFlags, int setterFlags) {
+      if (IS_VAR.invoke(propertyFlags)) {
+        return new NonNullParameterHintCollector.PropertyVisitor(nonNullparamHints);
+      }
+      return null;
+    }
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java b/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java
index bcb70ed..2f9477e 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java
@@ -4,7 +4,11 @@
 
 package com.android.tools.r8.kotlin;
 
+import static kotlinx.metadata.Flag.Property.IS_VAR;
+
+import kotlinx.metadata.KmFunctionVisitor;
 import kotlinx.metadata.KmPackageVisitor;
+import kotlinx.metadata.KmPropertyVisitor;
 import kotlinx.metadata.jvm.KotlinClassMetadata;
 
 public final class KotlinFile extends KotlinInfo<KotlinClassMetadata.FileFacade> {
@@ -21,12 +25,25 @@
   }
 
   @Override
-  void validateMetadata(KotlinClassMetadata.FileFacade metadata) {
+  void processMetadata(KotlinClassMetadata.FileFacade metadata) {
     // To avoid lazy parsing/verifying metadata.
-    metadata.accept(new FileFacadeMetadataVisitor());
+    metadata.accept(new PackageVisitorForNonNullParameterHints());
   }
 
-  private static class FileFacadeMetadataVisitor extends KmPackageVisitor {
+  private class PackageVisitorForNonNullParameterHints extends KmPackageVisitor {
+    @Override
+    public KmFunctionVisitor visitFunction(int functionFlags, String functionName) {
+      return new NonNullParameterHintCollector.FunctionVisitor(nonNullparamHints);
+    }
+
+    @Override
+    public KmPropertyVisitor visitProperty(
+        int propertyFlags, String name, int getterFlags, int setterFlags) {
+      if (IS_VAR.invoke(propertyFlags)) {
+        return new NonNullParameterHintCollector.PropertyVisitor(nonNullparamHints);
+      }
+      return null;
+    }
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinInfo.java
index 702e0eb..7cc8aa1 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinInfo.java
@@ -4,22 +4,25 @@
 
 package com.android.tools.r8.kotlin;
 
+import com.google.common.collect.HashBasedTable;
+import java.util.BitSet;
 import kotlinx.metadata.jvm.KotlinClassMetadata;
 
 // Provides access to kotlin information.
 public abstract class KotlinInfo<MetadataKind extends KotlinClassMetadata> {
   MetadataKind metadata;
+  final HashBasedTable<String, String, BitSet> nonNullparamHints = HashBasedTable.create();
 
   KotlinInfo() {
   }
 
   KotlinInfo(MetadataKind metadata) {
-    validateMetadata(metadata);
+    processMetadata(metadata);
     this.metadata = metadata;
   }
 
-  // Subtypes will define how to validate the given metadata.
-  abstract void validateMetadata(MetadataKind metadata);
+  // Subtypes will define how to process the given metadata.
+  abstract void processMetadata(MetadataKind metadata);
 
   public enum Kind {
     Class, File, Synthetic, Part, Facade
@@ -66,4 +69,12 @@
   public KotlinClassFacade asClassFacade() {
     return null;
   }
+
+  public boolean hasNonNullParameterHints() {
+    return !nonNullparamHints.isEmpty();
+  }
+
+  public BitSet lookupNonNullParameterHint(String name, String descriptor) {
+    return nonNullparamHints.get(name, descriptor);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClass.java b/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClass.java
index 4660800..fc11f8c 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClass.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClass.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.kotlin;
 
 import com.android.tools.r8.graph.DexClass;
+import kotlinx.metadata.KmFunctionVisitor;
 import kotlinx.metadata.KmLambdaVisitor;
 import kotlinx.metadata.jvm.KotlinClassMetadata;
 
@@ -32,21 +33,23 @@
   }
 
   private KotlinSyntheticClass(Flavour flavour, KotlinClassMetadata.SyntheticClass metadata) {
+    super(metadata);
     this.flavour = flavour;
-    validateMetadata(metadata);
-    this.metadata = metadata;
   }
 
   @Override
-  void validateMetadata(KotlinClassMetadata.SyntheticClass metadata) {
+  void processMetadata(KotlinClassMetadata.SyntheticClass metadata) {
     if (metadata.isLambda()) {
-      SyntheticClassMetadataVisitor visitor = new SyntheticClassMetadataVisitor();
       // To avoid lazy parsing/verifying metadata.
-      metadata.accept(visitor);
+      metadata.accept(new LambdaVisitorForNonNullParameterHints());
     }
   }
 
-  private static class SyntheticClassMetadataVisitor extends KmLambdaVisitor {
+  private class LambdaVisitorForNonNullParameterHints extends KmLambdaVisitor {
+    @Override
+    public KmFunctionVisitor visitFunction(int functionFlags, String functionName) {
+      return new NonNullParameterHintCollector.FunctionVisitor(nonNullparamHints);
+    }
   }
 
   public boolean isLambda() {
diff --git a/src/main/java/com/android/tools/r8/kotlin/NonNullParameterHintCollector.java b/src/main/java/com/android/tools/r8/kotlin/NonNullParameterHintCollector.java
new file mode 100644
index 0000000..21b9409
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/NonNullParameterHintCollector.java
@@ -0,0 +1,184 @@
+// Copyright (c) 2018, 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.kotlin;
+
+import static kotlinx.metadata.Flag.Type.IS_NULLABLE;
+
+import com.google.common.collect.HashBasedTable;
+import java.util.BitSet;
+import kotlinx.metadata.KmConstructorExtensionVisitor;
+import kotlinx.metadata.KmConstructorVisitor;
+import kotlinx.metadata.KmExtensionType;
+import kotlinx.metadata.KmFunctionExtensionVisitor;
+import kotlinx.metadata.KmFunctionVisitor;
+import kotlinx.metadata.KmPropertyExtensionVisitor;
+import kotlinx.metadata.KmPropertyVisitor;
+import kotlinx.metadata.KmTypeVisitor;
+import kotlinx.metadata.KmValueParameterVisitor;
+import kotlinx.metadata.jvm.JvmConstructorExtensionVisitor;
+import kotlinx.metadata.jvm.JvmFieldSignature;
+import kotlinx.metadata.jvm.JvmFunctionExtensionVisitor;
+import kotlinx.metadata.jvm.JvmMethodSignature;
+import kotlinx.metadata.jvm.JvmPropertyExtensionVisitor;
+
+class NonNullParameterHintCollector {
+
+  static class FunctionVisitor extends KmFunctionVisitor {
+
+    private final HashBasedTable<String, String, BitSet> paramHints;
+
+    private BitSet paramHint = new BitSet();
+    private int paramIndex = 0;
+    private String name = "";
+    private String descriptor = "";
+
+    FunctionVisitor(HashBasedTable<String, String, BitSet> paramHints) {
+      this.paramHints = paramHints;
+    }
+
+    @Override
+    public KmTypeVisitor visitReceiverParameterType(int typeFlags) {
+      if (!IS_NULLABLE.invoke(typeFlags)) {
+        paramHint.set(paramIndex);
+      }
+      paramIndex++;
+      return null;
+    }
+
+    @Override
+    public KmValueParameterVisitor visitValueParameter(int paramFlags, String paramName) {
+      return new KmValueParameterVisitor() {
+        @Override
+        public KmTypeVisitor visitType(int typeFlags) {
+          if (!IS_NULLABLE.invoke(typeFlags)) {
+            paramHint.set(paramIndex);
+          }
+          paramIndex++;
+          return null;
+        }
+      };
+    }
+
+    @Override
+    public KmFunctionExtensionVisitor visitExtensions(KmExtensionType type) {
+      if (type != JvmFunctionExtensionVisitor.TYPE) {
+        return null;
+      }
+      return new JvmFunctionExtensionVisitor() {
+        @Override
+        public void visit(JvmMethodSignature desc) {
+          name = desc.getName();
+          descriptor = desc.getDesc();
+        }
+      };
+    }
+
+    @Override
+    public void visitEnd() {
+      if (name.isEmpty() || descriptor.isEmpty()) {
+        return;
+      }
+      paramHints.put(name, descriptor, paramHint);
+    }
+  }
+
+  static class ConstructorVisitor extends KmConstructorVisitor {
+    private final HashBasedTable<String, String, BitSet> paramHints;
+
+    private BitSet paramHint = new BitSet();
+    private int paramIndex = 0;
+    private final String name = "<init>";
+    private String descriptor = "";
+
+    ConstructorVisitor(HashBasedTable<String, String, BitSet> paramHints) {
+      this.paramHints = paramHints;
+    }
+
+    @Override
+    public KmValueParameterVisitor visitValueParameter(int paramFlags, String paramName) {
+      return new KmValueParameterVisitor() {
+        @Override
+        public KmTypeVisitor visitType(int typeFlags) {
+          if (!IS_NULLABLE.invoke(typeFlags)) {
+            paramHint.set(paramIndex);
+          }
+          paramIndex++;
+          return null;
+        }
+      };
+    }
+
+    @Override
+    public KmConstructorExtensionVisitor visitExtensions(KmExtensionType type) {
+      if (type != JvmConstructorExtensionVisitor.TYPE) {
+        return null;
+      }
+      return new JvmConstructorExtensionVisitor() {
+        @Override
+        public void visit(JvmMethodSignature desc) {
+          assert name.equals(desc.getName());
+          descriptor = desc.getDesc();
+        }
+      };
+    }
+
+    @Override
+    public void visitEnd() {
+      if (descriptor.isEmpty()) {
+        return;
+      }
+      paramHints.put(name, descriptor, paramHint);
+    }
+  }
+
+  static class PropertyVisitor extends KmPropertyVisitor {
+    private final HashBasedTable<String, String, BitSet> paramHints;
+
+    private BitSet paramHint = new BitSet();
+    private int paramIndex = 0;
+    private String name = "";
+    private String descriptor = "";
+
+    PropertyVisitor(HashBasedTable<String, String, BitSet> paramHints) {
+      this.paramHints = paramHints;
+    }
+
+    @Override
+    public KmTypeVisitor visitReturnType(int typeFlags) {
+      if (!IS_NULLABLE.invoke(typeFlags)) {
+        paramHint.set(paramIndex);
+      }
+      paramIndex++;
+      return null;
+    }
+
+    @Override
+    public KmPropertyExtensionVisitor visitExtensions(KmExtensionType type) {
+      if (type != JvmPropertyExtensionVisitor.TYPE) {
+        return null;
+      }
+      return new JvmPropertyExtensionVisitor() {
+        @Override
+        public void visit(
+            JvmFieldSignature fieldDesc,
+            JvmMethodSignature getterDesc,
+            JvmMethodSignature setterDesc) {
+          if (setterDesc != null) {
+            name = setterDesc.getName();
+            descriptor = setterDesc.getDesc();
+          }
+        }
+      };
+    }
+
+    @Override
+    public void visitEnd() {
+      if (name.isEmpty() || descriptor.isEmpty()) {
+        return;
+      }
+      paramHints.put(name, descriptor, paramHint);
+    }
+  }
+}
\ No newline at end of file