Package com.google.gwt.dev.jjs.impl

Source Code of com.google.gwt.dev.jjs.impl.BuildTypeMap$JsParameterResolver

/*
* Copyright 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* 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.gwt.dev.jjs.impl;

import com.google.gwt.dev.javac.JsniCollector;
import com.google.gwt.dev.jjs.HasSourceInfo;
import com.google.gwt.dev.jjs.InternalCompilerException;
import com.google.gwt.dev.jjs.SourceInfo;
import com.google.gwt.dev.jjs.ast.JClassType;
import com.google.gwt.dev.jjs.ast.JDeclaredType;
import com.google.gwt.dev.jjs.ast.JEnumType;
import com.google.gwt.dev.jjs.ast.JField;
import com.google.gwt.dev.jjs.ast.JInterfaceType;
import com.google.gwt.dev.jjs.ast.JLocal;
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.JNewInstance;
import com.google.gwt.dev.jjs.ast.JParameter;
import com.google.gwt.dev.jjs.ast.JParameterRef;
import com.google.gwt.dev.jjs.ast.JPrimitiveType;
import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.jjs.ast.JReferenceType;
import com.google.gwt.dev.jjs.ast.JReturnStatement;
import com.google.gwt.dev.jjs.ast.JType;
import com.google.gwt.dev.jjs.ast.JField.Disposition;
import com.google.gwt.dev.jjs.ast.js.JsniMethodBody;
import com.google.gwt.dev.js.JsAbstractSymbolResolver;
import com.google.gwt.dev.js.ast.JsFunction;
import com.google.gwt.dev.js.ast.JsName;
import com.google.gwt.dev.js.ast.JsNameRef;
import com.google.gwt.dev.js.ast.JsProgram;

import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.CompilationResult;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
import org.eclipse.jdt.internal.compiler.ast.AnnotationMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.LocalTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
import org.eclipse.jdt.internal.compiler.lookup.NestedTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.SyntheticArgumentBinding;
import org.eclipse.jdt.internal.compiler.lookup.SyntheticMethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.util.Util;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

/**
* This is a Builder for {@link TypeMap}. The whole point of this pass is to
* create raw unfinished, unlinked AST nodes for types, methods, fields, and
* parameters, and to map the original JDT nodes to these AST nodes. That way
* when GenerateJavaDom runs, it just uses the TypeMap output from this Builder
* to retrieve whatever referenceable nodes it needs without worrying about
* whether they need to be created. Building our AST from JDT starts here.
*/
public class BuildTypeMap {

  /**
   * Creates JNodes for every method, field, initializer, parameter, and local
   * and memorizes the mapping from the JDT Binding to the corresponding JNode
   * for each thing created. Note that this pass also 1) sets up the super
   * type(s) for any member or local types created in BuildTypeMapVisitor (see
   * the comments there about why it had to be deferred). 2) adds each
   * user-defined type to a flat list. 3) Creates JNodes for all methods and
   * variables and memorizes the mapping from the JDT Binding to the
   * corresponding JNode for each created method and variable. 4) Maps all
   * synthetic arguments and fields for nested and local classes. 5) Slurps in
   * JSNI code for native methods as an opaque string.
   *
   * Note that methods and fields are not added to their classes here, that
   * isn't done until {@link GenerateJavaDom}.
   */
  private static class BuildDeclMapVisitor extends ASTVisitor {

    private String currentFileName;

    private int[] currentSeparatorPositions;

    private final JsProgram jsProgram;
    private JProgram program;
    private List<TypeDeclaration> typeDecls = new ArrayList<TypeDeclaration>();
    private final TypeMap typeMap;

    public BuildDeclMapVisitor(TypeMap typeMap, JsProgram jsProgram) {
      this.typeMap = typeMap;
      program = this.typeMap.getProgram();
      this.jsProgram = jsProgram;
    }

    public TypeDeclaration[] getTypeDeclarataions() {
      return typeDecls.toArray(new TypeDeclaration[typeDecls.size()]);
    }

    @Override
    public boolean visit(AnnotationMethodDeclaration methodDeclaration,
        ClassScope scope) {
      return visit((MethodDeclaration) methodDeclaration, scope);
    }

    @Override
    public boolean visit(Argument argument, BlockScope scope) {
      try {
        if (scope == scope.methodScope()) {
          return true;
        }

        JMethodBody enclosingBody = findEnclosingMethod(scope);
        SourceInfo info = makeSourceInfo(argument, enclosingBody.getMethod());
        LocalVariableBinding b = argument.binding;
        JType localType = (JType) typeMap.get(b.type);
        JLocal newLocal = program.createLocal(info, argument.name, localType,
            b.isFinal(), enclosingBody);
        typeMap.put(b, newLocal);
        return true;
      } catch (Throwable e) {
        throw translateException(argument, e);
      }
    }

    /**
     * Weird: we used to have JConstructor (and JConstructorCall) in our AST,
     * but we got rid of them completely and instead model them as instance
     * methods whose qualifier is a naked no-argument new operation. See
     * {@link GenerateJavaAST.JavaASTGenerationVisitor#processConstructor(ConstructorDeclaration)}
     * for details.
     */
    @Override
    public boolean visit(ConstructorDeclaration ctorDecl, ClassScope scope) {
      try {
        MethodBinding b = ctorDecl.binding;
        JClassType enclosingType = (JClassType) typeMap.get(scope.enclosingSourceType());
        String name = enclosingType.getShortName();
        SourceInfo info = makeSourceInfo(ctorDecl, enclosingType);
        JMethod newMethod = program.createMethod(info, name.toCharArray(),
            enclosingType, enclosingType, false, false, true, b.isPrivate(),
            false);

        // Enums have hidden arguments for name and value
        if (enclosingType.isEnumOrSubclass() != null) {
          program.createParameter(info, "enum$name".toCharArray(),
              program.getTypeJavaLangString(), true, false, newMethod);
          program.createParameter(info, "enum$ordinal".toCharArray(),
              program.getTypePrimitiveInt(), true, false, newMethod);
        }

        // user args
        mapParameters(newMethod, ctorDecl);
        // original params are now frozen

        info.addCorrelation(program.getCorrelator().by(newMethod));

        int syntheticParamCount = 0;
        ReferenceBinding declaringClass = b.declaringClass;
        if (declaringClass.isNestedType() && !declaringClass.isStatic()) {
          // add synthetic args for outer this and locals
          NestedTypeBinding nestedBinding = (NestedTypeBinding) declaringClass;
          Set<String> alreadyNamedVariables = new HashSet<String>();
          if (nestedBinding.enclosingInstances != null) {
            for (int i = 0; i < nestedBinding.enclosingInstances.length; ++i) {
              SyntheticArgumentBinding arg = nestedBinding.enclosingInstances[i];
              String argName = String.valueOf(arg.name);
              if (alreadyNamedVariables.contains(argName)) {
                argName += "_" + i;
              }
              createParameter(arg, argName, newMethod);
              ++syntheticParamCount;
              alreadyNamedVariables.add(argName);
            }
          }

          if (nestedBinding.outerLocalVariables != null) {
            for (int i = 0; i < nestedBinding.outerLocalVariables.length; ++i) {
              SyntheticArgumentBinding arg = nestedBinding.outerLocalVariables[i];
              String argName = String.valueOf(arg.name);
              if (alreadyNamedVariables.contains(argName)) {
                argName += "_" + i;
              }
              createParameter(arg, argName, newMethod);
              ++syntheticParamCount;
              alreadyNamedVariables.add(argName);
            }
          }
        }

        typeMap.put(b, newMethod);

        // Now let's implicitly create a static function called 'new' that will
        // allow construction from JSNI methods
        if (!enclosingType.isAbstract()) {
          ReferenceBinding enclosingBinding = ctorDecl.binding.declaringClass.enclosingType();
          JReferenceType outerType = enclosingBinding == null ? null
              : (JReferenceType) typeMap.get(enclosingBinding);
          createSyntheticConstructor(newMethod,
              ctorDecl.binding.declaringClass.isStatic(), outerType);
        }

        return true;
      } catch (Throwable e) {
        throw translateException(ctorDecl, e);
      }
    }

    @Override
    public boolean visit(FieldDeclaration fieldDeclaration, MethodScope scope) {
      try {
        FieldBinding b = fieldDeclaration.binding;
        JDeclaredType enclosingType = (JDeclaredType) typeMap.get(scope.enclosingSourceType());
        SourceInfo info = makeSourceInfo(fieldDeclaration, enclosingType);
        Expression initialization = fieldDeclaration.initialization;
        if (initialization != null
            && initialization instanceof AllocationExpression
            && ((AllocationExpression) initialization).enumConstant != null) {
          createEnumField(info, b, enclosingType);
        } else {
          createField(info, b, enclosingType);
        }
        return true;
      } catch (Throwable e) {
        throw translateException(fieldDeclaration, e);
      }
    }

    @Override
    public boolean visit(LocalDeclaration localDeclaration, BlockScope scope) {
      try {
        LocalVariableBinding b = localDeclaration.binding;
        JType localType = (JType) typeMap.get(localDeclaration.type.resolvedType);
        JMethodBody enclosingBody = findEnclosingMethod(scope);
        SourceInfo info = makeSourceInfo(localDeclaration,
            enclosingBody.getMethod());
        JLocal newLocal = program.createLocal(info, localDeclaration.name,
            localType, b.isFinal(), enclosingBody);
        typeMap.put(b, newLocal);
        return true;
      } catch (Throwable e) {
        throw translateException(localDeclaration, e);
      }
    }

    @Override
    public boolean visit(MethodDeclaration methodDeclaration, ClassScope scope) {
      try {
        MethodBinding b = methodDeclaration.binding;
        JDeclaredType enclosingType = (JDeclaredType) typeMap.get(scope.enclosingSourceType());
        SourceInfo info = makeSourceInfo(methodDeclaration, enclosingType);
        JMethod newMethod = processMethodBinding(b, enclosingType, info);
        mapParameters(newMethod, methodDeclaration);
        info.addCorrelation(program.getCorrelator().by(newMethod));

        if (newMethod.isNative()) {
          processNativeMethod(methodDeclaration, info, enclosingType, newMethod);
        }

        return true;
      } catch (Throwable e) {
        throw translateException(methodDeclaration, e);
      }
    }

    @Override
    public boolean visit(TypeDeclaration localTypeDeclaration, BlockScope scope) {
      return process(localTypeDeclaration);
    }

    @Override
    public boolean visit(TypeDeclaration memberTypeDeclaration, ClassScope scope) {
      return process(memberTypeDeclaration);
    }

    @Override
    public boolean visit(TypeDeclaration typeDeclaration,
        CompilationUnitScope scope) {
      return process(typeDeclaration);
    }

    private JField createEnumField(SourceInfo info, FieldBinding binding,
        JReferenceType enclosingType) {
      JType type = (JType) typeMap.get(binding.type);
      JField field = program.createEnumField(info, binding.name,
          (JEnumType) enclosingType, (JClassType) type, binding.original().id);
      info.addCorrelation(program.getCorrelator().by(field));
      typeMap.put(binding, field);
      return field;
    }

    private JField createField(SourceInfo info, FieldBinding binding,
        JDeclaredType enclosingType) {
      JType type = (JType) typeMap.get(binding.type);

      boolean isCompileTimeConstant = binding.isStatic() && (binding.isFinal())
          && (binding.constant() != Constant.NotAConstant)
          && (binding.type.isBaseType());
      assert (type instanceof JPrimitiveType || !isCompileTimeConstant);

      assert (!binding.isFinal() || !binding.isVolatile());
      Disposition disposition;
      if (isCompileTimeConstant) {
        disposition = Disposition.COMPILE_TIME_CONSTANT;
      } else if (binding.isFinal()) {
        disposition = Disposition.FINAL;
      } else if (binding.isVolatile()) {
        disposition = Disposition.VOLATILE;
      } else {
        disposition = Disposition.NONE;
      }

      JField field = program.createField(info, binding.name, enclosingType,
          type, binding.isStatic(), disposition);
      typeMap.put(binding, field);
      info.addCorrelation(program.getCorrelator().by(field));
      return field;
    }

    private JField createField(SyntheticArgumentBinding binding,
        JDeclaredType enclosingType) {
      JType type = (JType) typeMap.get(binding.type);
      SourceInfo info = enclosingType.getSourceInfo().makeChild(
          BuildDeclMapVisitor.class, "Field " + String.valueOf(binding.name));
      JField field = program.createField(info, binding.name, enclosingType,
          type, false, Disposition.FINAL);
      info.addCorrelation(program.getCorrelator().by(field));
      if (binding.matchingField != null) {
        typeMap.put(binding.matchingField, field);
      }
      typeMap.put(binding, field);
      return field;
    }

    private JParameter createParameter(LocalVariableBinding binding,
        JMethod enclosingMethod) {
      JType type = (JType) typeMap.get(binding.type);
      SourceInfo info = makeSourceInfo(binding.declaration, enclosingMethod);
      JParameter param = program.createParameter(info, binding.name, type,
          binding.isFinal(), false, enclosingMethod);
      typeMap.put(binding, param);
      return param;
    }

    private JParameter createParameter(SyntheticArgumentBinding arg,
        String argName, JMethod enclosingMethod) {
      JType type = (JType) typeMap.get(arg.type);
      JParameter param = program.createParameter(
          enclosingMethod.getSourceInfo().makeChild(BuildTypeMap.class,
              "Parameter " + argName), argName.toCharArray(), type, true,
          false, enclosingMethod);
      return param;
    }

    /**
     * Create a method that invokes the specified constructor. This is done as
     * an aid to JSNI users to be able to invoke a Java constructor via a method
     * named ::new.
     *
     * @param constructor the constructor to invoke
     * @param staticClass indicates if the class being constructed is static
     * @param enclosingType the type that encloses the type that is to be
     *          constructed. This may be <code>null</code> if the class is a
     *          top-level type.
     */
    private JMethod createSyntheticConstructor(JMethod constructor,
        boolean staticClass, JReferenceType enclosingType) {
      JClassType type = (JClassType) constructor.getEnclosingType();

      // Define the method
      JMethod synthetic = program.createMethod(type.getSourceInfo().makeChild(
          BuildDeclMapVisitor.class, "Synthetic constructor"),
          "new".toCharArray(), type, type, false, true, true, false, false);

      // new Foo() : Create the instance
      JNewInstance newInstance = new JNewInstance(
          type.getSourceInfo().makeChild(BuildDeclMapVisitor.class,
              "new instance"), type);

      // (new Foo()).Foo() : Invoke the constructor method on the instance
      JMethodCall call = new JMethodCall(type.getSourceInfo().makeChild(
          BuildDeclMapVisitor.class, "constructor invocation"), newInstance,
          constructor);
      /*
       * If the type isn't static, make the first parameter a reference to the
       * instance of the enclosing class. It's the first instance to allow the
       * JSNI qualifier to be moved without affecting evaluation order.
       */
      JParameter enclosingInstance = null;
      if (!staticClass) {
        enclosingInstance = program.createParameter(
            synthetic.getSourceInfo().makeChild(BuildDeclMapVisitor.class,
                "outer instance"), "this$outer".toCharArray(), enclosingType,
            false, false, synthetic);
      }

      /*
       * In one pass, add the parameters to the synthetic constructor and
       * arguments to the method call.
       */
      for (Iterator<JParameter> i = constructor.getParams().iterator(); i.hasNext();) {
        JParameter param = i.next();
        /*
         * This supports x.new Inner() by passing the enclosing instance
         * implicitly as the last argument to the constructor.
         */
        if (enclosingInstance != null && !i.hasNext()) {
          call.addArg(new JParameterRef(synthetic.getSourceInfo().makeChild(
              BuildDeclMapVisitor.class, "enclosing instance"),
              enclosingInstance));
        } else {
          JParameter syntheticParam = program.createParameter(
              synthetic.getSourceInfo().makeChild(BuildDeclMapVisitor.class,
                  "Argument " + param.getName()),
              param.getName().toCharArray(), param.getType(), true, false,
              synthetic);
          call.addArg(new JParameterRef(
              syntheticParam.getSourceInfo().makeChild(
                  BuildDeclMapVisitor.class, "reference"), syntheticParam));
        }
      }

      // Lock the method.
      synthetic.freezeParamTypes();

      // return (new Foo()).Foo() : The only statement in the function
      JReturnStatement ret = new JReturnStatement(
          synthetic.getSourceInfo().makeChild(BuildDeclMapVisitor.class,
              "Return statement"), call);

      // Add the return statement to the method body
      JMethodBody body = (JMethodBody) synthetic.getBody();
      body.getBlock().addStmt(ret);

      // Done
      return synthetic;
    }

    private JMethodBody findEnclosingMethod(BlockScope scope) {
      JMethod method;
      MethodScope methodScope = scope.methodScope();
      if (methodScope.isInsideInitializer()) {
        JDeclaredType enclosingType = (JDeclaredType) typeMap.get(scope.classScope().referenceContext.binding);
        if (methodScope.isStatic) {
          // clinit
          method = enclosingType.getMethods().get(0);
        } else {
          // init
          assert (enclosingType instanceof JClassType);
          method = enclosingType.getMethods().get(1);
        }
      } else {
        AbstractMethodDeclaration referenceMethod = methodScope.referenceMethod();
        method = (JMethod) typeMap.get(referenceMethod.binding);
      }
      assert !method.isNative() && !method.isAbstract();
      return (JMethodBody) method.getBody();
    }

    private SourceInfo makeSourceInfo(AbstractMethodDeclaration methodDecl,
        HasSourceInfo enclosing) {
      CompilationResult compResult = methodDecl.compilationResult;
      int[] indexes = compResult.lineSeparatorPositions;
      String fileName = String.valueOf(compResult.fileName);
      int startLine = Util.getLineNumber(methodDecl.sourceStart, indexes, 0,
          indexes.length - 1);
      SourceInfo toReturn = program.createSourceInfo(methodDecl.sourceStart,
          methodDecl.bodyEnd, startLine, fileName);

      // The SourceInfo will inherit Correlations from its enclosing object
      if (enclosing != null) {
        toReturn.copyMissingCorrelationsFrom(enclosing.getSourceInfo());
      }

      return toReturn;
    }

    private SourceInfo makeSourceInfo(Statement stmt, HasSourceInfo enclosing) {
      int startLine = Util.getLineNumber(stmt.sourceStart,
          currentSeparatorPositions, 0, currentSeparatorPositions.length - 1);
      SourceInfo toReturn = program.createSourceInfo(stmt.sourceStart,
          stmt.sourceEnd, startLine, currentFileName);

      // The SourceInfo will inherit Correlations from its enclosing object
      if (enclosing != null) {
        toReturn.copyMissingCorrelationsFrom(enclosing.getSourceInfo());
      }

      return toReturn;
    }

    private void mapParameters(JMethod method, AbstractMethodDeclaration x) {
      MethodBinding b = x.binding;
      int paramCount = (b.parameters != null ? b.parameters.length : 0);
      if (paramCount > 0) {
        for (int i = 0, n = x.arguments.length; i < n; ++i) {
          createParameter(x.arguments[i].binding, method);
        }
      }
      method.freezeParamTypes();
    }

    /**
     * Add synthetic fields, setup super types. You'll notice that we DON'T have
     * any concept of "inner" or "outer" types in our AST. Truth is, we found it
     * easier to simply model everything as flat classes and emulate the nesting
     * behavior (and final local access on local classes). It's much closer to
     * how we'll eventually be generating JavaScript code (code generation is
     * more straightforward), it makes for fewer kinds of things to worry about
     * when optimizing (more aggressive optimizations), and it's how Java
     * actually implements the stuff under the hood anyway.
     */
    private boolean process(TypeDeclaration typeDeclaration) {
      CompilationResult compResult = typeDeclaration.compilationResult;
      currentSeparatorPositions = compResult.lineSeparatorPositions;
      currentFileName = String.valueOf(compResult.fileName);
      SourceTypeBinding binding = typeDeclaration.binding;

      if (binding.constantPoolName() == null) {
        /*
         * Weird case: if JDT determines that this local class is totally
         * uninstantiable, it won't bother allocating a local name.
         */
        return false;
      }
      JDeclaredType type = (JDeclaredType) typeMap.get(binding);
      try {
        // Create an override for getClass().
        if (type instanceof JClassType
            && type != program.getTypeJavaLangObject()
            && type != program.getIndexedType("Array")) {
          JMethod getClassMethod = program.createMethod(
              type.getSourceInfo().makeChild(BuildDeclMapVisitor.class,
                  "Synthetic getClass()"), "getClass".toCharArray(), type,
              program.getTypeJavaLangClass(), false, false, false, false, false);
          assert (type.getMethods().get(2) == getClassMethod);
          getClassMethod.freezeParamTypes();
        }

        if (binding.isNestedType() && !binding.isStatic()) {
          // add synthetic fields for outer this and locals
          assert (type instanceof JClassType);
          NestedTypeBinding nestedBinding = (NestedTypeBinding) binding;
          if (nestedBinding.enclosingInstances != null) {
            for (int i = 0; i < nestedBinding.enclosingInstances.length; ++i) {
              SyntheticArgumentBinding arg = nestedBinding.enclosingInstances[i];
              if (arg.matchingField != null) {
                createField(arg, type);
              }
            }
          }

          if (nestedBinding.outerLocalVariables != null) {
            for (int i = 0; i < nestedBinding.outerLocalVariables.length; ++i) {
              SyntheticArgumentBinding arg = nestedBinding.outerLocalVariables[i];
              createField(arg, type);
            }
          }
        }

        ReferenceBinding superClassBinding = binding.superclass();
        if (superClassBinding != null) {
          // TODO: handle separately?
          assert (binding.superclass().isClass() || binding.superclass().isEnum());
          JClassType superClass = (JClassType) typeMap.get(superClassBinding);
          type.setSuperClass(superClass);
        }

        ReferenceBinding[] superInterfaces = binding.superInterfaces();
        for (int i = 0; i < superInterfaces.length; ++i) {
          ReferenceBinding superInterfaceBinding = superInterfaces[i];
          assert (superInterfaceBinding.isInterface());
          JInterfaceType superInterface = (JInterfaceType) typeMap.get(superInterfaceBinding);
          type.addImplements(superInterface);
        }

        if (type instanceof JEnumType) {
          processEnumType(binding, (JEnumType) type);
        }

        typeDecls.add(typeDeclaration);
        return true;
      } catch (OutOfMemoryError e) {
        // Always rethrow OOMs (might have no memory to load ICE class anyway).
        throw e;
      } catch (InternalCompilerException ice) {
        ice.addNode(type);
        throw ice;
      } catch (Throwable e) {
        throw new InternalCompilerException(type, "Error building type map", e);
      }
    }

    private void processEnumType(SourceTypeBinding binding, JEnumType type) {
      // Visit the synthetic values() and valueOf() methods.
      for (MethodBinding methodBinding : binding.methods()) {
        if (methodBinding instanceof SyntheticMethodBinding) {
          JMethod newMethod = processMethodBinding(methodBinding, type,
              type.getSourceInfo());
          TypeBinding[] parameters = methodBinding.parameters;
          if (parameters.length == 0) {
            assert newMethod.getName().equals("values");
          } else if (parameters.length == 1) {
            assert newMethod.getName().equals("valueOf");
            assert typeMap.get(parameters[0]) == program.getTypeJavaLangString();
            program.createParameter(newMethod.getSourceInfo().makeChild(
                BuildDeclMapVisitor.class, "name parameter"),
                "name".toCharArray(), program.getTypeJavaLangString(), true,
                false, newMethod);
          } else {
            assert false;
          }
          newMethod.freezeParamTypes();
        }
      }
    }

    private JMethod processMethodBinding(MethodBinding b,
        JDeclaredType enclosingType, SourceInfo info) {
      JType returnType = (JType) typeMap.get(b.returnType);
      JMethod newMethod = program.createMethod(info, b.selector, enclosingType,
          returnType, b.isAbstract(), b.isStatic(), b.isFinal(), b.isPrivate(),
          b.isNative());

      typeMap.put(b, newMethod);
      return newMethod;
    }

    private void processNativeMethod(MethodDeclaration methodDeclaration,
        SourceInfo info, JDeclaredType enclosingType, JMethod newMethod) {
      // TODO: use existing parsed JSNI functions from CompilationState.
      // Handle JSNI block
      char[] source = methodDeclaration.compilationResult().getCompilationUnit().getContents();
      String unitSource = String.valueOf(source);
      JsFunction jsFunction = JsniCollector.parseJsniFunction(
          methodDeclaration, unitSource, enclosingType.getName(),
          info.getFileName(), jsProgram);
      if (jsFunction != null) {
        jsFunction.setFromJava(true);
        ((JsniMethodBody) newMethod.getBody()).setFunc(jsFunction);
        // Ensure that we've resolved the parameter and local references within
        // the JSNI method for later pruning.
        JsParameterResolver localResolver = new JsParameterResolver(jsFunction);
        localResolver.accept(jsFunction);
      }
    }

    private InternalCompilerException translateException(
        AbstractMethodDeclaration amd, Throwable e) {
      if (e instanceof OutOfMemoryError) {
        // Always rethrow OOMs (might have no memory to load ICE class anyway).
        throw (OutOfMemoryError) e;
      }
      InternalCompilerException ice;
      if (e instanceof InternalCompilerException) {
        ice = (InternalCompilerException) e;
      } else {
        ice = new InternalCompilerException("Error building type map", e);
      }
      ice.addNode(amd.getClass().getName(), amd.toString(), makeSourceInfo(amd,
          null));
      return ice;
    }

    private InternalCompilerException translateException(Statement stmt,
        Throwable e) {
      if (e instanceof OutOfMemoryError) {
        // Always rethrow OOMs (might have no memory to load ICE class anyway).
        throw (OutOfMemoryError) e;
      }
      InternalCompilerException ice;
      if (e instanceof InternalCompilerException) {
        ice = (InternalCompilerException) e;
      } else {
        ice = new InternalCompilerException("Error building type map", e);
      }
      ice.addNode(stmt.getClass().getName(), stmt.toString(), makeSourceInfo(
          stmt, null));
      return ice;
    }
  }

  /**
   * Creates JNodes for every type and memorizes the mapping from the JDT
   * Binding to the corresponding JNode for each created type. Note that since
   * there could be forward references, it is not possible to set up super
   * types; it must be done is a subsequent pass.
   */
  private static class BuildTypeMapVisitor extends ASTVisitor {

    private final JProgram program;

    private final TypeMap typeMap;

    public BuildTypeMapVisitor(TypeMap typeMap) {
      this.typeMap = typeMap;
      program = this.typeMap.getProgram();
    }

    @Override
    public boolean visit(TypeDeclaration localTypeDeclaration, BlockScope scope) {
      assert (TypeDeclaration.kind(localTypeDeclaration.modifiers) != TypeDeclaration.INTERFACE_DECL);
      return process(localTypeDeclaration);
    }

    @Override
    public boolean visit(TypeDeclaration memberTypeDeclaration, ClassScope scope) {
      return process(memberTypeDeclaration);
    }

    @Override
    public boolean visit(TypeDeclaration typeDeclaration,
        CompilationUnitScope scope) {
      return process(typeDeclaration);
    }

    private SourceInfo makeSourceInfo(TypeDeclaration typeDecl) {
      CompilationResult compResult = typeDecl.compilationResult;
      int[] indexes = compResult.lineSeparatorPositions;
      String fileName = String.valueOf(compResult.fileName);
      int startLine = Util.getLineNumber(typeDecl.sourceStart, indexes, 0,
          indexes.length - 1);
      return program.createSourceInfo(typeDecl.sourceStart, typeDecl.bodyEnd,
          startLine, fileName);
    }

    private boolean process(TypeDeclaration typeDeclaration) {
      try {
        char[][] name = typeDeclaration.binding.compoundName;
        SourceTypeBinding binding = typeDeclaration.binding;
        if (binding instanceof LocalTypeBinding) {
          char[] localName = binding.constantPoolName();
          if (localName == null) {
            /*
             * Weird case: if JDT determines that this local class is totally
             * uninstantiable, it won't bother allocating a local name.
             */
            return false;
          }

          for (int i = 0, c = localName.length; i < c; ++i) {
            if (localName[i] == '/') {
              localName[i] = '.';
            }
          }
          name = new char[1][0];
          name[0] = localName;
        }

        SourceInfo info = makeSourceInfo(typeDeclaration);
        JDeclaredType newType;
        if (binding.isClass()) {
          newType = program.createClass(info, name, binding.isAbstract(),
              binding.isFinal());
        } else if (binding.isInterface() || binding.isAnnotationType()) {
          newType = program.createInterface(info, name);
        } else if (binding.isEnum()) {
          if (binding.isAnonymousType()) {
            // Don't model an enum subclass as a JEnumType.
            newType = program.createClass(info, name, false, true);
          } else {
            newType = program.createEnum(info, name);
          }
        } else {
          assert (false);
          return false;
        }
        info.addCorrelation(program.getCorrelator().by(newType));

        /**
         * We emulate static initializers and instance initializers as methods.
         * As in other cases, this gives us: simpler AST, easier to optimize,
         * more like output JavaScript. Clinit is always in slot 0, init (if it
         * exists) is always in slot 1.
         */
        JMethod clinit = program.createMethod(info.makeChild(
            BuildTypeMapVisitor.class, "Class initializer"),
            "$clinit".toCharArray(), newType, program.getTypeVoid(), false,
            true, true, true, false);
        clinit.freezeParamTypes();

        if (newType instanceof JClassType) {
          JMethod init = program.createMethod(info.makeChild(
              BuildTypeMapVisitor.class, "Instance initializer"),
              "$init".toCharArray(), newType, program.getTypeVoid(), false,
              false, true, true, false);
          init.freezeParamTypes();
        }

        typeMap.put(binding, newType);
        return true;
      } catch (Throwable e) {
        throw translateException(typeDeclaration, e);
      }
    }

    private InternalCompilerException translateException(
        TypeDeclaration typeDecl, Throwable e) {
      if (e instanceof OutOfMemoryError) {
        // Always rethrow OOMs (might have no memory to load ICE class anyway).
        throw (OutOfMemoryError) e;
      }
      InternalCompilerException ice;
      if (e instanceof InternalCompilerException) {
        ice = (InternalCompilerException) e;
      } else {
        ice = new InternalCompilerException("Error building type map", e);
      }
      ice.addNode(typeDecl.getClass().getName(), typeDecl.toString(),
          makeSourceInfo(typeDecl));
      return ice;
    }
  }

  /**
   * Resolves the scope of JS identifiers solely within the scope of a method.
   */
  private static class JsParameterResolver extends JsAbstractSymbolResolver {
    private final JsFunction jsFunction;

    public JsParameterResolver(JsFunction jsFunction) {
      this.jsFunction = jsFunction;
    }

    @Override
    public void resolve(JsNameRef x) {
      // Only resolve unqualified names
      if (x.getQualifier() == null) {
        JsName name = getScope().findExistingName(x.getIdent());

        // Ensure that we're resolving a name from the function's parameters
        if (name != null
            && jsFunction.getParameters().contains(name.getStaticRef())) {
          x.resolve(name);
        }
      }
    }
  }

  public static TypeDeclaration[] exec(TypeMap typeMap,
      CompilationUnitDeclaration[] unitDecls, JsProgram jsProgram) {
    createPeersForTypes(unitDecls, typeMap);
    return createPeersForNonTypeDecls(unitDecls, typeMap, jsProgram);
  }

  private static TypeDeclaration[] createPeersForNonTypeDecls(
      CompilationUnitDeclaration[] unitDecls, TypeMap typeMap,
      JsProgram jsProgram) {
    // Traverse again to create our JNode peers for each method, field,
    // parameter, and local
    BuildDeclMapVisitor v2 = new BuildDeclMapVisitor(typeMap, jsProgram);
    for (int i = 0; i < unitDecls.length; ++i) {
      unitDecls[i].traverse(v2, unitDecls[i].scope);
    }
    return v2.getTypeDeclarataions();
  }

  private static void createPeersForTypes(
      CompilationUnitDeclaration[] unitDecls, TypeMap typeMap) {
    // Traverse once to create our JNode peers for each type
    BuildTypeMapVisitor v1 = new BuildTypeMapVisitor(typeMap);
    for (int i = 0; i < unitDecls.length; ++i) {
      unitDecls[i].traverse(v1, unitDecls[i].scope);
    }
  }
}
TOP

Related Classes of com.google.gwt.dev.jjs.impl.BuildTypeMap$JsParameterResolver

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.