blob: 401ca91308ac2c4a91e8738fb68878a06a409703 [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.SourceOrigin;
import com.google.gwt.dev.util.StringInterner;
import com.google.gwt.dev.util.collect.Lists;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
/**
* A Java method implementation.
*/
public class JMethod extends JNode implements HasEnclosingType, HasName, HasType, CanBeAbstract,
CanBeSetFinal, CanBeNative, CanBeStatic {
private static class ExternalSerializedForm implements Serializable {
private final JDeclaredType enclosingType;
private final String signature;
public ExternalSerializedForm(JMethod method) {
enclosingType = method.getEnclosingType();
signature = method.getSignature();
}
private Object readResolve() {
return new JMethod(signature, enclosingType);
}
}
private static class ExternalSerializedNullMethod implements Serializable {
public static final ExternalSerializedNullMethod INSTANCE = new ExternalSerializedNullMethod();
private Object readResolve() {
return NULL_METHOD;
}
}
public static final JMethod NULL_METHOD = new JMethod(SourceOrigin.UNKNOWN, "nullMethod", null,
JNullType.INSTANCE, false, false, true, false);
private static final String TRACE_METHOD_WILDCARD = "*";
static {
NULL_METHOD.setSynthetic();
NULL_METHOD.freezeParamTypes();
}
static boolean replaces(List<? extends JMethod> newMethods, List<? extends JMethod> oldMethods) {
if (newMethods.size() != oldMethods.size()) {
return false;
}
for (int i = 0, c = newMethods.size(); i < c; ++i) {
if (!newMethods.get(i).replaces(oldMethods.get(i))) {
return false;
}
}
return true;
}
private static void trace(String title, String code) {
System.out.println("---------------------------");
System.out.println(title + ":");
System.out.println("---------------------------");
System.out.println(code);
}
protected transient String signature;
/**
* Special serialization treatment.
*/
private transient JAbstractMethodBody body = null;
private final JDeclaredType enclosingType;
private boolean isAbstract;
private boolean isFinal;
private final boolean isPrivate;
private final boolean isStatic;
private boolean isSynthetic = false;
private final String name;
private List<JType> originalParamTypes;
private JType originalReturnType;
/**
* References to any methods which this method overrides. This should be an
* EXHAUSTIVE list, that is, if C overrides B overrides A, then C's overrides
* list will contain both A and B.
*/
private List<JMethod> overrides = Collections.emptyList();
private List<JParameter> params = Collections.emptyList();
private JType returnType;
private List<JClassType> thrownExceptions = Collections.emptyList();
private boolean trace = false;
private boolean traceFirst = true;
/**
* These are only supposed to be constructed by JProgram.
*/
public JMethod(SourceInfo info, String name, JDeclaredType enclosingType, JType returnType,
boolean isAbstract, boolean isStatic, boolean isFinal, boolean isPrivate) {
super(info);
this.name = StringInterner.get().intern(name);
this.enclosingType = enclosingType;
this.returnType = returnType;
this.isAbstract = isAbstract;
this.isStatic = isStatic;
this.isFinal = isFinal;
this.isPrivate = isPrivate;
}
/**
* Construct a bare-bones deserialized external method.
*/
private JMethod(String signature, JDeclaredType enclosingType) {
super(SourceOrigin.UNKNOWN);
this.name = signature.substring(0, signature.indexOf('('));
this.enclosingType = enclosingType;
this.signature = signature;
this.isAbstract = false;
this.isStatic = false;
this.isPrivate = false;
}
/**
* Add a method that this method overrides.
*/
public void addOverride(JMethod toAdd) {
assert canBePolymorphic();
overrides = Lists.add(overrides, toAdd);
}
/**
* Add methods that this method overrides.
*/
public void addOverrides(List<JMethod> toAdd) {
assert canBePolymorphic();
overrides = Lists.addAll(overrides, toAdd);
}
/**
* Adds a parameter to this method.
*/
public void addParam(JParameter x) {
params = Lists.add(params, x);
}
public void addThrownException(JClassType exceptionType) {
thrownExceptions = Lists.add(thrownExceptions, exceptionType);
}
public void addThrownExceptions(List<JClassType> exceptionTypes) {
thrownExceptions = Lists.addAll(thrownExceptions, exceptionTypes);
}
/**
* Returns true if this method can participate in virtual dispatch. Returns
* true for non-private instance methods; false for static methods, private
* instance methods, and constructors.
*/
public boolean canBePolymorphic() {
return !isStatic() && !isPrivate();
}
public void freezeParamTypes() {
List<JType> paramTypes = new ArrayList<JType>();
for (JParameter param : params) {
paramTypes.add(param.getType());
}
setOriginalTypes(returnType, paramTypes);
}
public JAbstractMethodBody getBody() {
assert !isExternal() : "External types do not have method bodies.";
return body;
}
public JDeclaredType getEnclosingType() {
return enclosingType;
}
public String getName() {
return name;
}
public List<JType> getOriginalParamTypes() {
return originalParamTypes;
}
public JType getOriginalReturnType() {
return originalReturnType;
}
/**
* Returns the transitive closure of all the methods this method overrides.
*/
public List<JMethod> getOverrides() {
return overrides;
}
/**
* Returns the parameters of this method.
*/
public List<JParameter> getParams() {
return params;
}
public String getSignature() {
if (signature == null) {
StringBuilder sb = new StringBuilder();
sb.append(getName());
sb.append('(');
for (JType type : getOriginalParamTypes()) {
sb.append(type.getJsniSignatureName());
}
sb.append(')');
sb.append(getOriginalReturnType().getJsniSignatureName());
signature = sb.toString();
}
return signature;
}
public List<JClassType> getThrownExceptions() {
return thrownExceptions;
}
public JType getType() {
return returnType;
}
public boolean isAbstract() {
return isAbstract;
}
public boolean isExternal() {
return getEnclosingType() != null && getEnclosingType().isExternal();
}
public boolean isFinal() {
return isFinal;
}
public boolean isNative() {
if (body == null) {
return false;
} else {
return body.isNative();
}
}
public boolean isPrivate() {
return isPrivate;
}
public boolean isStatic() {
return isStatic;
}
public boolean isSynthetic() {
return isSynthetic;
}
public boolean isTrace() {
return trace;
}
/**
* Returns <code>true</code> if this method can participate in instance
* dispatch.
*/
public boolean needsVtable() {
return !isStatic();
}
/**
* Removes the parameter at the specified index.
*/
public void removeParam(int index) {
params = Lists.remove(params, index);
}
/**
* Resolve an external references during AST stitching.
*/
public void resolve(JType originalReturnType, List<JType> originalParamTypes, JType returnType,
List<JClassType> thrownExceptions) {
if (getClass().desiredAssertionStatus()) {
assert originalReturnType.replaces(this.originalReturnType);
assert JType.replaces(originalParamTypes, this.originalParamTypes);
assert returnType.replaces(this.returnType);
assert JType.replaces(thrownExceptions, this.thrownExceptions);
}
this.originalReturnType = originalReturnType;
this.originalParamTypes = Lists.normalize(originalParamTypes);
this.returnType = returnType;
this.thrownExceptions = Lists.normalize(thrownExceptions);
}
public void setAbstract(boolean isAbstract) {
this.isAbstract = isAbstract;
}
public void setBody(JAbstractMethodBody body) {
this.body = body;
if (body != null) {
body.setMethod(this);
}
}
public void setFinal() {
isFinal = true;
}
public void setOriginalTypes(JType returnType, List<JType> paramTypes) {
if (originalParamTypes != null) {
throw new InternalCompilerException("Param types already frozen");
}
originalReturnType = returnType;
originalParamTypes = Lists.normalize(paramTypes);
// Determine if we should trace this method.
if (enclosingType != null) {
String jsniSig = JProgram.getJsniSig(this);
Set<String> set = JProgram.traceMethods.get(enclosingType.getName());
if (set != null
&& (set.contains(name) || set.contains(jsniSig) || set.contains(TRACE_METHOD_WILDCARD))) {
trace = true;
}
// Try the short name.
if (!trace && enclosingType != null) {
set = JProgram.traceMethods.get(enclosingType.getShortName());
if (set != null
&& (set.contains(name) || set.contains(jsniSig) || set.contains(TRACE_METHOD_WILDCARD))) {
trace = true;
}
}
}
}
public void setSynthetic() {
isSynthetic = true;
}
public void setTrace() {
this.trace = true;
}
public void setType(JType newType) {
returnType = newType;
}
public void traverse(JVisitor visitor, Context ctx) {
String before = null;
before = traceBefore(visitor);
if (visitor.visit(this, ctx)) {
visitChildren(visitor);
}
visitor.endVisit(this, ctx);
traceAfter(visitor, before);
}
protected void traceAfter(JVisitor visitor, String before) {
if (trace && visitor instanceof JModVisitor) {
String after = this.toSource();
if (!after.equals(before)) {
String title = visitor.getClass().getSimpleName();
trace(title, after);
}
}
}
protected String traceBefore(JVisitor visitor) {
if (trace && visitor instanceof JModVisitor) {
String source = this.toSource();
if (traceFirst) {
traceFirst = false;
trace("JAVA INITIAL", source);
}
return source;
}
return null;
}
protected void visitChildren(JVisitor visitor) {
params = visitor.acceptImmutable(params);
if (body != null) {
body = (JAbstractMethodBody) visitor.accept(body);
}
}
protected Object writeReplace() {
if (isExternal()) {
return new ExternalSerializedForm(this);
} else if (this == NULL_METHOD) {
return ExternalSerializedNullMethod.INSTANCE;
} else {
return this;
}
}
/**
* See {@link #writeBody(ObjectOutputStream)}.
*
* @see #writeBody(ObjectOutputStream)
*/
void readBody(ObjectInputStream stream) throws IOException, ClassNotFoundException {
body = (JAbstractMethodBody) stream.readObject();
}
boolean replaces(JMethod originalMethod) {
if (this == originalMethod) {
return true;
}
return originalMethod.isExternal() && originalMethod.getSignature().equals(this.getSignature())
&& this.getEnclosingType().replaces(originalMethod.getEnclosingType());
}
/**
* After all types, fields, and methods are written to the stream, this method
* writes method bodies to the stream.
*
* @see JProgram#writeObject(ObjectOutputStream)
*/
void writeBody(ObjectOutputStream stream) throws IOException {
stream.writeObject(body);
}
}