/** Determines if the exported method can be used as-is. */
  private static boolean isIdentityFunction(FragmentGeneratorContext context, JMethod m)
      throws UnableToCompleteException {
    TreeLogger logger =
        context.parentLogger.branch(
            TreeLogger.DEBUG, "Determining identity status of " + m.getName(), null);
    FragmentGeneratorOracle fgo = context.fragmentGeneratorOracle;

    boolean identityOnly = m.isStatic();
    JParameter[] parameters = m.getParameters();

    identityOnly &=
        context
            .fragmentGeneratorOracle
            .findFragmentGenerator(logger, context.typeOracle, m.getReturnType())
            .isIdentity();

    for (int i = 0; i < parameters.length && identityOnly; i++) {
      FragmentGenerator fragmentGenerator =
          fgo.findFragmentGenerator(logger, context.typeOracle, parameters[i].getType());
      identityOnly &= fragmentGenerator.isIdentity();
    }

    return identityOnly;
  }
  /** Writes a linkage function object that will invoke the exported function. */
  private static void writeLinkageInvocation(FragmentGeneratorContext context, JMethod m)
      throws UnableToCompleteException {
    TreeLogger logger =
        context.parentLogger.branch(
            TreeLogger.DEBUG, "Writing function() for " + m.getName(), null);

    SourceWriter sw = context.sw;
    JParameter[] parameters = m.getParameters();
    FragmentGeneratorOracle fgo = context.fragmentGeneratorOracle;
    FragmentGenerator returnFragmentGenerator =
        fgo.findFragmentGenerator(logger, context.typeOracle, m.getReturnType());

    sw.print("function(");
    for (int i = 0; i < parameters.length; i++) {
      sw.print("arg");
      sw.print(String.valueOf(i));
      if (i < parameters.length - 1) {
        sw.print(", ");
      }
    }
    sw.println(") {");
    sw.indent();

    if (returnFragmentGenerator.isIdentity()) {
      sw.print("return ");
    } else {
      sw.print("var javaReturn = ");
    }

    // Don't need to reference the instance on a static method
    if (!m.isStatic()) {
      sw.print(context.parameterName);
      sw.print(".");
    }

    sw.print("@");
    sw.print(m.getEnclosingType().getQualifiedSourceName());
    sw.print("::");
    sw.print(m.getName());
    sw.print("(");

    // Argument list for the Java invocation
    for (int i = 0; i < parameters.length; i++) {
      sw.print(parameters[i].getType().getJNISignature());
    }

    sw.println(")(");
    // Indent the parameters, each on its own like to improve readability
    sw.indent();
    sw.indent();

    for (int i = 0; i < parameters.length; i++) {
      // Create a sub-context to generate the wrap/unwrap logic
      JType returnType = parameters[i].getType();
      FragmentGeneratorContext subParams = new FragmentGeneratorContext(context);
      subParams.returnType = returnType;
      subParams.parameterName = "arg" + i;

      FragmentGenerator fragmentGenerator =
          fgo.findFragmentGenerator(logger, context.typeOracle, returnType);
      if (fragmentGenerator == null) {
        logger.log(
            TreeLogger.ERROR,
            "No fragment generator for " + returnType.getQualifiedSourceName(),
            null);
        throw new UnableToCompleteException();
      }

      fragmentGenerator.fromJS(subParams);

      if (i < parameters.length - 1) {
        sw.println(", ");
      }
    }

    sw.outdent();
    sw.outdent();
    sw.println(");");

    if (!returnFragmentGenerator.isIdentity()) {
      FragmentGeneratorContext returnContext = new FragmentGeneratorContext(context);
      returnContext.parameterName = "javaReturn";
      returnContext.returnType = m.getReturnType();
      sw.print("return ");
      returnFragmentGenerator.toJS(returnContext);
      sw.println(";");
    }

    sw.outdent();
    sw.print("}");
  }