blob: a3ff6146e56b2218cd09300940350613929af9f2 [file] [log] [blame]
/*
* 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.ast;
import com.google.gwt.dev.jjs.InternalCompilerException;
import com.google.gwt.dev.jjs.SourceInfo;
import com.google.gwt.dev.jjs.ast.js.JsniMethodRef;
/**
* Java class literal expression.
*
* NOTE: This class is modeled as if it were a JFieldRef to a field declared in
* ClassLiteralHolder. That field contains the class object allocation
* initializer.
*/
public class JClassLiteral extends JLiteral implements JAnnotationArgument {
/**
* Create an expression that will evaluate, at run time, to the class literal.
* Cannot be called after optimizations begin.
*/
static JMethodCall computeClassObjectAllocation(JProgram program,
SourceInfo info, JType type) {
String typeName = getTypeName(program, 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 = program.getLiteralClass(classType.getSuperClass());
} else {
superclassLiteral = program.getLiteralNull();
}
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;
}
if ("valueOf".equals(methodIt.getName())) {
if (methodIt.getParams().size() != 1) {
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(program.getLiteralNull());
call.addArg(program.getLiteralNull());
}
} else if (type instanceof JArrayType) {
JArrayType arrayType = (JArrayType) type;
JClassLiteral componentLiteral = program.getLiteralClass(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 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 static String getTypeName(JProgram program, 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 final JField field;
private final JType refType;
/**
* This constructor is only used by {@link JProgram}.
*/
JClassLiteral(SourceInfo sourceInfo, JType type, JField field) {
super(sourceInfo);
refType = type;
this.field = field;
}
public JNode annotationNode() {
return this;
}
/**
* Returns the field holding my allocated object.
*/
public JField getField() {
return field;
}
public JType getRefType() {
return refType;
}
public JType getType() {
return field.getType();
}
public void traverse(JVisitor visitor, Context ctx) {
if (visitor.visit(this, ctx)) {
}
visitor.endVisit(this, ctx);
}
}