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

com.google.gwt.reflect.rebind.injectors.MagicClassInjector Maven / Gradle / Ivy

package com.google.gwt.reflect.rebind.injectors;

import java.util.HashMap;
import java.util.Queue;

import xapi.source.read.SourceUtil;

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.dev.javac.StandardGeneratorContext;
import com.google.gwt.dev.jjs.MagicMethodGenerator;
import com.google.gwt.dev.jjs.SourceOrigin;
import com.google.gwt.dev.jjs.UnifyAstListener;
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.JGwtCreate;
import com.google.gwt.dev.jjs.ast.JMethod;
import com.google.gwt.dev.jjs.ast.JMethodBody;
import com.google.gwt.dev.jjs.ast.JMethodCall;
import com.google.gwt.dev.jjs.ast.JType;
import com.google.gwt.dev.jjs.impl.UnifyAst;
import com.google.gwt.dev.jjs.impl.UnifyAst.UnifyVisitor;
import com.google.gwt.reflect.client.strategy.ReflectionStrategy;
import com.google.gwt.reflect.rebind.ReflectionUtilAst;
import com.google.gwt.reflect.rebind.ReflectionUtilJava;
import com.google.gwt.reflect.rebind.generators.GwtAnnotationGenerator;
import com.google.gwt.reflect.rebind.generators.MagicClassGenerator;
import com.google.gwt.reflect.rebind.generators.MemberGenerator;
import com.google.gwt.reflect.rebind.generators.ReflectionGeneratorContext;

public class MagicClassInjector implements MagicMethodGenerator, UnifyAstListener {

  private static final Type logLevel = Type.TRACE;
  private static final ThreadLocal injector = new ThreadLocal();

  public static JExpression injectMagicClass(TreeLogger logger, JMethodCall x, JMethod currentMethod,
    Context context, UnifyAstView ast) throws UnableToCompleteException {
    // defer to our static instance here
    return injector.get().injectMagic(logger, x, currentMethod, context, ast);
  }

  private final HashMap classEnhancers = new HashMap();

  ReflectionStrategy strategy;
 
  /**
   * Instance created by {@link UnifyAst} before any methods are called.
   */
  public MagicClassInjector() {
    assert injector.get() == null : "You cannot create MagicClassInjector more than once; "
        + " if you need to run parallel gwt compiles, run them in different threads.";
    injector.set(this);
  }

  @Override
  public void destroy(TreeLogger logger) {
    // Tell the generator thread to clean itself up;
    // This will allow us to intelligently handle recompiles.
    // Every recompile should generate fresh results,
    // but we don't want to generate a new type if we recursively access the same type twice.
    MagicClassGenerator.cleanup();
    
    
    MemberGenerator.cleanup();
    // Our annotation generator caches types that it has seen.
    // We want to clear these regularly, so our generator
    // can examine cached, generated types, and avoid needless regeneration.
    GwtAnnotationGenerator.cleanup();
    
    
    injector.remove();
  }

    /**
     * Call {@link MagicClassGenerator#execImpl(TreeLogger, ReflectionGeneratorContext, JClassType)},
     * then load (and cache) a jjs {@link JMethodCall} to call the generated class enhancer method.
     * 
     * @param clsName -> The name of the class to rebind
     * @param params -> The {@link ReflectionGeneratorContext}
     * @return -> JMethod call MyClassEnhancer.enhance(MyClass.class)
     * @throws UnableToCompleteException
     */
    public JMethodCall doRebind(String clsName, ReflectionGeneratorContext params) throws UnableToCompleteException {
      // generate
      params.getLogger().log(logLevel, "Binding magic class for " + clsName);
      String srcName = SourceUtil.toSourceName(params.getClazz().getRefType().getName());
      JClassType type = params.getTypeOracle().findType(srcName);
    
      if (type == null) {
        params.getLogger().log(Type.ERROR, "Unable to enhance class; "+srcName+" not found by Gwt type oracle");
        throw new UnableToCompleteException();
      }
      
      StandardGeneratorContext ctx = params.getGeneratorContext();
      String result = MagicClassGenerator.generate(params.getLogger(), params, type);
      ctx.finish(params.getLogger());
    
      params.getLogger().log(logLevel, "Generated Class Enhancer: " + result);
      JDeclaredType success = params.getAst().searchForTypeBySource(result);
    
      //Okay, we've generated the correct magic class subtype;
      //Now pull off its static accessor method to enhance and return our class.
    
      for (JMethod method : success.getMethods()) {
        if (method.isStatic() && method.getName().equals("enhanceClass")) {
          JMethodCall call = new JMethodCall(method.getSourceInfo().makeChild(SourceOrigin.UNKNOWN), null, method);
          call.addArg(params.getClazz().makeStatement().getExpr());
          return call;
        }
      }
      params.getLogger().log(Type.ERROR, "Unable to load .enhanceClass() for "+result);
      throw new UnableToCompleteException();
    }

    @Override
    public JExpression injectMagic(TreeLogger logger, JMethodCall methodCall, JMethod currentMethod,
      Context context, UnifyAstView ast) throws UnableToCompleteException {
    
      JClassLiteral clazz = ReflectionUtilAst.extractClassLiteral(logger, methodCall, 0, ast);
      JType type = clazz.getRefType();
      if (type == null) {
        logger.log(Type.WARN, "ClassLiteral with null reftype: "+clazz.toSource());
        return null;
      }
      return get(type.getName(), new ReflectionGeneratorContext(
          logger, clazz, methodCall, currentMethod, context, ast));
    }

    @Override
    public void onUnifyAstStart(TreeLogger logger, UnifyAstView ast, UnifyVisitor visitor, Queue todo) {
      for (final JMethod method : ast.getProgram().getEntryMethods()) {
        if (method.getBody() instanceof JMethodBody) {
          JMethodBody body = (JMethodBody) method.getBody();
          // obtain the entry point
          EntryPointFinder finder = findEntryPoint(logger);
          body.traverse(finder, finder.getContext());
          // find a default strategy
          if (finder.result == null) {
            strategy = MagicClassGenerator.class.getAnnotation(ReflectionStrategy.class);
          } else {
            com.google.gwt.core.ext.typeinfo.JClassType type = ast.getTypeOracle().findType(finder.result.getName());
            strategy = type.getAnnotation(ReflectionStrategy.class);
            if (strategy == null) {
              strategy = type.getPackage().getAnnotation(ReflectionStrategy.class);
              if (strategy == null) {
                // Nothing on the entry point or it's package;
                // use a default instance of the ReflectionStrategy annotation
                strategy = MagicClassGenerator.class.getAnnotation(ReflectionStrategy.class);
              }
            }
          }
          assert strategy != null;
        }
      }
    }

    @Override
    public boolean onUnifyAstPostProcess(TreeLogger logger, UnifyAstView ast, UnifyVisitor visitor, Queue todo) {
      return false;// no per-permutation post processing needed
    }

    public static ReflectionStrategy getDefaultStrategy() {
      return injector.get().strategy;
    }

    /**
     * Implements a caching layer guarding the {@link #initialize(String, ReflectionGeneratorContext)} method
     * 
     * @param key
     * @param params
     * @return
     */
    protected JExpression get(String key, ReflectionGeneratorContext params) {
      //because we cache results, super dev mode recompiles need to skip the
      //cache if the magic class does not exist, thus we test type presence on every get().

      JDeclaredType type =
        params.getAst().searchForTypeByBinary(params.getClazz().getRefType().getName());
      String typeName = JGwtCreate.nameOf(type);
      String generatedName = ReflectionUtilJava.generatedMagicClassName(typeName);
      try {
        params.getAst().searchForTypeBySource(generatedName);
      }catch(NoClassDefFoundError e) {
        classEnhancers.remove(key);
      }
      if (classEnhancers.containsKey(key)) {
        JMethodCall previous = classEnhancers.get(key);
        previous = new JMethodCall(previous, previous.getInstance());
        previous.addArg(params.getClazz().makeStatement().getExpr());
        return previous.makeStatement().getExpr();
      }
      JMethodCall expr = initialize(key, params);
      classEnhancers.put(key, expr);
      expr.setArg(0, params.getClazz().makeStatement().getExpr());
      return expr.makeStatement().getExpr();
    }

  protected JMethodCall initialize(String clsName, ReflectionGeneratorContext params) {
      try {
        return doRebind(clsName, params);
      } catch (Exception e) {
        if (e instanceof RuntimeException) throw (RuntimeException)e;
        throw new RuntimeException("Could not initialize magic class for " + clsName, e);
      }
    }

  private EntryPointFinder findEntryPoint(TreeLogger logger) {
    return new EntryPointFinder(logger);
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy