package xapi.dev.collect; import java.io.PrintWriter; import java.util.Queue; import javax.inject.Provider; 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.dev.javac.StandardGeneratorContext; import com.google.gwt.dev.jjs.MagicMethodGenerator; import com.google.gwt.dev.jjs.SourceInfo; 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.JConstructor; 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.JNewInstance; import com.google.gwt.dev.jjs.impl.UnifyAst.UnifyVisitor; import com.google.gwt.reflect.rebind.ReflectionUtilAst; import xapi.dev.source.ClassBuffer; import xapi.dev.source.SourceBuilder; import xapi.gwt.collect.IntToListGwt; import xapi.source.X_Source; public class IntToInjector implements MagicMethodGenerator, UnifyAstListener { private JMethod createMethod; private JDeclaredType intToListType; private JMethod createForClassMethod; @Override public void destroy(final TreeLogger arg0) { createMethod = null; createForClassMethod = null; intToListType = null; } @Override public JExpression injectMagic(final TreeLogger logger, final JMethodCall methodCall, final JMethod enclosingMethod, final Context context, final UnifyAstView ast) throws UnableToCompleteException { index(ast); final SourceInfo info = methodCall.getSourceInfo().makeChild(); final JExpression classArg = methodCall.getArgs().get(0); final JClassLiteral classLit = ReflectionUtilAst.extractClassLiteral( logger, classArg, ast, false); if (classLit == null) { // If the code did not send a class literal, then we should defer to // making a runtime call that relies on runtime array reflection. // TODO TRACE a warning for this if (logger.isLoggable(Type.TRACE)) { logger.log(Type.TRACE, "Encountered a non-class literal instantiation " + "of an IntTo class @ "+enclosingMethod.getSignature()); } return new JMethodCall(info, null, createForClassMethod, classArg); } final String litType = classLit.getRefType().getName(); final String binaryName = classLit.getRefType().getName(), typeName = binaryName.replace('$', '_'); final String providerName = typeName+"_ArrayProvider"; JDeclaredType providerType; try { try { providerType = ast.searchForTypeBySource(providerName); } catch (final NoClassDefFoundError e) { throw new UnableToCompleteException(); } } catch (final UnableToCompleteException ignored) { providerType = null; } if (providerType == null) { final String[] names = X_Source.splitClassName(binaryName); if ("java".equals(names[0])) { names[0] = "javax"; } final String componentType = names[1].replace('$', '.'); names[1] = names[1].replace('$', '_'); final SourceBuilder<Object> builder = new SourceBuilder<>("public final class "+names[1]+"_ArrayProvider"); final String simpleType = builder.getImports() .addImports(Provider.class) .addImport(X_Source.qualifiedName(names[0], componentType)); builder.setPackage(names[0]); final ClassBuffer out = builder.getClassBuffer().addInterface("Provider<"+simpleType+"[]>"); out.createMethod("public final "+simpleType+"[] get()") .returnValue("new "+simpleType.split("<")[0] +"[0]"); final StandardGeneratorContext gen = ast.getGeneratorContext(); final PrintWriter pw = gen.tryCreate(logger, names[0], out.getSimpleName()); pw.print(builder.toString()); gen.commit(logger, pw); gen.finish(logger); providerType = ast.searchForTypeBySource(builder.getQualifiedName()); providerType = ast.translate(providerType); } for (final JMethod ctor : providerType.getMethods()) { if (ctor instanceof JConstructor) { final JMethodCall create = new JMethodCall(info, null, createMethod, new JNewInstance(info, (JConstructor)ctor)); ast.translate(providerType); return create; } } logger.log(Type.ERROR, "Unable to complete generation of IntTo<"+litType+">"); throw new UnableToCompleteException(); } @Override public boolean onUnifyAstPostProcess(final TreeLogger arg0, final UnifyAstView arg1, final UnifyVisitor arg2, final Queue<JMethod> arg3) { return false; } @Override public void onUnifyAstStart(final TreeLogger arg0, final UnifyAstView arg1, final UnifyVisitor arg2, final Queue<JMethod> arg3) { } private JDeclaredType index(final UnifyAstView ast) { if (intToListType != null) { return intToListType; } ast.getProgram().addIndexedTypeName(IntToListGwt.class.getName()); intToListType = ast.searchForTypeBySource(IntToListGwt.class.getName()); for (final JMethod method : intToListType.getMethods()) { if (method.getName().equals("create")) { createMethod = ast.translate(method); if (createForClassMethod != null) { break; } } else if (method.getName().equals("createForClass")) { createForClassMethod = ast.translate(method); if (createMethod != null) { break; } } } return intToListType; } }