com.google.dart.java2dart.engine.EngineSemanticProcessor.java Source code

Java tutorial

Introduction

Here is the source code for com.google.dart.java2dart.engine.EngineSemanticProcessor.java

Source

/*
 * Copyright (c) 2013, the Dart project authors.
 * 
 * Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 * 
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package com.google.dart.java2dart.engine;

import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.dart.engine.ast.ASTNode;
import com.google.dart.engine.ast.Block;
import com.google.dart.engine.ast.ClassDeclaration;
import com.google.dart.engine.ast.ClassMember;
import com.google.dart.engine.ast.CompilationUnit;
import com.google.dart.engine.ast.CompilationUnitMember;
import com.google.dart.engine.ast.ConstructorDeclaration;
import com.google.dart.engine.ast.ConstructorInitializer;
import com.google.dart.engine.ast.Expression;
import com.google.dart.engine.ast.ExpressionStatement;
import com.google.dart.engine.ast.FieldDeclaration;
import com.google.dart.engine.ast.FormalParameter;
import com.google.dart.engine.ast.ListLiteral;
import com.google.dart.engine.ast.MethodDeclaration;
import com.google.dart.engine.ast.MethodInvocation;
import com.google.dart.engine.ast.NormalFormalParameter;
import com.google.dart.engine.ast.PropertyAccess;
import com.google.dart.engine.ast.SimpleFormalParameter;
import com.google.dart.engine.ast.SimpleIdentifier;
import com.google.dart.engine.ast.SimpleStringLiteral;
import com.google.dart.engine.ast.Statement;
import com.google.dart.engine.ast.TypeName;
import com.google.dart.engine.ast.VariableDeclaration;
import com.google.dart.engine.ast.VariableDeclarationList;
import com.google.dart.engine.ast.VariableDeclarationStatement;
import com.google.dart.engine.ast.visitor.GeneralizingASTVisitor;
import com.google.dart.engine.ast.visitor.RecursiveASTVisitor;
import com.google.dart.engine.scanner.Keyword;
import com.google.dart.engine.scanner.TokenType;
import com.google.dart.java2dart.Context;
import com.google.dart.java2dart.processor.SemanticProcessor;
import com.google.dart.java2dart.util.JavaUtils;

import static com.google.dart.java2dart.util.ASTFactory.assignmentExpression;
import static com.google.dart.java2dart.util.ASTFactory.binaryExpression;
import static com.google.dart.java2dart.util.ASTFactory.block;
import static com.google.dart.java2dart.util.ASTFactory.blockFunctionBody;
import static com.google.dart.java2dart.util.ASTFactory.constructorDeclaration;
import static com.google.dart.java2dart.util.ASTFactory.expressionFunctionBody;
import static com.google.dart.java2dart.util.ASTFactory.expressionStatement;
import static com.google.dart.java2dart.util.ASTFactory.fieldDeclaration;
import static com.google.dart.java2dart.util.ASTFactory.formalParameterList;
import static com.google.dart.java2dart.util.ASTFactory.functionDeclaration;
import static com.google.dart.java2dart.util.ASTFactory.functionExpression;
import static com.google.dart.java2dart.util.ASTFactory.identifier;
import static com.google.dart.java2dart.util.ASTFactory.ifStatement;
import static com.google.dart.java2dart.util.ASTFactory.integer;
import static com.google.dart.java2dart.util.ASTFactory.methodDeclaration;
import static com.google.dart.java2dart.util.ASTFactory.methodInvocation;
import static com.google.dart.java2dart.util.ASTFactory.namedFormalParameter;
import static com.google.dart.java2dart.util.ASTFactory.nullLiteral;
import static com.google.dart.java2dart.util.ASTFactory.prefixExpression;
import static com.google.dart.java2dart.util.ASTFactory.propertyAccess;
import static com.google.dart.java2dart.util.ASTFactory.redirectingConstructorInvocation;
import static com.google.dart.java2dart.util.ASTFactory.returnStatement;
import static com.google.dart.java2dart.util.ASTFactory.simpleFormalParameter;
import static com.google.dart.java2dart.util.ASTFactory.thisExpression;
import static com.google.dart.java2dart.util.ASTFactory.typeName;
import static com.google.dart.java2dart.util.ASTFactory.variableDeclaration;
import static com.google.dart.java2dart.util.ASTFactory.variableDeclarationList;
import static com.google.dart.java2dart.util.ASTFactory.variableDeclarationStatement;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;

import java.io.PrintWriter;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * {@link SemanticProcessor} for Engine.
 */
public class EngineSemanticProcessor extends SemanticProcessor {
    /**
     * Adds "main" function with given {@link Statement}s.
     */
    public static void addMain(CompilationUnit unit, List<Statement> statements) {
        unit.getDeclarations().add(functionDeclaration(null, null, "main",
                functionExpression(formalParameterList(), blockFunctionBody(statements))));
    }

    /**
     * Gather all <code>TestSuite.addTestSuite</code> into "main" function.
     */
    public static boolean gatherTestSuites(final List<Statement> mainStatements, CompilationUnitMember node) {
        if (node instanceof ClassDeclaration) {
            ClassDeclaration classDeclaration = (ClassDeclaration) node;
            if (classDeclaration.getName().getName().equals("TestAll")) {
                node.accept(new RecursiveASTVisitor<Void>() {
                    @Override
                    public Void visitMethodInvocation(MethodInvocation node) {
                        if (node.getMethodName().getName().equals("addTestSuite")) {
                            mainStatements.add(expressionStatement(
                                    methodInvocation(node.getArgumentList().getArguments().get(0), "dartSuite")));
                        }
                        return super.visitMethodInvocation(node);
                    }
                });
                return true;
            }
        }
        return false;
    }

    /**
     * Update {@link ASTNode} translated classes to add constructors with all named parameters and
     * rename "full" constructors with all required positional parameters.
     */
    public static void generateConstructorWithNamedParametersInAST(final Context context, CompilationUnit unit) {
        unit.accept(new RecursiveASTVisitor<Void>() {
            @Override
            public Void visitClassDeclaration(ClassDeclaration node) {
                ITypeBinding binding = context.getNodeTypeBinding(node);
                if (JavaUtils.isSubtype(binding, "com.google.dart.engine.ast.ASTNode")) {
                    List<ClassMember> members = node.getMembers();
                    for (int i = 0; i < members.size(); i++) {
                        ClassMember member = members.get(i);
                        if (member instanceof ConstructorDeclaration) {
                            ConstructorDeclaration constructor = (ConstructorDeclaration) member;
                            // prepare names, rename "full"
                            String optionalName;
                            String fullName;
                            if (constructor.getName() == null) {
                                fullName = "full";
                                context.renameConstructor(constructor, fullName);
                                optionalName = null;
                            } else {
                                optionalName = constructor.getName().getName();
                                fullName = optionalName + "_full";
                                context.renameConstructor(constructor, fullName);
                            }
                            // add constructor with optional names parameters
                            List<FormalParameter> namedParameters = Lists.newArrayList();
                            List<Expression> fullConstructorArguments = Lists.newArrayList();
                            for (FormalParameter parameter : constructor.getParameters().getParameters()) {
                                NormalFormalParameter normalParameter = (NormalFormalParameter) parameter;
                                namedParameters.add(namedFormalParameter(normalParameter, null));
                                fullConstructorArguments.add(normalParameter.getIdentifier());
                            }
                            ConstructorInitializer fullConstructorInvocation = redirectingConstructorInvocation(
                                    fullName, fullConstructorArguments);
                            members.add(++i,
                                    constructorDeclaration(constructor.getDocumentationComment(),
                                            constructor.getReturnType(), identifier(optionalName),
                                            formalParameterList(namedParameters),
                                            Lists.newArrayList(fullConstructorInvocation), null));
                        }
                    }
                }
                return null;
            }
        });
    }

    /**
     * Generates "invokeParserMethodImpl" and supporting Dart code.
     */
    public static void replaceReflectionMethods(final Context context, final PrintWriter pw, ASTNode node) {
        node.accept(new RecursiveASTVisitor<Void>() {
            @Override
            public Void visitClassDeclaration(ClassDeclaration node) {
                List<ClassMember> members = Lists.newArrayList(node.getMembers());
                // visit s usually
                for (ClassMember classMember : members) {
                    classMember.accept(this);
                }
                // use Parser, fill method lookup table
                if (node.getName().getName().equals("Parser")) {
                    Set<String> usedMethodSignatures = Sets.newHashSet();
                    pw.println();
                    pw.print("Map<String, MethodTrampoline> _methodTable_Parser = <String, MethodTrampoline> {");
                    for (ClassMember classMember : members) {
                        Object binding = context.getNodeBinding(classMember);
                        if (classMember instanceof MethodDeclaration && binding instanceof IMethodBinding) {
                            MethodDeclaration method = (MethodDeclaration) classMember;
                            IMethodBinding methodBinding = (IMethodBinding) binding;
                            if (method.getPropertyKeyword() == null) {
                                int parameterCount = methodBinding.getParameterTypes().length;
                                String methodSignature = methodBinding.getName() + "_" + parameterCount;
                                // don't add method more than once and hope that it is never called
                                if (usedMethodSignatures.contains(methodSignature)) {
                                    continue;
                                } else {
                                    usedMethodSignatures.add(methodSignature);
                                }
                                // generate map entry to method
                                pw.println();
                                pw.print("  '");
                                pw.print(methodSignature);
                                pw.print("': ");
                                pw.print("new MethodTrampoline(");
                                pw.print(parameterCount);
                                pw.print(", (Parser target");
                                for (int i = 0; i < parameterCount; i++) {
                                    pw.print(", arg");
                                    pw.print(i);
                                }
                                pw.print(") => target.");
                                pw.print(method.getName().getName());
                                pw.print("(");
                                for (int i = 0; i < parameterCount; i++) {
                                    if (i != 0) {
                                        pw.print(", ");
                                    }
                                    pw.print("arg");
                                    pw.print(i);
                                }
                                pw.print(")),");
                            }
                        }
                    }
                    pw.println("};");
                    pw.println();
                }
                return null;
            }

            @Override
            public Void visitMethodDeclaration(MethodDeclaration node) {
                String name = node.getName().getName();
                if (name.equals("findParserMethod")) {
                    removeMethod(node);
                }
                if (name.equals("invokeParserMethodImpl")) {
                    removeMethod(node);
                    String source = toSource(
                            "Object invokeParserMethodImpl(Parser parser, String methodName, List<Object> objects, Token tokenStream) {",
                            "  parser.currentToken = tokenStream;",
                            "  MethodTrampoline method = _methodTable_Parser['${methodName}_${objects.length}'];",
                            "  return method.invoke(parser, objects);", "}");
                    pw.print("\n");
                    pw.print(source);
                    pw.print("\n");
                }
                return super.visitMethodDeclaration(node);
            }

            @Override
            public Void visitMethodInvocation(MethodInvocation node) {
                String name = node.getMethodName().getName();
                if (name.equals("invokeParserMethodImpl")) {
                    node.setTarget(null);
                    return null;
                }
                return super.visitMethodInvocation(node);
            }

            private void removeMethod(MethodDeclaration node) {
                ((ClassDeclaration) node.getParent()).getMembers().remove(node);
            }
        });
    }

    /**
     * Find code like:
     * 
     * <pre>
     *   Field scopeField = visitor.getClass().getSuperclass().getDeclaredField("nameScope");
     *   scopeField.setAccessible(true);
     *   Scope outerScope = (Scope) scopeField.get(visitor);
     * </pre>
     * 
     * and replaces it with direct calling of generated public accessors.
     */
    static void rewriteReflectionFieldsWithDirect(final Context context, CompilationUnit unit) {
        final Map<String, String> varToField = Maps.newHashMap();
        final Map<String, String> varToMethod = Maps.newHashMap();
        final Set<Pair<String, String>> refClassFields = Sets.newHashSet();
        final String accessorSuffix = "_J2DAccessor";
        unit.accept(new RecursiveASTVisitor<Void>() {

            @Override
            public Void visitBlock(Block node) {
                List<Statement> statements = ImmutableList.copyOf(node.getStatements());
                for (Statement statement : statements) {
                    statement.accept(this);
                }
                return null;
            }

            @Override
            public Void visitExpressionStatement(ExpressionStatement node) {
                if (node.getExpression() instanceof MethodInvocation) {
                    MethodInvocation invocation = (MethodInvocation) node.getExpression();
                    if (JavaUtils.isMethod(context.getNodeBinding(invocation), "java.lang.reflect.AccessibleObject",
                            "setAccessible")) {
                        ((Block) node.getParent()).getStatements().remove(node);
                        return null;
                    }
                }
                return super.visitExpressionStatement(node);
            }

            @Override
            public Void visitMethodInvocation(MethodInvocation node) {
                List<Expression> arguments = node.getArgumentList().getArguments();
                if (JavaUtils.isMethod(context.getNodeBinding(node), "java.lang.reflect.Field", "get")) {
                    Expression target = arguments.get(0);
                    String varName = ((SimpleIdentifier) node.getTarget()).getName();
                    String fieldName = varToField.get(varName);
                    String accessorName = fieldName + accessorSuffix;
                    SemanticProcessor.replaceNode(node, propertyAccess(target, identifier(accessorName)));
                    return null;
                }
                if (JavaUtils.isMethod(context.getNodeBinding(node), "java.lang.reflect.Field", "set")) {
                    Expression target = arguments.get(0);
                    String varName = ((SimpleIdentifier) node.getTarget()).getName();
                    String fieldName = varToField.get(varName);
                    String accessorName = fieldName + accessorSuffix;
                    SemanticProcessor.replaceNode(node, assignmentExpression(
                            propertyAccess(target, identifier(accessorName)), TokenType.EQ, arguments.get(1)));
                    return null;
                }
                if (JavaUtils.isMethod(context.getNodeBinding(node), "java.lang.reflect.Method", "invoke")) {
                    Expression target = arguments.get(0);
                    String varName = ((SimpleIdentifier) node.getTarget()).getName();
                    String methodName = varToMethod.get(varName);
                    List<Expression> methodArgs;
                    if (arguments.size() == 1) {
                        methodArgs = Lists.newArrayList();
                    } else if (arguments.size() == 2 && arguments.get(1) instanceof ListLiteral) {
                        methodArgs = ((ListLiteral) arguments.get(1)).getElements();
                    } else {
                        methodArgs = Lists.newArrayList(arguments);
                        methodArgs.remove(0);
                    }
                    if (methodName != null) {
                        SemanticProcessor.replaceNode(node, methodInvocation(target, methodName, methodArgs));
                    }
                    return null;
                }
                return super.visitMethodInvocation(node);
            }

            @Override
            public Void visitVariableDeclarationStatement(VariableDeclarationStatement node) {
                super.visitVariableDeclarationStatement(node);
                VariableDeclarationList variableList = node.getVariables();
                ITypeBinding typeBinding = context.getNodeTypeBinding(variableList.getType());
                List<VariableDeclaration> variables = variableList.getVariables();
                if (JavaUtils.isTypeNamed(typeBinding, "java.lang.reflect.Field") && variables.size() == 1) {
                    VariableDeclaration variable = variables.get(0);
                    if (variable.getInitializer() instanceof MethodInvocation) {
                        MethodInvocation initializer = (MethodInvocation) variable.getInitializer();
                        if (JavaUtils.isMethod(context.getNodeBinding(initializer), "java.lang.Class",
                                "getDeclaredField")) {
                            Expression getFieldArgument = initializer.getArgumentList().getArguments().get(0);
                            String varName = variable.getName().getName();
                            String fieldName = ((SimpleStringLiteral) getFieldArgument).getValue();
                            varToField.put(varName, fieldName);
                            ((Block) node.getParent()).getStatements().remove(node);
                            // add (Class, Field) pair to generate accessor later
                            addClassFieldPair(initializer.getTarget(), fieldName);
                        }
                    }
                }
                if (JavaUtils.isTypeNamed(typeBinding, "java.lang.reflect.Method") && variables.size() == 1) {
                    VariableDeclaration variable = variables.get(0);
                    if (variable.getInitializer() instanceof MethodInvocation) {
                        MethodInvocation initializer = (MethodInvocation) variable.getInitializer();
                        if (JavaUtils.isMethod(context.getNodeBinding(initializer), "java.lang.Class",
                                "getDeclaredMethod")) {
                            Expression getMethodArgument = initializer.getArgumentList().getArguments().get(0);
                            String varName = variable.getName().getName();
                            String methodName = ((SimpleStringLiteral) getMethodArgument).getValue();
                            varToMethod.put(varName, methodName);
                            ((Block) node.getParent()).getStatements().remove(node);
                        }
                    }
                }
                return null;
            }

            private void addClassFieldPair(Expression target, String fieldName) {
                while (target instanceof MethodInvocation) {
                    target = ((MethodInvocation) target).getTarget();
                }
                // we expect: object.runtimeType
                if (target instanceof PropertyAccess) {
                    Expression classTarget = ((PropertyAccess) target).getTarget();
                    ITypeBinding classTargetBinding = context.getNodeTypeBinding(classTarget);
                    String className = classTargetBinding.getName();
                    refClassFields.add(Pair.of(className, fieldName));
                }
            }
        });
        // generate private field accessors
        unit.accept(new RecursiveASTVisitor<Void>() {
            @Override
            public Void visitClassDeclaration(ClassDeclaration node) {
                String className = node.getName().getName();
                for (Pair<String, String> pair : refClassFields) {
                    if (pair.getLeft().equals(className)) {
                        String fieldName = pair.getRight();
                        String accessorName = fieldName + accessorSuffix;
                        String privateFieldName = "_" + fieldName;
                        node.getMembers()
                                .add(methodDeclaration(null, null, Keyword.GET, null, identifier(accessorName),
                                        null, expressionFunctionBody(identifier(privateFieldName))));
                        node.getMembers()
                                .add(methodDeclaration(null, null, Keyword.SET, null, identifier(accessorName),
                                        formalParameterList(simpleFormalParameter("__v")),
                                        expressionFunctionBody(assignmentExpression(identifier(privateFieldName),
                                                TokenType.EQ, identifier("__v")))));
                    }
                }
                return super.visitClassDeclaration(node);
            }
        });
    }

    static void useImportPrefix(final Context context, final ASTNode rootNode, final String prefixName,
            final String[] packageNames) {
        rootNode.accept(new RecursiveASTVisitor<Void>() {
            @Override
            public Void visitPropertyAccess(PropertyAccess node) {
                super.visitPropertyAccess(node);
                Expression target = node.getTarget();
                Object binding0 = context.getNodeBinding(target);
                if (binding0 instanceof ITypeBinding) {
                    ITypeBinding binding = (ITypeBinding) binding0;
                    String shortName = binding.getName();
                    shortName = StringUtils.substringBefore(shortName, "<");
                    if (isPrefixPackage(binding)) {
                        SemanticProcessor.replaceNode(target, identifier(prefixName, shortName));
                        return null;
                    }
                }
                return null;
            }

            @Override
            public Void visitTypeName(TypeName node) {
                ITypeBinding binding = context.getNodeTypeBinding(node);
                if (binding != null) {
                    String shortName = binding.getName();
                    shortName = StringUtils.substringBefore(shortName, "<");
                    if (isPrefixPackage(binding)) {
                        node.setName(identifier(prefixName, shortName));
                        return null;
                    }
                }
                return super.visitTypeName(node);
            }

            private boolean isPrefixPackage(ITypeBinding binding) {
                String typeName = binding.getQualifiedName();
                for (String packageName : packageNames) {
                    if (typeName.startsWith(packageName)) {
                        return true;
                    }
                }
                return false;
            }
        });
    }

    private static String toSource(String... lines) {
        return Joiner.on("\n").join(lines);
    }

    public EngineSemanticProcessor(Context context) {
        super(context);
    }

    @Override
    public void process(final CompilationUnit unit) {
        List<CompilationUnitMember> declarations = unit.getDeclarations();
        // remove NodeList, it is declared in enginelib.dart
        for (Iterator<CompilationUnitMember> iter = declarations.iterator(); iter.hasNext();) {
            CompilationUnitMember member = iter.next();
            if (member instanceof ClassDeclaration) {
                ClassDeclaration classDeclaration = (ClassDeclaration) member;
                String name = classDeclaration.getName().getName();
                if (name.equals("NodeList")) {
                    iter.remove();
                }
            }
        }
        // process nodes
        unit.accept(new GeneralizingASTVisitor<Void>() {
            @Override
            public Void visitClassDeclaration(ClassDeclaration node) {
                // visit copy of members (we modify them)
                for (ClassMember member : Lists.newArrayList(node.getMembers())) {
                    member.accept(this);
                }
                ITypeBinding typeBinding = context.getNodeTypeBinding(node);
                // hashCode is broken on Dart VM. So, we generate it using different way.
                // https://code.google.com/p/dart/issues/detail?id=5746
                if (JavaUtils.isTypeNamed(typeBinding, "com.google.dart.engine.ast.ASTNode")) {
                    node.getMembers().add(fieldDeclaration(true, typeName("int"),
                            variableDeclaration("_hashCodeGenerator", integer(0))));
                    node.getMembers()
                            .add(fieldDeclaration(false, Keyword.FINAL, typeName("int"), variableDeclaration(
                                    "hashCode",
                                    prefixExpression(TokenType.PLUS_PLUS, identifier("_hashCodeGenerator")))));
                }
                // "Type" is declared in dart:core, so replace it
                if (JavaUtils.isTypeNamed(typeBinding, "com.google.dart.engine.type.Type")) {
                    SimpleIdentifier nameNode = node.getName();
                    context.renameIdentifier(nameNode, "Type2");
                }
                // done
                return null;
            }

            @Override
            public Void visitFieldDeclaration(FieldDeclaration node) {
                super.visitFieldDeclaration(node);
                ClassDeclaration parentClass = (ClassDeclaration) node.getParent();
                if (parentClass.getName().getName().equals("FileBasedSource")) {
                    for (VariableDeclaration field : node.getFields().getVariables()) {
                        if (field.getName().getName().equals("_UTF_8_CHARSET")) {
                            parentClass.getMembers().remove(node);
                        }
                    }
                }
                return null;
            }

            @Override
            public Void visitMethodDeclaration(MethodDeclaration node) {
                super.visitMethodDeclaration(node);
                IMethodBinding binding = (IMethodBinding) context.getNodeBinding(node);
                String name = node.getName().getName();
                if ("accept".equals(name) && node.getParameters().getParameters().size() == 1) {
                    node.setReturnType(null);
                    FormalParameter formalParameter = node.getParameters().getParameters().get(0);
                    ((SimpleFormalParameter) formalParameter).getType().setTypeArguments(null);
                }
                if (isMethodInClass(binding, "ensureVmIsExecutable", "com.google.dart.engine.sdk.DartSdk")) {
                    node.setBody(blockFunctionBody());
                    return null;
                }
                if (isMethodInClass(binding, "getContents", "com.google.dart.engine.source.FileBasedSource")) {
                    SimpleIdentifier receiverIdent = node.getParameters().getParameters().get(0).getIdentifier();
                    Block tryCacheBlock = block();
                    {
                        tryCacheBlock.getStatements()
                                .add(variableDeclarationStatement(
                                        variableDeclarationList(null, typeName("String"),
                                                variableDeclaration("contents",
                                                        methodInvocation(identifier("_contentCache"), "getContents",
                                                                thisExpression())))));
                        SimpleIdentifier contentsIdent = identifier("contents");
                        Expression modificationStampExpr = methodInvocation(identifier("_contentCache"),
                                "getModificationStamp", thisExpression());
                        tryCacheBlock
                                .getStatements().add(
                                        ifStatement(
                                                binaryExpression(contentsIdent, TokenType.BANG_EQ, nullLiteral()),
                                                block(expressionStatement(methodInvocation(receiverIdent, "accept2",
                                                        contentsIdent, modificationStampExpr)),
                                                        returnStatement())));
                    }
                    ExpressionStatement doReadStatement = expressionStatement(methodInvocation(receiverIdent,
                            "accept2", methodInvocation(identifier("_file"), "readAsStringSync"),
                            methodInvocation(identifier("_file"), "lastModified")));
                    node.setBody(blockFunctionBody(tryCacheBlock, doReadStatement));
                    return null;
                }
                return null;
            }

            @Override
            public Void visitMethodInvocation(MethodInvocation node) {
                List<Expression> args = node.getArgumentList().getArguments();
                if (isMethodInClass(node, "toArray", "com.google.dart.engine.utilities.collection.IntList")) {
                    replaceNode(node, node.getTarget());
                    return null;
                }
                if (isMethodInClass(node, "equals", "com.google.dart.engine.utilities.general.ObjectUtilities")) {
                    replaceNode(node, binaryExpression(args.get(0), TokenType.EQ_EQ, args.get(1)));
                    return null;
                }
                if (isMethodInClass(node, "getContents", "com.google.dart.engine.utilities.io.FileUtilities")) {
                    replaceNode(node, methodInvocation(args.get(0), "readAsStringSync"));
                    return null;
                }
                if (isMethodInClass(node, "getExtension", "com.google.dart.engine.utilities.io.FileUtilities")) {
                    replaceNode(node.getTarget(), identifier("FileNameUtilities"));
                    return null;
                }
                return super.visitMethodInvocation(node);
            }

            @Override
            public Void visitTypeName(TypeName node) {
                if (node.getName() instanceof SimpleIdentifier) {
                    SimpleIdentifier nameNode = (SimpleIdentifier) node.getName();
                    String name = nameNode.getName();
                    if ("IntList".equals(name)) {
                        replaceNode(node, typeName("List", typeName("int")));
                        return null;
                    }
                }
                return super.visitTypeName(node);
            }
        });
    }
}