blob: fbca4e7aa1280a1408e78325b6acb655afe56058 [file] [log] [blame]
/*
* Copyright 2006 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 java.lang;
import com.google.gwt.core.client.JavaScriptObject;
import java.lang.reflect.Type;
import javaemul.internal.annotations.DoNotInline;
/**
* Generally unsupported. This class is provided so that the GWT compiler can
* choke down class literal references.
* <p>
* NOTE: The code in this class is very sensitive and should keep its dependencies upon other
* classes to a minimum.
*
* @param <T> the type of the object
*/
public final class Class<T> implements Type {
private static final int PRIMITIVE = 0x00000001;
private static final int INTERFACE = 0x00000002;
private static final int ARRAY = 0x00000004;
private static final int ENUM = 0x00000008;
/**
* Create a Class object for an array.<p>
*
* Arrays are not registered in the prototype table and get the class literal explicitly at
* construction.<p>
*
* NOTE: this method is accessed through JSNI (Violator pattern) to avoid changing the public API.
*/
private static native <T> Class<T> getClassLiteralForArray(Class<?> leafClass,
int dimensions) /*-{
var arrayLiterals = leafClass.@Class::arrayLiterals = leafClass.@Class::arrayLiterals || [];
return arrayLiterals[dimensions] || (arrayLiterals[dimensions] =
leafClass.@Class::createClassLiteralForArray(I)(dimensions));
}-*/;
private <T> Class<T> createClassLiteralForArray(int dimensions) {
Class<T> clazz = new Class<T>();
clazz.modifiers = ARRAY;
clazz.superclass = Object.class;
if (dimensions > 1) {
clazz.componentType = getClassLiteralForArray(this, dimensions - 1);
} else {
clazz.componentType = this;
}
return clazz;
}
/**
* Create a Class object for a class.
*
* @skip
*/
@DoNotInline
static <T> Class<T> createForClass(String packageName, String compoundClassName,
JavaScriptObject typeId, Class<? super T> superclass) {
Class<T> clazz = createClassObject(packageName, compoundClassName, typeId);
maybeSetClassLiteral(typeId, clazz);
clazz.superclass = superclass;
return clazz;
}
/**
* Create a Class object for an enum.
*
* @skip
*/
@DoNotInline
static <T> Class<T> createForEnum(String packageName, String compoundClassName,
JavaScriptObject typeId, Class<? super T> superclass,
JavaScriptObject enumConstantsFunc, JavaScriptObject enumValueOfFunc) {
Class<T> clazz = createClassObject(packageName, compoundClassName, typeId);
maybeSetClassLiteral(typeId, clazz);
clazz.modifiers = (enumConstantsFunc != null) ? ENUM : 0;
clazz.superclass = clazz.enumSuperclass = superclass;
clazz.enumConstantsFunc = enumConstantsFunc;
clazz.enumValueOfFunc = enumValueOfFunc;
return clazz;
}
/**
* Create a Class object for an interface.
*
* @skip
*/
@DoNotInline
static <T> Class<T> createForInterface(String packageName, String compoundClassName) {
Class<T> clazz = createClassObject(packageName, compoundClassName, null);
clazz.modifiers = INTERFACE;
return clazz;
}
/**
* Create a Class object for a primitive.
*
* @skip
*/
@DoNotInline
static Class<?> createForPrimitive(String className, JavaScriptObject primitiveTypeId) {
Class<?> clazz = createClassObject("", className, primitiveTypeId);
clazz.typeId = primitiveTypeId;
clazz.modifiers = PRIMITIVE;
return clazz;
}
/**
* Used by {@link WebModePayloadSink} to create uninitialized instances.
*/
@DoNotInline
static native JavaScriptObject getPrototypeForClass(Class<?> clazz) /*-{
if (clazz.@Class::isPrimitive()()) {
return null;
}
var typeId = clazz.@Class::typeId;
return @com.google.gwt.lang.Runtime::prototypesByTypeId[typeId];
}-*/;
public static boolean isClassMetadataEnabled() {
// This body may be replaced by the compiler
return true;
}
/**
* null implies non-instantiable type, with no entries in
* {@link Runtime::prototypesByTypeId}.
*/
private static native boolean isInstantiable(JavaScriptObject typeId) /*-{
return !!typeId;
}-*/;
/**
* Creates the class object for a type and initiliazes its fields.
*/
private static <T> Class<T> createClassObject(String packageName, String compoundClassName,
JavaScriptObject typeId) {
Class<T> clazz = new Class<T>();
if (isClassMetadataEnabled()) {
clazz.packageName = packageName;
clazz.compoundName = compoundClassName;
} else {
synthesizeClassNamesFromTypeId(clazz, typeId);
}
return clazz;
}
/**
* Install class literal into prototype.clazz field (if type is instantiable) such that
* Object.getClass() returning this.clazz returns the literal. Also stores typeId on class literal
* for looking up prototypes given a literal. This is used for deRPC at the moment, but may be
* used to implement Class.newInstance() in the future.
*
* If the prototype for typeId has not yet been created, then install the literal into a
* placeholder array to differentiate the two cases.
*/
private static native void maybeSetClassLiteral(JavaScriptObject typeId, Class<?> clazz) /*-{
var proto;
if (!typeId) {
// Type is not instantiable, hence not registered in the metadata table.
return;
}
clazz.@Class::typeId = typeId;
// Guarantees virtual method won't be pruned by using a JSNI ref
// This is required because deRPC needs to call it.
var prototype = @Class::getPrototypeForClass(Ljava/lang/Class;)(clazz);
// A class literal may be referenced prior to an async-loaded vtable setup
// For example, class literal lives in inital fragment,
// but type is instantiated in another fragment
if (!prototype) {
// Leave a place holder for now to be filled in by __defineClass__ later.
// TODO(rluble): Do not rely on the fact that if the entry is an array it is a placeholder.
@com.google.gwt.lang.Runtime::prototypesByTypeId[typeId] = [clazz];
return;
}
// Type already registered in the metadata table, install the class literal in the appropriate
// prototype field.
prototype.@java.lang.Object::___clazz = clazz;
}-*/;
/**
* Initiliazes {@code clazz} names from metadata.
* <p>
* Written in JSNI to minimize dependencies (on String.+).
*/
private static native void initializeNames(Class<?> clazz) /*-{
if (clazz.@Class::isArray(*)()) {
var componentType = clazz.@Class::componentType;
if (componentType.@Class::isPrimitive()()) {
clazz.@Class::typeName = "[" + componentType.@Class::typeId;
} else if (!componentType.@Class::isArray()()) {
clazz.@Class::typeName = "[L" + componentType.@Class::getName()() + ";";
} else {
clazz.@Class::typeName = "[" + componentType.@Class::getName()();
}
clazz.@Class::canonicalName = componentType.@Class::getCanonicalName()() + "[]";
clazz.@Class::simpleName = componentType.@Class::getSimpleName()() + "[]";
return;
}
var packageName = clazz.@Class::packageName;
var compoundName = clazz.@Class::compoundName;
compoundName = compoundName.split("/");
clazz.@Class::typeName =
@Class::join(*)('.', [packageName , @Class::join(*)("$", compoundName)]);
clazz.@Class::canonicalName =
@Class::join(*)('.', [packageName , @Class::join(*)(".", compoundName)]);
clazz.@Class::simpleName = compoundName[compoundName.length - 1];
}-*/;
/**
* Joins an array of strings with the specified separator.
* <p>
* Written in JSNI to minimize dependencies (on String.+).
*/
private static native String join(String separator, JavaScriptObject strings) /*-{
var i = 0;
while (!strings[i] || strings[i] == "") {
i++;
}
var result = strings[i++];
for (; i < strings.length; i++) {
if (!strings[i] || strings[i] == "") {
continue;
}
result += separator + strings[i];
}
return result;
}-*/;
/**
* Initiliazes {@code clazz} names from typeIds.
* <p>
* Only called if metadata IS disabled.
* <p>
* Written in JSNI to minimize dependencies (on toString() and String.+).
*/
static native void synthesizeClassNamesFromTypeId(Class<?> clazz, JavaScriptObject typeId) /*-{
// The initial "" + in the below code is to prevent clazz.getAnonymousId from
// being autoboxed. The class literal creation code is run very early
// during application start up, before class Integer has been initialized.
clazz.@Class::typeName = "Class$" +
(!!typeId ? "S" + typeId : "" + clazz.@Class::sequentialId);
clazz.@Class::canonicalName = clazz.@Class::typeName;
clazz.@Class::simpleName = clazz.@Class::typeName;
}-*/;
/**
* Sets the class object for primitives.
* <p>
* Written in JSNI to minimize dependencies (on (String)+).
*/
static native void synthesizePrimitiveNamesFromTypeId(Class<?> clazz,
JavaScriptObject primitiveTypeId) /*-{
clazz.@Class::typeName = "Class$" + primitiveTypeId;
clazz.@Class::canonicalName = clazz.@Class::typeName;
clazz.@Class::simpleName = clazz.@Class::typeName;
}-*/;
JavaScriptObject enumValueOfFunc;
int modifiers;
private Class<?> componentType;
@SuppressWarnings("unused")
private JavaScriptObject enumConstantsFunc;
private Class<? super T> enumSuperclass;
private Class<? super T> superclass;
private String simpleName;
private String typeName;
private String canonicalName;
private String packageName;
private String compoundName;
private JavaScriptObject typeId;
private JavaScriptObject arrayLiterals;
private JavaScriptObject jsConstructor;
// Assign a sequential id to each class literal to avoid calling hashCode which bring Impl as
// a dependency.
private int sequentialId = nextSequentialId++;
private static int nextSequentialId = 1;
/**
* Not publicly instantiable.
*
* @skip
*/
private Class() {
// Initialize in constructor to avoid V8 invalidating hidden classes.
typeName = null;
simpleName = null;
packageName = null;
compoundName = null;
canonicalName = null;
typeId = null;
arrayLiterals = null;
}
public boolean desiredAssertionStatus() {
// This body is ignored by the JJS compiler and a new one is
// synthesized at compile-time based on the actual compilation arguments.
return false;
}
private void ensureNamesAreInitialized() {
if (typeName != null) {
return;
}
initializeNames(this);
}
public String getCanonicalName() {
ensureNamesAreInitialized();
return canonicalName;
}
public Class<?> getComponentType() {
return componentType;
}
public native T[] getEnumConstants() /*-{
return this.@Class::enumConstantsFunc
&& (this.@Class::enumConstantsFunc)();
}-*/;
public String getName() {
ensureNamesAreInitialized();
return typeName;
}
public String getSimpleName() {
ensureNamesAreInitialized();
return simpleName;
}
public Class<? super T> getSuperclass() {
if (isClassMetadataEnabled()) {
return superclass;
} else {
return null;
}
}
public boolean isArray() {
return (modifiers & ARRAY) != 0;
}
public boolean isEnum() {
return (modifiers & ENUM) != 0;
}
public boolean isInterface() {
return (modifiers & INTERFACE) != 0;
}
public boolean isPrimitive() {
return (modifiers & PRIMITIVE) != 0;
}
@Override
public String toString() {
return (isInterface() ? "interface " : (isPrimitive() ? "" : "class "))
+ getName();
}
/**
* Used by Enum to allow getSuperclass() to be pruned.
*/
Class<? super T> getEnumSuperclass() {
return enumSuperclass;
}
}