| /* |
| * 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); |
| } |
| } |