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

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

/*
 * 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.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.JExpression;
import com.google.gwt.dev.jjs.ast.JField;
import com.google.gwt.dev.jjs.ast.JField.Disposition;
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.JNullLiteral;
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.JRuntimeTypeReference;
import com.google.gwt.dev.jjs.ast.JType;
import com.google.gwt.dev.jjs.ast.js.JsniClassLiteral;
import com.google.gwt.dev.jjs.ast.js.JsniMethodBody;
import com.google.gwt.dev.jjs.ast.js.JsniMethodRef;
import com.google.gwt.dev.js.ast.JsContext;
import com.google.gwt.dev.js.ast.JsInvocation;
import com.google.gwt.dev.js.ast.JsModVisitor;
import com.google.gwt.dev.js.ast.JsNameRef;
import com.google.gwt.dev.js.ast.JsNumberLiteral;
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 com.google.gwt.thirdparty.guava.common.base.Joiner;
import com.google.gwt.thirdparty.guava.common.collect.ImmutableMap;
import com.google.gwt.thirdparty.guava.common.collect.Lists;
import com.google.gwt.thirdparty.guava.common.collect.Maps;
import com.google.gwt.thirdparty.guava.common.collect.Sets;

import java.util.Map;
import java.util.Set;

/**
 * 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. * * Class literal factory methods are responsible for installing references * to themselves on the Object.clazz field of their JS runtime prototype * since getClass() is no longer an overridden method. Prototypes can be * looked up via 'typeId' from the global prototypesByTypeId object, and so each * class literal factory method is passed the typeId of its type. *

*/ public class ImplementClassLiteralsAsFields { private static Map, ClassLiteralFactoryMethod> literalFactoryMethodByTypeClass = new ImmutableMap.Builder() .put(JEnumType.class, ClassLiteralFactoryMethod.CREATE_FOR_ENUM) .put(JClassType.class, ClassLiteralFactoryMethod.CREATE_FOR_CLASS) .put(JInterfaceType.class, ClassLiteralFactoryMethod.CREATE_FOR_INTERFACE) .put(JPrimitiveType.class, ClassLiteralFactoryMethod.CREATE_FOR_PRIMITIVE) .build(); /** * Class used to construct invocations to class literal factory methods. */ public enum ClassLiteralFactoryMethod { CREATE_FOR_ENUM() { @Override JMethodCall createCall(SourceInfo info, JProgram program, JType type, JLiteral superclassLiteral) { JEnumType enumType = type.isEnumOrSubclass(); assert enumType != null; // createForEnum(packageName, typeName, runtimeTypeReference, Enum.class, type.values(), // type.valueOf(java/lang/String)); JMethodCall call = createBaseCall(info, program, type, "Class.createForEnum"); call.addArg(new JRuntimeTypeReference(info, program.getTypeJavaLangObject(), (JReferenceType) type)); call.addArg(superclassLiteral); maybeAddStandardMethodAsArg(info, program, type, "values()", call); maybeAddStandardMethodAsArg(info, program, type, "valueOf(Ljava/lang/String;)", call); return call; } private void maybeAddStandardMethodAsArg(SourceInfo info, JProgram program, JType type, String methodSignature, JMethodCall call) { JEnumType enumType = type.isEnumOrSubclass(); JMethod standardMethod = null; if (enumType == type) { for (JMethod method : enumType.getMethods()) { if (method.isStatic() && method.getSignature().startsWith(methodSignature)) { standardMethod = method; break; } } } assert enumType != type || standardMethod != null : "Could not find enum " + methodSignature + " method"; if (standardMethod == null) { call.addArg(JNullLiteral.INSTANCE); } else { call.addArg(new JsniMethodRef(info, standardMethod.getJsniSignature(true, false), standardMethod, program.getJavaScriptObject())); } } }, CREATE_FOR_CLASS() { @Override JMethodCall createCall(SourceInfo info, JProgram program, JType type, JLiteral superclassLiteral) { // Class.createForClass(packageName, typeName, runtimeTypeReference, superclassliteral) JMethodCall call = createBaseCall(info, program, type, "Class.createForClass"); call.addArg(new JRuntimeTypeReference(info, program.getTypeJavaLangObject(), (JReferenceType) type)); call.addArg(superclassLiteral); return call; } }, CREATE_FOR_PRIMITIVE() { @Override JMethodCall createCall(SourceInfo info, JProgram program, JType type, JLiteral superclassLiteral) { // Class.createForPrimitive(typeName, typeSignature) JMethodCall call = new JMethodCall(info, null, program.getIndexedMethod("Class.createForPrimitive")); call.addArg(program.getStringLiteral(info, type.getShortName())); call.addArg(program.getStringLiteral(info, type.getJavahSignatureName())); return call; } }, CREATE_FOR_INTERFACE() { @Override JMethodCall createCall(SourceInfo info, JProgram program, JType type, JLiteral superclassLiteral) { // Class.createForClass(packageName, typeName, runtimeTypeReference, superclassliteral) JMethodCall call = createBaseCall(info, program, type, "Class.createForInterface"); call.addArg(new JRuntimeTypeReference(info, program.getTypeJavaLangObject(), (JReferenceType) type)); call.addArg(superclassLiteral); return call; } }; abstract JMethodCall createCall(SourceInfo info, JProgram program, JType type, JLiteral superclassLiteral); private static JMethodCall createBaseCall(SourceInfo info, JProgram program, JType type, String indexedMethodName) { String[] compoundName = maybeMangleJSOTypeName(program, type); JMethodCall call = new JMethodCall(info, null, program.getIndexedMethod(indexedMethodName), program.getStringLiteral(info, type.getPackageName()), getCompoundNameLiteral(program, info, compoundName)); return call; } private static String[] maybeMangleJSOTypeName(JProgram program, JType type) { assert !(type instanceof JArrayType); String[] compoundName = type.getCompoundName(); // Mangle the class name to match hosted mode. if (program.typeOracle.isJavaScriptObject(type)) { compoundName[compoundName.length - 1] = compoundName[compoundName.length - 1] + '$'; } return compoundName; } private static JExpression getCompoundNameLiteral(final JProgram program, final SourceInfo info, String[] compoundName) { return program.getStringLiteral(info, Joiner.on('/').join(compoundName)); } } private class NormalizeVisitor extends JModVisitor { @Override public void endVisit(JClassLiteral x, Context ctx) { JType type = x.getRefType(); if (type instanceof JArrayType) { // Replace array class literals by an expression to obtain the class literal from the // leaf type of the array. JArrayType arrayType = (JArrayType) type; JClassLiteral leafTypeClassLiteral = new JClassLiteral(x.getSourceInfo(), arrayType.getLeafType()); resolveClassLiteral(leafTypeClassLiteral); JExpression arrayClassLiteralExpression = program.createArrayClassLiteralExpression( x.getSourceInfo(), leafTypeClassLiteral, arrayType.getDims()); ctx.replaceMe(arrayClassLiteralExpression); } else { // Just resolve the class literal. resolveClassLiteral(x); } } @Override public void endVisit(JsniClassLiteral x, Context ctx) { // JsniClassLiterals will be traversed explicitly in JsniMethodBody. For each JsniClassLiteral // in JsniMethodBody.classRefs there is at least a corresponding JsNameRef (with a jsni // identifier) that needs to be altered accordingly. } @Override public void endVisit(final JsniMethodBody jsniMethodBody, Context ctx) { if (jsniMethodBody.getClassRefs().size() == 0) { return; } final Map jsniClassLiteralsByJsniReference = Maps.newHashMap(); final JMethod getClassLiteralForArrayMethod = program.getIndexedMethod("Array.getClassLiteralForArray"); final String getClassLiteralForArrayMethodIdent = "@" + getClassLiteralForArrayMethod.getJsniSignature(true, false); boolean areThereArrayClassLiterals = false; for (JsniClassLiteral jsniClassLiteral : jsniMethodBody.getClassRefs()) { // Check for the presence of array class literals. if (jsniClassLiteral.getRefType() instanceof JArrayType) { areThereArrayClassLiterals = true; } else { // Resolve non array class literals. resolveClassLiteral(jsniClassLiteral); } // Map JSNI reference string to the actual JsniClassLiterals. Object o = jsniClassLiteralsByJsniReference.put(jsniClassLiteral.getIdent(), jsniClassLiteral); assert o == null; } if (!areThereArrayClassLiterals) { // No array class literal no need to explore the body. return; } final Set newClassRefs = Sets.newLinkedHashSet(); // Replace all arrays JSNI class literals with the its construction via the leaf type class // literal. JsModVisitor replaceJsniClassLiteralVisitor = new JsModVisitor() { @Override public void endVisit(JsNameRef x, JsContext ctx) { if (!x.isJsniReference()) { return; } JsniClassLiteral jsniClassLiteral = jsniClassLiteralsByJsniReference.get( x.getIdent()); if (jsniClassLiteral == null) { return; } if (jsniClassLiteral.getRefType() instanceof JArrayType) { // Replace the array class literal by an expression that retrieves it from // that of the leaf type. JArrayType arrayType = (JArrayType) jsniClassLiteral.getRefType(); JType leafType = arrayType.getLeafType(); jsniClassLiteral = new JsniClassLiteral(jsniClassLiteral.getSourceInfo(), leafType); // Array.getClassLiteralForArray(leafType.class, dimensions) SourceInfo info = x.getSourceInfo(); JsNameRef getArrayClassLiteralMethodNameRef = new JsNameRef(info, getClassLiteralForArrayMethodIdent); JsInvocation invocation = new JsInvocation(info, getArrayClassLiteralMethodNameRef, new JsNameRef(info, jsniClassLiteral.getIdent()), new JsNumberLiteral(info, arrayType.getDims())); // Finally resolve the class literal. resolveClassLiteral(jsniClassLiteral); ctx.replaceMe(invocation); } newClassRefs.add(jsniClassLiteral); } }; replaceJsniClassLiteralVisitor.accept(jsniMethodBody.getFunc()); if (!replaceJsniClassLiteralVisitor.didChange()) { // Nothing was changed, no need to replace JsniMethodBody. return; } JsniMethodBody newBody = new JsniMethodBody(jsniMethodBody.getSourceInfo(), jsniMethodBody.getFunc(), Lists.newArrayList(newClassRefs), jsniMethodBody.getJsniFieldRefs(), jsniMethodBody.getJsniMethodRefs(), jsniMethodBody.getUsedStrings()); // Add getClassLiteralForArray as a JsniMethodRef. newBody.addJsniRef( new JsniMethodRef(jsniMethodBody.getSourceInfo(), getClassLiteralForArrayMethodIdent, getClassLiteralForArrayMethod, program.getJavaScriptObject())); ctx.replaceMe(newBody); } private void resolveClassLiteral(JClassLiteral x) { JField field = resolveClassLiteralField(x.getRefType()); x.setField(field); } } public static void exec(JProgram program) { Event normalizerEvent = SpeedTracerLogger.start(CompilerEventType.NORMALIZER); new ImplementClassLiteralsAsFields(program).execImpl(); normalizerEvent.end(); } private final Map classLiteralFields = Maps.newIdentityHashMap(); 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.getClinitMethod().getBody(); assert program.getDeclaredTypes().contains(typeClassLiteralHolder); } private JLiteral getSuperclassClassLiteral(SourceInfo info, JType type) { if (!(type instanceof JClassType) || ((JClassType) type).getSuperClass() == null) { return JNullLiteral.INSTANCE; } JClassType classType = (JClassType) type; if (program.isJsTypePrototype(classType)) { /* * When a Java type extends a JS prototype stub, we make the superclass literal * equal to the Js interface. */ JDeclaredType jsInterface = program.typeOracle.getNearestJsType(type, true); assert jsInterface != null; return createDependentClassLiteral(info, jsInterface); } return createDependentClassLiteral(info, classType.getSuperClass()); } private JClassLiteral createDependentClassLiteral(SourceInfo info, JType type) { JClassLiteral classLiteral = new JClassLiteral(info.makeChild(), type); classLiteral.setField(resolveClassLiteralField(classLiteral.getRefType())); return classLiteral; } private void execImpl() { NormalizeVisitor visitor = new NormalizeVisitor(); visitor.accept(program); if (!program.typeOracle.hasWholeWorldKnowledge()) { // TODO(rluble): This is kind of hacky. It would be more general purpose to create a class // literal implementation for every class and let unused ones be pruned by optimization. // Instead we're prematurely optimizing. We should be doing this traversal unconditionally. for (JType type : program.getDeclaredTypes()) { resolveClassLiteralField(type); } } program.recordClassLiteralFields(classLiteralFields); } /** * 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", /JRuntimeTypeReference/"java.lang.Object", null)
   * Class.createForClass("java.lang.", "Exception", /JRuntimeTypeReference/"java.lang.Exception",
   *   Throwable.class)
   * 
* * Interface: * *
   * Class.createForInterface("java.lang.", "Comparable")
   * 
* * Arrays are lazily created. * * Primitive: * *
   * Class.createForPrimitive("", "int", "I")
   * 
* * Enum: * *
   * Class.createForEnum("com.example.", "MyEnum", /JRuntimeTypeReference/"com.example.MyEnum",
   *     Enum.class, public static MyEnum[] values(), public static MyEnum valueOf(String name))
   * 
* * Enum subclass: * *
   * Class.createForEnum("com.example.", "MyEnum$1", /JRuntimeTypeReference/"com.example.MyEnum$1",
   *   MyEnum.class, null, null))
   * 
*/ private JMethodCall createLiteralCall(SourceInfo info, JProgram program, JType type) { type = type.getUnderlyingType(); Class typeClass = type.getClass(); if (type.isEnumOrSubclass() != null) { typeClass = JEnumType.class; } return literalFactoryMethodByTypeClass.get(typeClass).createCall(info, program, type, getSuperclassClassLiteral(info, type)); } /** * Resolve a class literal field. Takes the form: * *
   * class ClassLiteralHolder {
   *   Class Ljava_lang_Object_2_classLit =
   *       Class.createForClass("java.lang.", "Object", /JNameOf/"java.lang.Object", null)
   * }
   * 
*/ private JField resolveClassLiteralField(JType type) { type = program.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 = type.getSourceInfo().makeChild(); assert !(type instanceof JArrayType); JMethodCall classLiteralCreationExpression = createLiteralCall(info, program, type); // Create a field in the class literal holder to hold the object. field = new JField(info, 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, classLiteralCreationExpression); classLiteralHolderClinitBody.getBlock().addStmt(decl); classLiteralFields.put(type, field); } return field; } private static String getClassLiteralName(JType type) { return type.getJavahSignatureName() + "_classLit"; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy