blob: 0ed73434394e38cc4eb25932c4c7cb055888abd4 [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.SourceInfo;
import com.google.gwt.dev.util.Preconditions;
import com.google.gwt.dev.util.collect.Lists;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
/**
* Base class for any reference type.
*/
public abstract class JDeclaredType extends JReferenceType {
/**
* The other nodes that this node should implicitly rescue. Special
* serialization treatment.
*/
protected transient List<JNode> artificialRescues = Lists.create();
/**
* This type's fields. Special serialization treatment.
*/
protected transient List<JField> fields = Lists.create();
/**
* This type's methods. Special serialization treatment.
*/
protected transient List<JMethod> methods = Lists.create();
/**
* Tracks the target static initialization for this class. Default to self
* until removed or set to be a superclass.
*/
private JDeclaredType clinitTarget = this;
/**
* The type which originally enclosed this type. Null if this class was a
* top-level type. Note that all classes are converted to top-level types in
* {@link com.google.gwt.dev.jjs.impl.GenerateJavaAST}; this information is
* for tracking purposes.
*/
private JDeclaredType enclosingType;
/**
* True if this class is provided externally to the program by the program's
* host execution environment. For example, while compiling for the JVM, JRE
* types are external types. External types definitions are provided by class
* files which are considered opaque by the GWT compiler.
*/
private boolean isExternal;
/**
* This type's implemented interfaces.
*/
private List<JInterfaceType> superInterfaces = Lists.create();
public JDeclaredType(SourceInfo info, String name) {
super(info, name);
}
public void addArtificialRescue(JNode node) {
artificialRescues = Lists.add(artificialRescues, node);
}
/**
* TODO: remove me, just for source compatibility.
*/
@Deprecated
public void addField(int index, JField field) {
assert field.getEnclosingType() == this;
fields = Lists.add(fields, index, field);
}
/**
* Adds a field to this type.
*/
public void addField(JField field) {
assert field.getEnclosingType() == this;
fields = Lists.add(fields, field);
}
/**
* Adds an implemented interface to this type.
*/
public void addImplements(JInterfaceType superInterface) {
superInterfaces = Lists.add(superInterfaces, superInterface);
}
/**
* Adds a method to this type.
*/
public void addMethod(JMethod method) {
assert method.getEnclosingType() == this;
methods = Lists.add(methods, method);
}
/**
* Returns <code>true</code> if a static field access of
* <code>targetType</code> from within this type should generate a clinit
* call. This will be true in cases where <code>targetType</code> has a live
* clinit method which we cannot statically know has already run. We can
* statically know the clinit method has already run when:
* <ol>
* <li><code>this == targetType</code></li>
* <li><code>this</code> is a subclass of <code>targetType</code>, because my
* clinit would have already run this <code>targetType</code>'s clinit; see
* JLS 12.4</li>
* </ol>
*/
public boolean checkClinitTo(JDeclaredType targetType) {
if (this == targetType) {
// Call to self (very common case).
return false;
}
if (targetType == null || !targetType.hasClinit()) {
// Target has no clinit (common case).
return false;
}
/*
* The clinit for the source of the reference must already have run, so if
* it's the same as this one, there it must have already run. One example is
* a reference from a subclass to something in a superclass.
*/
return this.getClinitTarget() != targetType.getClinitTarget();
}
public List<JNode> getArtificialRescues() {
return artificialRescues;
}
/**
* Returns the class that must be initialized to use this class. May be a
* superclass, or <code>null</code> if this class has no static initializer.
*/
public JDeclaredType getClinitTarget() {
return clinitTarget;
}
/**
* Returns the type which encloses this type.
*
* @return The enclosing type. May be {@code null}.
*/
public JDeclaredType getEnclosingType() {
return enclosingType;
}
/**
* Returns this type's fields;does not include fields defined in a super type
* unless they are overridden by this type.
*/
public List<JField> getFields() {
return fields;
}
/**
* Returns this type's implemented interfaces. Returns an empty list if this
* type implements no interfaces.
*/
public List<JInterfaceType> getImplements() {
return superInterfaces;
}
@Override
public String getJavahSignatureName() {
return "L" + name.replaceAll("_", "_1").replace('.', '_') + "_2";
}
@Override
public String getJsniSignatureName() {
return "L" + name.replace('.', '/') + ';';
}
/**
* Returns this type's declared methods; does not include methods defined in a
* super type unless they are overridden by this type.
*/
public List<JMethod> getMethods() {
return methods;
}
@Override
public String getShortName() {
int dotpos = name.lastIndexOf('.');
return name.substring(dotpos + 1);
}
/**
* Returns this type's super class, or <code>null</code> if this type is
* {@link Object} or an interface.
*/
public abstract JClassType getSuperClass();
/**
* Returns <code>true</code> when this class's clinit must be run dynamically.
*/
public boolean hasClinit() {
return clinitTarget != null;
}
@Override
public boolean isExternal() {
return isExternal;
}
/**
* Removes the field at the specified index.
*/
public void removeField(int i) {
assert !isExternal() : "External types can not be modiified.";
fields = Lists.remove(fields, i);
}
/**
* Removes the method at the specified index.
*/
public void removeMethod(int i) {
assert !isExternal() : "External types can not be modiified.";
methods = Lists.remove(methods, i);
}
/**
* Resolves external references during AST stitching.
*/
public void resolve(List<JInterfaceType> resolvedInterfaces, List<JNode> resolvedRescues) {
assert JType.replaces(resolvedInterfaces, superInterfaces);
superInterfaces = Lists.normalize(resolvedInterfaces);
assert JNameOf.replacesNamedElements(resolvedRescues, artificialRescues);
artificialRescues = Lists.normalize(resolvedRescues);
}
/**
* Sets the type which encloses this types.
*
* @param enclosingType May be {@code null}.
*/
public void setEnclosingType(JDeclaredType enclosingType) {
this.enclosingType = enclosingType;
}
public void setExternal(boolean isExternal) {
this.isExternal = isExternal;
}
/**
* Sorts this type's fields according to the specified sort.
*/
public void sortFields(Comparator<? super JField> sort) {
fields = Lists.sort(fields, sort);
}
/**
* Sorts this type's methods according to the specified sort.
*/
public void sortMethods(Comparator<? super JMethod> sort) {
// Sort the methods manually to avoid sorting clinit out of place!
JMethod a[] = methods.toArray(new JMethod[methods.size()]);
Arrays.sort(a, 1, a.length, sort);
methods = Lists.create(a);
}
/**
* Subclasses must replace themselves with a shallow reference when
* {@link #isExternal()} is <code>true</code>.
*/
protected abstract Object writeReplace();
/**
* Clears all existing implemented interfaces.
*/
void clearImplements() {
superInterfaces = Lists.create();
}
/**
* See {@link #writeMembers(ObjectOutputStream)}.
*
* @see #writeMembers(ObjectOutputStream)
*/
@SuppressWarnings("unchecked")
void readMembers(ObjectInputStream stream) throws IOException, ClassNotFoundException {
fields = (List<JField>) stream.readObject();
methods = (List<JMethod>) stream.readObject();
artificialRescues = (List<JNode>) stream.readObject();
}
/**
* See {@link #writeMethodBodies(ObjectOutputStream)}.
*
* @see #writeMethodBodies(ObjectOutputStream)
*/
void readMethodBodies(ObjectInputStream stream) throws IOException, ClassNotFoundException {
for (JMethod method : methods) {
method.readBody(stream);
}
}
/**
* Called to set this class's trivial initializer to point to a superclass.
*/
void setClinitTarget(JDeclaredType newClinitTarget) {
if (clinitTarget == newClinitTarget) {
return;
}
if (newClinitTarget != null && getClass().desiredAssertionStatus()) {
// Make sure this is a pure upgrade to a superclass or null.
for (JClassType current = (JClassType) clinitTarget; current != newClinitTarget; current =
current.getSuperClass()) {
Preconditions.checkNotNull(current.getSuperClass(),
"Null super class for: %s (currentTarget: %s; newTarget: %s) in %s", current,
clinitTarget, newClinitTarget, this);
}
}
clinitTarget = newClinitTarget;
}
/**
* After all types are written to the stream without transient members, this
* method actually writes fields and methods to the stream, which establishes
* type identity for them.
*
* @see JProgram#writeObject(ObjectOutputStream)
*/
void writeMembers(ObjectOutputStream stream) throws IOException {
stream.writeObject(fields);
stream.writeObject(methods);
stream.writeObject(artificialRescues);
}
/**
* After all types, fields, and methods are written to the stream, this method
* writes method bodies to the stream.
*
* @see JProgram#writeObject(ObjectOutputStream)
*/
void writeMethodBodies(ObjectOutputStream stream) throws IOException {
for (JMethod method : methods) {
method.writeBody(stream);
}
}
}