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

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

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

import static java.lang.reflect.Modifier.PRIVATE;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.UnsafeNativeLong;
import com.google.gwt.core.ext.GeneratorContext;
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.JField;
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.JMethod;
import com.google.gwt.dev.jjs.ast.JMethodCall;
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.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.io.PrintWriter;
import java.lang.reflect.Field;
import java.util.List;

public abstract class FieldGenerator extends MemberGenerator implements MagicMethodGenerator {

  protected abstract boolean isDeclared();

  protected JMethodCall getFactoryMethod(final TreeLogger logger, final JMethodCall callSite,
      final JMethod enclosingMethod, final Context context, final JClassLiteral classLit, final JExpression inst, final JExpression arg0, final UnifyAstView ast)
          throws UnableToCompleteException {

    if (classLit == null) {
      if (logger.isLoggable(logLevel())) {
        logger.log(logLevel(),
            "Non-final class literal used to invoke reflection field; "
                + ReflectionUtilAst.debug(callSite.getInstance()));
      }
      return checkConstPool(ast, callSite, inst, arg0);
    }

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

    // 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 JType ref = classLit.getRefType();
    final JClassType oracleType = ast.getTypeOracle().findType(ref.getName().replace('$', '.'));
    final com.google.gwt.core.ext.typeinfo.JField field =
        ReflectionUtilType.findField(logger, oracleType, name, isDeclared());

    if (field == null) {
      // We fail here because the requested method is not findable.
      if (shouldFailIfMissing(logger, ast)) {
        logger.log(Type.ERROR, "Unable to find field " + oracleType.getQualifiedSourceName()+"."+name+ ";");
        logger.log(Type.ERROR, "Did you forget to call StandardGeneratorContext.finish()?");
        throw new UnableToCompleteException();
      } else {
        return checkConstPool(ast, callSite, inst, arg0);
      }
    }
    if (logger.isLoggable(logLevel())) {
      logger.log(logLevel(), "Found injectable field " + field);
    }

    // now, get or make a handle to the requested method,
    return getFieldProvider(logger, ctx, field, classLit, isDeclared());
  }

  @Override
  public JExpression injectMagic(final TreeLogger logger, final JMethodCall callSite,
      final JMethod enclosingMethod, final Context context, final UnifyAstView ast)
      throws UnableToCompleteException {

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

    // and return a call to the generated Method provider
    return
        getFactoryMethod(logger, callSite, enclosingMethod, context, classLit, inst, arg0, ast)
        .makeStatement().getExpr();
  }

  public JMethodCall getFieldProvider(final TreeLogger logger, final ReflectionGeneratorContext ctx, final com.google.gwt.core.ext.typeinfo.JField field,
      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 = getOrMakeFieldFactory(logger, ctx, field, field.getEnclosingType(), manifest, declaredOnly);
    ctx.finish(logger);
    final UnifyAstView ast = ctx.getAst();
    final JDeclaredType factory = ast.searchForTypeBySource(factoryCls);
    // 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 Field subclass "+factoryCls);
    throw new UnableToCompleteException();
  }

  public String getOrMakeFieldFactory(final TreeLogger logger, final ReflectionGeneratorContext ctx, final com.google.gwt.core.ext.typeinfo.JField field,
      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 = field.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 fieldFactoryName = FieldGenerator.getFieldFactoryName(cls, name);
    JClassType factory;
    final String pkgName = field.getEnclosingType().getPackage().getName();
    factory = oracle.findType(pkgName, fieldFactoryName);
    if (factory == null) {
      return generateFieldFactory(logger, ctx, field, fieldFactoryName, manifest);
    } else {
      return (pkgName.length()==0?"":pkgName+".")+ fieldFactoryName;
    }
  }

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

  public static String getFieldFactoryName(final JClassType type,
    final String name) {
    final StringBuilder b = new StringBuilder(type.getName());
    b.append(MemberGenerator.FIELD_SPACER).append(name);
    return b.toString();
  }

  public String generateFieldFactory(final TreeLogger logger,
      final ReflectionGeneratorContext ctx,
      final JField field, String factoryName, final ReflectionManifest manifest)
        throws UnableToCompleteException {
      final String pkg = field.getEnclosingType().getPackage().getName();
      final JClassType enclosingType = field.getEnclosingType();
      final com.google.gwt.core.ext.typeinfo.JType fieldType = field.getType().getErasedType();
      final String jni = field.getType().getJNISignature();
      factoryName = factoryName.replace('.', '_');

      final SourceBuilder out = new SourceBuilder
      ("public final class " + factoryName).setPackage(pkg);

      out.getClassBuffer().createConstructor(PRIVATE);

      final GeneratorContext context = ctx.getGeneratorContext();
      final PrintWriter pw = context.tryCreate(logger, pkg, factoryName);
      if (pw == null) {
        if (isDebug(enclosingType, ReflectionStrategy.FIELD)) {
          logger.log(Type.INFO, "Skipped writing field for " + factoryName
            + ", as factory already exists");
        }
        return out.getQualifiedName();
      }

      final ClassBuffer cb = out.getClassBuffer();

      final GwtRetention retention = manifest.getRetention(field);

      final String ref = (field.isStatic() ? "" : "o.") + "@"
        + enclosingType.getQualifiedSourceName() + "::" + field.getName();
      final MethodBuffer accessor = cb
        .createMethod("private static JavaScriptObject getAccessor()")
        .setUseJsni(true)
        .println("return {").indent();
      accessor
      .println("getter: function(o) {");
      if (!field.isStatic()) {
        accessor.indentln(NULL_CHECK);
      }
      final boolean isPrimitive = field.getType().isPrimitive() != null;

      accessor.indent().print("return ");
      accessor.append(ref);

      accessor
      .println(";")
      .outdent()
      .print("}");
      if (field.isFinal()) {
        accessor.println().outdent().println("};");
      } else {
        accessor.println(", setter: function(o, v) {");
        if (!field.isStatic()) {
          accessor.indentln(NULL_CHECK);
        }
        accessor.indentln(ref + " = ");

        final StringBuilder unboxer = new StringBuilder();
        unboxer.append("v");

        accessor
        .indentln(unboxer + ";")
        .println("}")
        .outdent().println("};");
      }

      final MethodBuffer instantiate = cb
        .createMethod("public static Field instantiate()")
        .print("return new ")
        .addImports(Field.class, JavaScriptObject.class);

      if (isPrimitive) {
        switch (jni.charAt(0)) {
        case 'Z':
          instantiate.addImport("java.lang.reflect.Boolean_Field");
          instantiate.print("Boolean_Field(");
          break;
        case 'B':
          instantiate.addImport("java.lang.reflect.Byte_Field");
          instantiate.print("Byte_Field(");
          break;
        case 'S':
          instantiate.addImport("java.lang.reflect.Short_Field");
          instantiate.print("Short_Field(");
          break;
        case 'C':
          instantiate.addImport("java.lang.reflect.Char_Field");
          instantiate.print("Char_Field(");
          break;
        case 'I':
          instantiate.addImport("java.lang.reflect.Int_Field");
          instantiate.print("Int_Field(");
          break;
        case 'J':
          accessor.addAnnotation(UnsafeNativeLong.class);
          instantiate.addImport("java.lang.reflect.Long_Field");
          instantiate.print("Long_Field(");
          break;
        case 'F':
          instantiate.addImport("java.lang.reflect.Float_Field");
          instantiate.print("Float_Field(");
          break;
        case 'D':
          instantiate.addImport("java.lang.reflect.Double_Field");
          instantiate.print("Double_Field(");
          break;
        default:
          logger.log(Type.ERROR, "Bad primitive type in field generator "
            + fieldType.getQualifiedSourceName());
          throw new UnableToCompleteException();
        }
      } else {
        final String imported = instantiate.addImport(fieldType
          .getQualifiedSourceName());
        instantiate.print("Field(" + imported + ".class, ");
      }

      final String enclosing = instantiate.addImport(field.getEnclosingType()
        .getQualifiedSourceName());
      instantiate
        .print(enclosing + ".class, ")
        .print("\"" + field.getName() + "\", ")
        .print(ReflectionUtilType.getModifiers(field) + ", getAccessor(), ");

      appendAnnotationSupplier(logger, instantiate, field, retention, ctx);

      instantiate.print(");");

      final String src = out.toString();
      pw.println(src);
      if (isDebug(enclosingType, ReflectionStrategy.FIELD)) {
        logger.log(Type.INFO, "Field provider for " + field.toString() + "\n"
          + src);
      }

      ConstPoolGenerator.maybeCommit(logger, context);
      context.commit(logger, pw);
      return out.getQualifiedName();
    }

  /**
   * @see com.google.gwt.reflect.rebind.generators.MemberGenerator#getNotFoundExceptionType()
   */
  @Override
  protected IsQualified getNotFoundExceptionType() {
    return new IsQualified("java.lang", NoSuchFieldException.class.getSimpleName());
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy