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

Source Code of com.google.gwt.dev.jjs.impl.ImplementClassLiteralsAsFields

/*
* Copyright 2011 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.jjs.Correlation.Literal;
import com.google.gwt.dev.jjs.InternalCompilerException;
import com.google.gwt.dev.jjs.SourceInfo;
import com.google.gwt.dev.jjs.ast.Context;
import com.google.gwt.dev.jjs.ast.JArrayType;
import com.google.gwt.dev.jjs.ast.JClassLiteral;
import com.google.gwt.dev.jjs.ast.JClassType;
import com.google.gwt.dev.jjs.ast.JDeclarationStatement;
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.JFieldRef;
import com.google.gwt.dev.jjs.ast.JInterfaceType;
import com.google.gwt.dev.jjs.ast.JLiteral;
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.JModVisitor;
import com.google.gwt.dev.jjs.ast.JNameOf;
import com.google.gwt.dev.jjs.ast.JNullLiteral;
import com.google.gwt.dev.jjs.ast.JPrimitiveType;
import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.jjs.ast.JStringLiteral;
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.JsniMethodRef;
import com.google.gwt.dev.util.log.speedtracer.CompilerEventType;
import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger;
import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event;

import java.util.IdentityHashMap;
import java.util.Map;

/**
* Create fields to represent the mechanical implementation of class literals.
* Must be done after all class literals are created, but before optimizations
* begin. {@link ControlFlowAnalyzer} depends on this.
* <p>
* Class literals are implemented as static field references. The static fields
* are all put into the special com.google.gwt.lang.ClassLiteralHolder class.
* Ordinarily, accessing one of these fields would trigger a clinit to run, but
* we've special-cased class literal fields to evaluate as top-level code before
* the application starts running to avoid the clinit.
* <p>
* TODO(cromwellian): consider lazy-initialization to improve startup time.
*/
public class ImplementClassLiteralsAsFields {

  private class NormalizeVisitor extends JModVisitor {
    @Override
    public void endVisit(JClassLiteral x, Context ctx) {
      JField field = resolveClassLiteralField(x);
      x.setField(field);
    }
  }

  public static void exec(JProgram program) {
    Event normalizerEvent = SpeedTracerLogger.start(CompilerEventType.NORMALIZER);
    new ImplementClassLiteralsAsFields(program).execImpl();
    normalizerEvent.end();
  }

  private static String getClassName(String fullName) {
    int pos = fullName.lastIndexOf(".");
    return fullName.substring(pos + 1);
  }

  private static String getPackageName(String fullName) {
    int pos = fullName.lastIndexOf(".");
    return fullName.substring(0, pos + 1);
  }

  private final Map<JType, JField> classLiteralFields = new IdentityHashMap<JType, JField>();
  private final JMethodBody classLiteralHolderClinitBody;
  private final JProgram program;
  private final JClassType typeClassLiteralHolder;

  private ImplementClassLiteralsAsFields(JProgram program) {
    this.program = program;
    this.typeClassLiteralHolder = program.getTypeClassLiteralHolder();
    this.classLiteralHolderClinitBody =
        (JMethodBody) typeClassLiteralHolder.getMethods().get(0).getBody();
  }

  /**
   * Create an expression that will evaluate, at run time, to the class literal.
   * Causes recursive literal create (super type, array element type). Examples:
   *
   * Class:
   *
   * <pre>
   * Class.createForClass("java.lang.", "Object", /JNameOf/"java.lang.Object", null)
   * Class.createForClass("java.lang.", "Exception", /JNameOf/"java.lang.Exception", Throwable.class)
   * </pre>
   *
   * Interface:
   *
   * <pre>
   * Class.createForInterface(&quot;java.lang.&quot;, &quot;Comparable&quot;)
   * </pre>
   *
   * Primitive:
   *
   * <pre>
   * Class.createForPrimitive(&quot;&quot;, &quot;int&quot;, &quot; I&quot;)
   * </pre>
   *
   * Array:
   *
   * <pre>
   * Class.createForArray("", "[I", /JNameOf/"com.google.gwt.lang.Array", int.class)
   * Class.createForArray("[Lcom.example.", "Foo;", /JNameOf/"com.google.gwt.lang.Array", Foo.class)
   * </pre>
   *
   * Enum:
   *
   * <pre>
   * Class.createForEnum("com.example.", "MyEnum", /JNameOf/"com.example.MyEnum", Enum.class,
   *     public static MyEnum[] values(), public static MyEnum valueOf(String name))
   * </pre>
   *
   * Enum subclass:
   *
   * <pre>
   * Class.createForEnum("com.example.", "MyEnum$1", /JNameOf/"com.example.MyEnum$1", MyEnum.class,
   *     null, null))
   * </pre>
   */
  private JMethodCall computeClassObjectAllocation(SourceInfo info, JType type) {
    String typeName = getTypeName(type);

    JMethod method = program.getIndexedMethod(type.getClassLiteralFactoryMethod());

    /*
     * Use the classForEnum() constructor even for enum subtypes to aid in
     * pruning supertype data.
     */
    boolean isEnumOrSubclass = false;
    if (type instanceof JClassType) {
      JEnumType maybeEnum = ((JClassType) type).isEnumOrSubclass();
      if (maybeEnum != null) {
        isEnumOrSubclass = true;
        method = program.getIndexedMethod(maybeEnum.getClassLiteralFactoryMethod());
      }
    }

    assert method != null;

    JMethodCall call = new JMethodCall(info, null, method);
    JStringLiteral packageName = program.getLiteralString(info, getPackageName(typeName));
    JStringLiteral className = program.getLiteralString(info, getClassName(typeName));
    call.addArgs(packageName, className);

    if (type instanceof JArrayType) {
      // There's only one seed function for all arrays
      JDeclaredType arrayType = program.getIndexedType("Array");
      call.addArg(new JNameOf(info, className.getType(), arrayType));

    } else if (type instanceof JClassType) {
      // Add the name of the seed function for concrete types
      call.addArg(new JNameOf(info, className.getType(), type));

    } else if (type instanceof JPrimitiveType) {
      // And give primitive types an illegal, though meaningful, value
      call.addArg(program.getLiteralString(info, " " + type.getJavahSignatureName()));
    }

    if (type instanceof JClassType) {
      /*
       * For non-array classes and enums, determine the class literal of the
       * supertype, if there is one. Arrays are excluded because they always
       * have Object as their superclass.
       */
      JClassType classType = (JClassType) type;

      JLiteral superclassLiteral;
      if (classType.getSuperClass() != null) {
        superclassLiteral = createDependentClassLiteral(info, classType.getSuperClass());
      } else {
        superclassLiteral = JNullLiteral.INSTANCE;
      }

      call.addArg(superclassLiteral);

      if (classType instanceof JEnumType) {
        JEnumType enumType = (JEnumType) classType;
        JMethod valuesMethod = null;
        JMethod valueOfMethod = null;
        for (JMethod methodIt : enumType.getMethods()) {
          if ("values".equals(methodIt.getName())) {
            if (methodIt.getParams().size() != 0) {
              continue;
            }
            valuesMethod = methodIt;
          } else if ("valueOf".equals(methodIt.getName())) {
            if (methodIt.getParams().size() != 1
                || methodIt.getParams().get(0).getType() != program.getTypeJavaLangString()) {
              continue;
            }
            valueOfMethod = methodIt;
          }
        }
        if (valuesMethod == null) {
          throw new InternalCompilerException("Could not find enum values() method");
        }
        if (valueOfMethod == null) {
          throw new InternalCompilerException("Could not find enum valueOf() method");
        }
        call.addArg(new JsniMethodRef(info, null, valuesMethod, program.getJavaScriptObject()));
        call.addArg(new JsniMethodRef(info, null, valueOfMethod, program.getJavaScriptObject()));
      } else if (isEnumOrSubclass) {
        // A subclass of an enum class
        call.addArg(JNullLiteral.INSTANCE);
        call.addArg(JNullLiteral.INSTANCE);
      }
    } else if (type instanceof JArrayType) {
      JArrayType arrayType = (JArrayType) type;
      JClassLiteral componentLiteral =
          createDependentClassLiteral(info, arrayType.getElementType());
      call.addArg(componentLiteral);
    } else {
      assert (type instanceof JInterfaceType || type instanceof JPrimitiveType);
    }
    assert call.getArgs().size() == method.getParams().size() : "Argument / param mismatch "
        + call.toString() + " versus " + method.toString();
    return call;
  }

  private JClassLiteral createDependentClassLiteral(SourceInfo info, JType type) {
    JClassLiteral classLiteral = new JClassLiteral(info.makeChild(), type);
    JField field = resolveClassLiteralField(classLiteral);
    classLiteral.setField(field);
    return classLiteral;
  }

  private void execImpl() {
    NormalizeVisitor visitor = new NormalizeVisitor();
    visitor.accept(program);
  }

  private String getTypeName(JType type) {
    String typeName;
    if (type instanceof JArrayType) {
      typeName = type.getJsniSignatureName().replace('/', '.');
      // Mangle the class name to match hosted mode.
      if (program.isJavaScriptObject(((JArrayType) type).getLeafType())) {
        typeName = typeName.replace(";", "$;");
      }
    } else {
      typeName = type.getName();
      // Mangle the class name to match hosted mode.
      if (program.isJavaScriptObject(type)) {
        typeName += '$';
      }
    }
    return typeName;
  }


  private JType normalizeJsoType(JType type) {
    if (program.isJavaScriptObject(type)) {
      return program.getJavaScriptObject();
    }

    if (type instanceof JArrayType) {
      JArrayType aType = (JArrayType) type;
      if (program.isJavaScriptObject(aType.getLeafType())) {
        return program.getTypeArray(program.getJavaScriptObject(),
            aType.getDims());
      }
    }

    return type;
  }

  /**
   * Takes the form:
   *
   * <pre>
   * class ClassLiteralHolder {
   *   Class Ljava_lang_Object_2_classLit =
   *       Class.createForClass("java.lang.", "Object", /JNameOf/"java.lang.Object", null)
   * }
   * </pre>
   */
  private JField resolveClassLiteralField(JClassLiteral classLiteral) {
    JType type = classLiteral.getRefType();
    type = normalizeJsoType(type);
    JField field = classLiteralFields.get(type);
    if (field == null) {
      // Create the allocation expression FIRST since this may be recursive on
      // super type (this forces the super type classLit to be created first).
      SourceInfo info = typeClassLiteralHolder.getSourceInfo().makeChild();
      JMethodCall alloc = computeClassObjectAllocation(info, type);
      // Create a field in the class literal holder to hold the object.
      field =
          new JField(info, program.getClassLiteralName(type), typeClassLiteralHolder, program
              .getTypeJavaLangClass(), true, Disposition.FINAL);
      typeClassLiteralHolder.addField(field);
      info.addCorrelation(info.getCorrelator().by(Literal.CLASS));

      // Initialize the field.
      JFieldRef fieldRef = new JFieldRef(info, null, field, typeClassLiteralHolder);
      JDeclarationStatement decl = new JDeclarationStatement(info, fieldRef, alloc);
      classLiteralHolderClinitBody.getBlock().addStmt(decl);
      classLiteralFields.put(type, field);
    }
    return field;
  }
}
TOP

Related Classes of com.google.gwt.dev.jjs.impl.ImplementClassLiteralsAsFields

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.