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

com.google.gwt.dev.jjs.impl.ImplementClassLiteralsAsFields Maven / Gradle / Ivy

There is a newer version: 2.10.0
Show newest version
/*
 * 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.
 * 

* 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. *

* 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 classLiteralFields = new IdentityHashMap(); 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: * *

   * Class.createForClass("java.lang.", "Object", /JNameOf/"java.lang.Object", null)
   * Class.createForClass("java.lang.", "Exception", /JNameOf/"java.lang.Exception", Throwable.class)
   * 
* * Interface: * *
   * Class.createForInterface("java.lang.", "Comparable")
   * 
* * Primitive: * *
   * Class.createForPrimitive("", "int", " I")
   * 
* * Array: * *
   * Class.createForArray("", "[I", /JNameOf/"com.google.gwt.lang.Array", int.class)
   * Class.createForArray("[Lcom.example.", "Foo;", /JNameOf/"com.google.gwt.lang.Array", Foo.class)
   * 
* * Enum: * *
   * Class.createForEnum("com.example.", "MyEnum", /JNameOf/"com.example.MyEnum", Enum.class,
   *     public static MyEnum[] values(), public static MyEnum valueOf(String name))
   * 
* * Enum subclass: * *
   * Class.createForEnum("com.example.", "MyEnum$1", /JNameOf/"com.example.MyEnum$1", MyEnum.class,
   *     null, null))
   * 
*/ 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: * *
   * class ClassLiteralHolder {
   *   Class Ljava_lang_Object_2_classLit =
   *       Class.createForClass("java.lang.", "Object", /JNameOf/"java.lang.Object", null)
   * }
   * 
*/ 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; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy