<NME> AbstractCompilationUnitBuilder.java
<BEF> /*
 * Copyright 2019 Google Inc.
 *
 * 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.j2cl.transpiler.frontend.common;


import com.google.common.collect.Iterables;
import com.google.errorprone.annotations.FormatMethod;
import com.google.j2cl.common.InternalCompilerError;
import com.google.j2cl.transpiler.ast.BooleanLiteral;
import com.google.j2cl.transpiler.ast.CompilationUnit;
import com.google.j2cl.transpiler.ast.Expression;
import com.google.j2cl.transpiler.ast.NumberLiteral;
import com.google.j2cl.transpiler.ast.StringLiteral;
import com.google.j2cl.transpiler.ast.Type;
import com.google.j2cl.transpiler.ast.TypeDescriptor;
import com.google.j2cl.transpiler.ast.Variable;
import com.google.j2cl.transpiler.ast.VariableDeclarationExpression;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;

/** Base class for implementing that AST conversion from different front ends. */
public abstract class AbstractCompilationUnitBuilder {

  private final PackageInfoCache packageInfoCache = PackageInfoCache.get();

  /** Type stack to keep track of the lexically enclosing types as they are being created. */
  private final List<Type> typeStack = new ArrayList<>();

  private String currentSourceFile;
  private CompilationUnit currentCompilationUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;

/** Base class for implementing that AST conversion from different front ends. */
public abstract class AbstractCompilationUnitBuilder {
    packageInfoCache.setPackageProperties(
        PackageInfoCache.SOURCE_CLASS_PATH_ENTRY, packageName, jsNamespace, isNullMarked);
  }

  protected String getCurrentSourceFile() {
    return currentSourceFile;
  }

  protected void setCurrentSourceFile(String currentSourceFile) {
    this.currentSourceFile = currentSourceFile;
  }

  protected CompilationUnit getCurrentCompilationUnit() {
    return currentCompilationUnit;
  }

  protected void setCurrentCompilationUnit(CompilationUnit currentCompilationUnit) {
    this.currentCompilationUnit = currentCompilationUnit;
  }

  /** Invoke {@code supplier} with {@code type} in the type stack. */
  protected <T> T processEnclosedBy(Type type, Supplier<T> supplier) {
    typeStack.add(type);
    T converted = supplier.get();
    typeStack.remove(typeStack.size() - 1);
    return converted;
  }

  /** Returns the current type. */
  protected Type getCurrentType() {
    return Iterables.getLast(typeStack, null);
  }

  ////////////////////////////////////////////////////////////////////////////////////////////////
  // General helpers.
  ////////////////////////////////////////////////////////////////////////////////////////////////

  /**
   * Creates temporary variables for a resource that is declared outside of the try-catch statement.
   */
  protected static VariableDeclarationExpression toResource(Expression expression) {
    if (expression instanceof VariableDeclarationExpression) {
      return (VariableDeclarationExpression) expression;
    }

    // Create temporary variables for resources declared outside of the try statement.
    return VariableDeclarationExpression.newBuilder()
        .addVariableDeclaration(
            Variable.newBuilder()
                .setName("$resource")
                .setTypeDescriptor(expression.getTypeDescriptor())
                .setFinal(true)
                .build(),
            expression)
        .build();
  }

  protected Expression convertConstantToLiteral(
      Object constantValue, TypeDescriptor typeDescriptor) {
    if (constantValue instanceof Boolean) {
      return (boolean) constantValue ? BooleanLiteral.get(true) : BooleanLiteral.get(false);
    }
    if (constantValue instanceof Number) {
      return new NumberLiteral(typeDescriptor.toUnboxedType(), (Number) constantValue);
    }
    if (constantValue instanceof Character) {
      return NumberLiteral.fromChar((Character) constantValue);
    }
    if (constantValue instanceof String) {
      return new StringLiteral((String) constantValue);
    }
    throw internalCompilerError(
        "Unexpected type for compile time constant: %s", constantValue.getClass().getSimpleName());
  }

  @FormatMethod
  protected Error internalCompilerError(Throwable e, String format, Object... params) {
    return new InternalCompilerError(e, internalCompilerErrorMessage(format, params));
  }

  @FormatMethod
  protected Error internalCompilerError(String format, Object... params) {
    return new InternalCompilerError(internalCompilerErrorMessage(format, params));
  }

  @FormatMethod
  protected String internalCompilerErrorMessage(String format, Object... params) {
    return String.format(format, params) + ", in file: " + currentSourceFile;
  }
}
    List<Variable> parameters =
        AstUtils.createParameterVariables(functionalMethodDescriptor.getParameterTypeDescriptors());

    List<Variable> forwardingParameters = parameters;
    if (!targetMethodDescriptor.isStatic()
        && (qualifier == null || qualifier instanceof JavaScriptConstructorReference)) {
      // The qualifier for the instance method becomes the first parameter. Method references to
      // instance methods without an explicit qualifier use the first parameter in the functional
      // interface as the qualifier for the method call.
      checkArgument(
          parameters.size() == targetMethodDescriptor.getParameterTypeDescriptors().size() + 1
              || (parameters.size() >= targetMethodDescriptor.getParameterTypeDescriptors().size()
                  && targetMethodDescriptor.isVarargs()));
      qualifier = parameters.get(0).createReference();
      forwardingParameters = parameters.subList(1, parameters.size());
    }

    Statement forwardingStatement =
            qualifier,
            targetMethodDescriptor,
            isStaticDispatch,
            forwardingParameters.stream().map(Variable::createReference).collect(toImmutableList()),
            functionalMethodDescriptor.getReturnTypeDescriptor());
    return FunctionExpression.newBuilder()
        .setTypeDescriptor(expressionTypeDescriptor)

<MSG> [KT] Handle references to extension functions/properties

PiperOrigin-RevId: 454795999

<DFF> @@ -45,6 +45,7 @@ import com.google.j2cl.transpiler.ast.VariableDeclarationExpression;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.function.Supplier;
+import java.util.stream.Stream;
 
 /** Base class for implementing that AST conversion from different front ends. */
 public abstract class AbstractCompilationUnitBuilder {
@@ -168,18 +169,49 @@ public abstract class AbstractCompilationUnitBuilder {
     List<Variable> parameters =
         AstUtils.createParameterVariables(functionalMethodDescriptor.getParameterTypeDescriptors());
 
-    List<Variable> forwardingParameters = parameters;
-    if (!targetMethodDescriptor.isStatic()
-        && (qualifier == null || qualifier instanceof JavaScriptConstructorReference)) {
-      // The qualifier for the instance method becomes the first parameter. Method references to
-      // instance methods without an explicit qualifier use the first parameter in the functional
-      // interface as the qualifier for the method call.
+    // Does the method reference have a qualifier? I.e., the qualifier is not null and is not a
+    // class name (modeled as a JavaScriptConstructorReference). Used in unqualified instance
+    // method/Kotlin extension transformations below.
+    boolean hasQualifier =
+        qualifier != null && !(qualifier instanceof JavaScriptConstructorReference);
+
+    ImmutableList<Expression> forwardedArguments;
+    if (!targetMethodDescriptor.isStatic() && !hasQualifier) {
+      // This is a reference to an instance method without an explicit qualifier, e.g.:
+      //
+      // Class::instanceMethod
+      //
+      // The first parameter of the functional interface becomes the qualifier for the method call:
+      //
+      // (a, b) -> { a.instanceMethod(b) }
       checkArgument(
           parameters.size() == targetMethodDescriptor.getParameterTypeDescriptors().size() + 1
               || (parameters.size() >= targetMethodDescriptor.getParameterTypeDescriptors().size()
                   && targetMethodDescriptor.isVarargs()));
       qualifier = parameters.get(0).createReference();
-      forwardingParameters = parameters.subList(1, parameters.size());
+      forwardedArguments =
+          parameters.stream().skip(1).map(Variable::createReference).collect(toImmutableList());
+    } else if (targetMethodDescriptor.isStatic() && hasQualifier) {
+      // This is a reference to a static method but has an explicit qualifier. This path cannot
+      // be invoked by Java method references, because references to static methods in Java cannot
+      // have qualifiers. It can only be a Kotlin extension method, e.g.:
+      //
+      // q::extensionMethod
+      //
+      // We pass the qualifier as the first argument of the referenced method:
+      //
+      // (a, b) -> { extensionMethod(q, a, b) }
+      // TODO(b/233775253): Handle references to methods with varargs/default args (Kotlin)
+      checkArgument(
+          parameters.size() + 1 == targetMethodDescriptor.getParameterTypeDescriptors().size());
+      forwardedArguments =
+          Stream.concat(Stream.of(qualifier), parameters.stream().map(Variable::createReference))
+              .collect(toImmutableList());
+      qualifier = null;
+    } else {
+      // General case: forward all parameters normally.
+      forwardedArguments =
+          parameters.stream().map(Variable::createReference).collect(toImmutableList());
     }
 
     Statement forwardingStatement =
@@ -188,7 +220,7 @@ public abstract class AbstractCompilationUnitBuilder {
             qualifier,
             targetMethodDescriptor,
             isStaticDispatch,
-            forwardingParameters.stream().map(Variable::createReference).collect(toImmutableList()),
+            forwardedArguments,
             functionalMethodDescriptor.getReturnTypeDescriptor());
     return FunctionExpression.newBuilder()
         .setTypeDescriptor(expressionTypeDescriptor)

{'addition_count': 40, 'commit_subject': '[KT] Handle references to extension functions/properties', 'deletion_count': 8, 'file_extension': '.java', 'lang': 'java', 'license': 'apache-2.0', 'repo_name': 'google/j2cl'}