All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.gwt.reflect.rebind.generators.MethodGenerator Maven / Gradle / Ivy

The newest version!
package com.google.gwt.reflect.rebind.generators;

import static java.lang.reflect.Modifier.FINAL;
import static java.lang.reflect.Modifier.PRIVATE;
import static java.lang.reflect.Modifier.PUBLIC;
import static java.lang.reflect.Modifier.STATIC;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.TreeLogger.Type;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JParameter;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.dev.jjs.MagicMethodGenerator;
import com.google.gwt.dev.jjs.UnifyAstView;
import com.google.gwt.dev.jjs.ast.Context;
import com.google.gwt.dev.jjs.ast.JClassLiteral;
import com.google.gwt.dev.jjs.ast.JDeclaredType;
import com.google.gwt.dev.jjs.ast.JExpression;
import com.google.gwt.dev.jjs.ast.JIntLiteral;
import com.google.gwt.dev.jjs.ast.JMethod;
import com.google.gwt.dev.jjs.ast.JMethodCall;
import com.google.gwt.dev.jjs.ast.JNewArray;
import com.google.gwt.dev.jjs.ast.JStringLiteral;
import com.google.gwt.dev.jjs.ast.JType;
import com.google.gwt.reflect.client.strategy.GwtRetention;
import com.google.gwt.reflect.client.strategy.ReflectionStrategy;
import com.google.gwt.reflect.rebind.ReflectionManifest;
import com.google.gwt.reflect.rebind.ReflectionUtilAst;
import com.google.gwt.reflect.rebind.ReflectionUtilType;
import com.google.gwt.reflect.shared.GwtReflect;
import com.google.gwt.thirdparty.xapi.dev.source.ClassBuffer;
import com.google.gwt.thirdparty.xapi.dev.source.MethodBuffer;
import com.google.gwt.thirdparty.xapi.dev.source.SourceBuilder;
import com.google.gwt.thirdparty.xapi.source.read.JavaModel.IsQualified;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

public abstract class MethodGenerator extends MemberGenerator implements MagicMethodGenerator {

  protected abstract boolean isDeclared();

  @Override
  public JExpression injectMagic(final TreeLogger logger, final JMethodCall callSite,
      final JMethod enclosingMethod, final Context context, final UnifyAstView ast)
      throws UnableToCompleteException {
    final boolean isDebug = logger.isLoggable(logLevel());
    final boolean isFromGwtReflect = callSite.getArgs().size() > 2;
    final JExpression inst = isFromGwtReflect ? callSite.getArgs().get(0) : callSite.getInstance();


    if (isDebug) {
      logger.log(logLevel(), "Searching for class literal from "+inst.getClass().getName()+": "+inst);
    }

    final JClassLiteral classLit = ReflectionUtilAst.extractClassLiteral(logger, inst, ast, false);
    final List args = callSite.getArgs();
    final JExpression arg0 = args.get(isFromGwtReflect?1:0), arg1 = args.get(isFromGwtReflect?2:1);

    if (classLit == null) {
      if (isDebug) {
        logger.log(logLevel(),
            "Non-final class literal used to invoke reflection method; "
                + ReflectionUtilAst.debug(callSite.getInstance()));
      }
      return checkConstPool(ast, callSite, inst, arg0, arg1);
    }
    if (isDebug) {
      logger.log(logLevel(), "Found class literal "+classLit.getRefType().getName());
      logger.log(logLevel(), "Searching for method name from "+arg0.getClass().getName()+": "+arg0);
    }

    final JStringLiteral stringLit = ReflectionUtilAst.extractImmutableNode(logger, JStringLiteral.class, arg0, ast, false);
    if (stringLit == null) {
      if (isDebug) {
        logger.log(logLevel(),
            "Non-final string arg used to retrieve reflection method; "
                + ReflectionUtilAst.debug(arg0));
      }
      return checkConstPool(ast, callSite, inst, arg0, arg1);
    }
    final String name = stringLit.getValue();

    if (isDebug) {
      logger.log(logLevel(), "Found method name "+name);
      logger.log(logLevel(), "Searching for parameter names from "+arg1.getClass().getName()+": "+arg1);
    }

    final JNewArray newArray = ReflectionUtilAst.extractImmutableNode(logger, JNewArray.class, arg1, ast, false);
    if (newArray == null) {
      if (isDebug) {
        logger.log(logLevel(),
            "Non-final array arg used to retrieve reflection method "+name+" "
                + ReflectionUtilAst.debug(arg1));
      }
      return checkConstPool(ast, callSite, inst, arg0, arg1);
    }
    if (isDebug) {
      logger.log(logLevel(), "Found parameter arguments: "+newArray.initializers);
    }

    final ArrayList params = new ArrayList();
    if (newArray.initializers == null) {
      assert newArray.dims.size() == 1;
      final JIntLiteral size = ReflectionUtilAst.extractImmutableNode(logger, JIntLiteral.class,
          newArray.dims.get(0), ast, true);
      if (size.getValue() != 0) {
        logger.log(Type.ERROR, "Cannot provide empty arrays to method injectors"
            + " unless they are of size [0].");
        return checkConstPool(ast, callSite, inst, arg0, arg1);
      }
    } else {
      for (final JExpression expr : newArray.initializers) {
        if (isDebug) {
          logger.log(logLevel(), "Resolving parameter argument: "+expr.getClass().getName()+": "+expr);
        }
        final JClassLiteral type = ReflectionUtilAst.extractClassLiteral(logger, expr, ast, false);
        if (type == null) {
          if (isDebug) {
            logger.log(logLevel(),
                "Encountered non-class literal parameter argument "+expr.getClass().getName()+" : "+expr);
          }
          return checkConstPool(ast, callSite, inst, arg0, arg1);
        } else {
          params.add(type.getRefType());
        }
      }
    }

    // We got all our literals; the class, method name and parameter classes
    // now get the requested method
    final ReflectionGeneratorContext ctx = new ReflectionGeneratorContext(logger, classLit, callSite, enclosingMethod, context, ast);
    final String className = classLit.getRefType().getName();
    final JDeclaredType res = ast.searchForTypeBySource(className);
    final JClassType oracleType = ast.getTypeOracle().findType(className);
    final com.google.gwt.core.ext.typeinfo.JMethod method =
        ReflectionUtilType.findMethod(logger, oracleType, name, ReflectionUtilAst.getTypeNames(params), isDeclared());

    if (method == null) {
      // We fail here because the requested method is not findable.
      logger.log(Type.WARN, "Unable to find method " + oracleType.getQualifiedSourceName()+"."+name+ "("+params+").");
      logger.log(Type.WARN, "Did you forget to call StandardGeneratorContext.finish()?");
      logger.log(Type.WARN, "Returning `throw new MethodNotFoundException()` to match JVM behavior");
      return throwNotFoundException(logger, callSite, ast);
    }
    if (logger.isLoggable(Type.TRACE)) {
      logger.log(Type.TRACE, "Found method " + method);
    }

    // now, get or make a handle to the requested method,
    final JMethodCall methodFactory = getMethodProvider(logger, ctx, method, classLit, isDeclared());
    // and return a call to the generated Method provider
    return methodFactory.makeStatement().getExpr();
  }

  public JMethodCall getMethodProvider(final TreeLogger logger, final ReflectionGeneratorContext ctx, final com.google.gwt.core.ext.typeinfo.JMethod method,
      final JClassLiteral classLit, final boolean declaredOnly) throws UnableToCompleteException {
    final String clsName = classLit.getRefType().getName();
    final ReflectionManifest manifest = ReflectionManifest.getReflectionManifest(logger, clsName, ctx.getGeneratorContext());
    final String factoryCls = getOrMakeMethodFactory(logger, ctx, method, method.getEnclosingType(), manifest, declaredOnly);
    ctx.finish(logger);
    JDeclaredType factory = ctx.getAst().searchForTypeBySource(factoryCls);
    factory = ctx.getAst().translate(factory);
    // pull out the static accessor method
    for (final JMethod factoryMethod : factory.getMethods()) {
      if (factoryMethod.isStatic() && factoryMethod.getName().equals("instantiate")) {
        return new JMethodCall(factoryMethod.getSourceInfo(), null, factoryMethod);
      }
    }
    logger.log(Type.ERROR, "Unable to find static initializer for Method subclass "+factoryCls);
    throw new UnableToCompleteException();
  }

  public String getOrMakeMethodFactory(final TreeLogger logger, final ReflectionGeneratorContext ctx, final com.google.gwt.core.ext.typeinfo.JMethod method,
      final com.google.gwt.core.ext.typeinfo.JType classType, final ReflectionManifest manifest, final boolean declaredOnly) throws UnableToCompleteException {
    // get cached manifest for this type
    final String clsName = classType.getQualifiedSourceName();
    final TypeOracle oracle = ctx.getTypeOracle();
    final String name = method.getName();
    final JClassType cls = oracle.findType(clsName);
    if (cls == null) {
      logger.log(Type.ERROR, "Unable to find enclosing class "+clsName);
      throw new UnableToCompleteException();
    }

    final String methodFactoryName = MethodGenerator.getMethodFactoryName(cls, name, method.getParameters());
    JClassType factory;
    final String pkgName = method.getEnclosingType().getPackage().getName();
    factory = oracle.findType(pkgName, methodFactoryName);
    if (factory == null) {
      return generateMethodFactory(logger, ctx, method, methodFactoryName, manifest);
    } else {
      return (pkgName.length()==0?"":pkgName+".")+ methodFactoryName;
    }
  }

  @Override
  protected String memberGetter() {
    return "get"+(isDeclared()?"Declared":"")+"Method";
  }

  public static String getMethodFactoryName(final JClassType type,
    final String name,
    final JParameter[] list) {
    final StringBuilder b = new StringBuilder(type.getName());
    b.append(MemberGenerator.METHOD_SPACER).append(name);
    // Check for polymorphism
    final com.google.gwt.core.ext.typeinfo.JMethod[] overloads = type
      .getOverloads(name);
    if (overloads.length > 1) {
      // Have to use the parameters to make a unique name.
      // Might be worth it to move this method to instance level, and use a count
      final String uniqueName = ReflectionUtilType.toUniqueFactory(list,
        overloads);
      b.append('_').append(uniqueName);
    }
    return b.toString();
  }

  public String generateMethodFactory(final TreeLogger logger,
    final ReflectionGeneratorContext ctx,
    final com.google.gwt.core.ext.typeinfo.JMethod method,
    String factory, final ReflectionManifest manifest)
      throws UnableToCompleteException {


    final JClassType type = method.getEnclosingType();
    final String pkg = type.getPackage().getName();
    factory = factory.replace('.', '_');
    final SourceBuilder out = ctx.tryCreate(PUBLIC | FINAL, pkg, factory);

    if (out == null) {
      // TODO some kind of test to see if structure has changed...
      return pkg + "." + factory;
    }

    final String simpleName = out.getImports().addImport(type.getQualifiedSourceName());
    final String returnType = out.getImports().addImport(method.getReturnType().getErasedType().getQualifiedSourceName());
    final ClassBuffer cb = out.getClassBuffer();
    final String methodType = cb.addImport(Method.class);


    cb.createConstructor(Modifier.PRIVATE);
    cb.createField(methodType, "method", PRIVATE | STATIC);

    final MethodBuffer instantiator = cb
      .addImports(GwtReflect.class, JavaScriptObject.class)
      .createMethod(
        "public static " + methodType + " instantiate()")
        .println("if (method == null) {")
        .indent()
        .println("method = new " + methodType + "(")
        .print(simpleName + ".class, ")
        .print(returnType + ".class, ")
        .print("\"" + method.getName() + "\", ")
        .print(ReflectionUtilType.getModifiers(method) + ", ")
        .println("invoker(), ");


    // Print an array of all annotations retained on this constructor
    final GwtRetention retention = manifest.getRetention(method);
    appendAnnotationSupplier(logger, instantiator, method, retention, ctx);
    instantiator.print(", ");

    // Print the parameter array (also a reference to a constant array)
    appendClassArray(instantiator, method.getParameters(), ctx);
    instantiator.print(", ");

    // Include the throw exceptions
    appendClassArray(instantiator, method.getThrows(), ctx);

    // Finish up the instantiator method
    instantiator
      .println(");")
      .outdent()
      .println("}")
      .returnValue("method");

    // Construct an invoker method
    createInvokerMethod(cb, type, method.getReturnType(), method.getName(),
      method.getParameters(), method.isStatic(), method.isPublic());

    // Possibly log the source we have generated thus far
    if (isDebug(type, ReflectionStrategy.METHOD)) {
      logger.log(Type.INFO, out.toString());
    }

    // Commit the generator context (saves the provider class, and any constants we created)
    ctx.commit(logger);

    return out.getQualifiedName();
  }

  @Override
  protected IsQualified getNotFoundExceptionType() {
    return new IsQualified("java.lang", NoSuchMethodException.class.getSimpleName());
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy