| /* |
| * 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.common.InliningMode; |
| import com.google.gwt.dev.javac.JsInteropUtil; |
| 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.jjs.ast.js.JsniMethodBody; |
| import com.google.gwt.dev.jjs.impl.JjsUtils; |
| import com.google.gwt.dev.util.StringInterner; |
| import com.google.gwt.dev.util.collect.Lists; |
| import com.google.gwt.thirdparty.guava.common.collect.Iterables; |
| import com.google.gwt.thirdparty.guava.common.collect.Sets; |
| |
| 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.Comparator; |
| import java.util.List; |
| import java.util.Set; |
| |
| /** |
| * A Java method implementation. |
| */ |
| public class JMethod extends JNode implements JMember, CanBeAbstract { |
| |
| public static final Comparator<JMethod> BY_SIGNATURE_COMPARATOR = new Comparator<JMethod>() { |
| @Override |
| public int compare(JMethod m1, JMethod m2) { |
| return m1.getSignature().compareTo(m2.getSignature()); |
| } |
| }; |
| |
| private JsMemberType jsMemberType = JsMemberType.NONE; |
| private String jsName; |
| private String jsNamespace; |
| private boolean exported; |
| private boolean isJsOverlay = false; |
| private Specialization specialization; |
| private InliningMode inliningMode = InliningMode.NORMAL; |
| private boolean preventDevirtualization = false; |
| private boolean hasSideEffects = true; |
| private boolean defaultMethod = false; |
| private boolean syntheticAccidentalOverride = false; |
| private Set<String> suppressedWarnings; |
| |
| @Override |
| public void setJsMemberInfo(JsMemberType type, String namespace, String name, boolean exported) { |
| this.jsMemberType = type; |
| this.jsName = name; |
| this.jsNamespace = namespace; |
| this.exported = exported; |
| } |
| |
| @Override |
| public boolean isJsInteropEntryPoint() { |
| return exported && !needsDynamicDispatch() && !isJsNative() && !isJsOverlay(); |
| } |
| |
| @Override |
| public boolean canBeReferencedExternally() { |
| if (isJsOverlay() || (!needsDynamicDispatch() && isJsNative())) { |
| // JsOverlays, native constructors and native static methods can not be referenced |
| // externally |
| return false; |
| } |
| for (JMethod method : getOverriddenMethodsIncludingSelf()) { |
| if (method.exported || method.isJsFunctionMethod() || method.isJsNative()) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @Override |
| public boolean canBeImplementedExternally() { |
| return isJsNative() || isJsFunctionMethod(); |
| } |
| |
| /** |
| * Adds a new final parameter to this method. |
| */ |
| public JParameter createFinalParameter(SourceInfo info, String name, JType type) { |
| return createParameter(info, name, type, true, false, false, false); |
| } |
| |
| /** |
| * Adds a new parameter to this method. |
| */ |
| public JParameter createParameter(SourceInfo info, String name, JType type) { |
| return createParameter(info, name, type, false, false, false, false); |
| } |
| |
| /** |
| * Adds a new parameter to this method. |
| */ |
| public JParameter createParameter(SourceInfo info, String name, JType type, boolean isFinal, |
| boolean isVarargs) { |
| return createParameter(info, name, type, isFinal, isVarargs, false, false); |
| } |
| |
| /** |
| * Adds a new parameter to this method that is a copy of {@code from}. |
| */ |
| public JParameter cloneParameter(JParameter from) { |
| return createParameter(from.getSourceInfo(), from.getName(), from.getType(), from.isFinal(), |
| from.isVarargs(), from.isThis(), from.isOptional()); |
| } |
| |
| /** |
| * Creates a parameter to hold the value of this in devirtualized methods. |
| */ |
| public JParameter createThisParameter(SourceInfo info, JType type) { |
| return createParameter(info, "this$static", type, true, false, true, false); |
| } |
| |
| private JParameter createParameter(SourceInfo info, String name, JType type, |
| boolean isFinal, boolean isVarargs, boolean isThis, boolean isOptional) { |
| assert (name != null); |
| assert (type != null); |
| |
| JParameter parameter = new JParameter(info, name, type, isFinal, isVarargs, isThis, isOptional); |
| addParameter(parameter); |
| return parameter; |
| } |
| |
| /** |
| * Adds a parameter to this method. |
| */ |
| private void addParameter(JParameter x) { |
| // Local types can capture local variables and sandwich the parameters of constructors between |
| // the outer reference and the local captures. |
| assert params.isEmpty() || !params.get(params.size() - 1).isVarargs() |
| || getEnclosingType().getClassDisposition().isLocalType(); |
| params = Lists.add(params, x); |
| } |
| |
| private boolean isInterfaceMethod() { |
| return enclosingType instanceof JInterfaceType; |
| } |
| |
| @Override |
| public String getJsNamespace() { |
| if (jsNamespace == null) { |
| jsNamespace = enclosingType.getQualifiedJsName(); |
| } |
| return jsNamespace; |
| } |
| |
| @Override |
| public String getQualifiedJsName() { |
| String namespace = getJsNamespace(); |
| String actualJsName = getJsName(); |
| if (actualJsName.isEmpty()) { |
| assert !needsDynamicDispatch(); |
| return namespace; |
| } else if (JsInteropUtil.isGlobal(namespace) || JsInteropUtil.isWindow(namespace)) { |
| assert !needsDynamicDispatch(); |
| return namespace + "." + actualJsName; |
| } else { |
| return namespace + (isStatic() ? "." : ".prototype.") + actualJsName; |
| } |
| } |
| |
| @Override |
| public String getJsName() { |
| for (JMethod method : getOverriddenMethodsIncludingSelf()) { |
| if (method.jsName != null) { |
| return method.jsName; |
| } |
| } |
| return getJsMemberType().computeName(this); |
| } |
| |
| public boolean isJsConstructor() { |
| return getJsMemberType() == JsMemberType.CONSTRUCTOR; |
| } |
| |
| /** |
| * Returns {@code true} if this method is the first JsMember in the method hierarchy that exposes |
| * an existing non-JsMember inside a class. |
| */ |
| public boolean exposesNonJsMember() { |
| if (isInterfaceMethod() || enclosingType.isJsNative() || !JjsUtils.requiresJsName(this)) { |
| return false; |
| } |
| |
| boolean hasNonJsMemberParent = false; |
| for (JMethod overriddenMethod : overriddenMethods) { |
| if (!JjsUtils.requiresJsName(overriddenMethod)) { |
| hasNonJsMemberParent = true; |
| } |
| if (overriddenMethod.exposesNonJsMember()) { |
| return false; // some other method already exposed this method. |
| } |
| } |
| |
| return hasNonJsMemberParent; |
| } |
| |
| @Override |
| public JsMemberType getJsMemberType() { |
| for (JMethod method : getOverriddenMethodsIncludingSelf()) { |
| if (method.jsMemberType != JsMemberType.NONE) { |
| return method.jsMemberType; |
| } |
| } |
| return JsMemberType.NONE; |
| } |
| |
| private boolean isJsFunctionMethod() { |
| return enclosingType.isJsFunction() && isAbstract(); |
| } |
| |
| public boolean isOrOverridesJsFunctionMethod() { |
| for (JMethod method : getOverriddenMethodsIncludingSelf()) { |
| if (method.isJsFunctionMethod()) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @Override |
| public boolean isJsNative() { |
| return body == null |
| || (!isJsOverlay() && getEnclosingType().isJsNative() && !JProgram.isClinit(this)); |
| } |
| |
| @Override |
| public boolean isJsOverlay() { |
| return isJsOverlay |
| || getEnclosingType().isJsoType(); |
| } |
| |
| public void setSyntheticAccidentalOverride() { |
| this.syntheticAccidentalOverride = true; |
| } |
| |
| public boolean isSyntheticAccidentalOverride() { |
| return syntheticAccidentalOverride; |
| } |
| |
| public void setSpecialization(List<JType> paramTypes, JType returnsType, String targetMethod) { |
| this.specialization = new Specialization(paramTypes, |
| returnsType == null ? this.getOriginalReturnType() : returnsType, targetMethod); |
| } |
| |
| public Specialization getSpecialization() { |
| return specialization; |
| } |
| |
| public void removeSpecialization() { |
| specialization = null; |
| } |
| |
| public boolean isInliningAllowed() { |
| return inliningMode != InliningMode.DO_NOT_INLINE; |
| } |
| |
| public InliningMode getInliningMode() { |
| return inliningMode; |
| } |
| |
| public void setInliningMode(InliningMode inliningMode) { |
| this.inliningMode = inliningMode; |
| } |
| |
| public boolean hasSideEffects() { |
| return hasSideEffects; |
| } |
| |
| public void setHasSideEffects(boolean hasSideEffects) { |
| this.hasSideEffects = hasSideEffects; |
| } |
| |
| public void setDefaultMethod(boolean defaultMethod) { |
| this.defaultMethod = defaultMethod; |
| } |
| |
| @Override |
| public void setJsOverlay() { |
| this.isJsOverlay = true; |
| } |
| |
| public boolean isDefaultMethod() { |
| return defaultMethod; |
| } |
| |
| public boolean isDevirtualizationAllowed() { |
| return !preventDevirtualization; |
| } |
| |
| public void disallowDevirtualization() { |
| this.preventDevirtualization = true; |
| } |
| |
| @Override |
| public boolean isJsMethodVarargs() { |
| if (getParams().isEmpty() || !(canBeReferencedExternally() || canBeImplementedExternally())) { |
| return false; |
| } |
| |
| JParameter lastParameter = Iterables.getLast(getParams()); |
| return lastParameter.isVarargs(); |
| } |
| /** |
| * AST representation of @SpecializeMethod. |
| */ |
| public static class Specialization implements Serializable { |
| private List<JType> params; |
| private JType returns; |
| private String target; |
| private JMethod targetMethod; |
| |
| public Specialization(List<JType> params, JType returns, String target) { |
| this.params = params; |
| this.returns = returns; |
| this.target = target; |
| } |
| |
| public List<JType> getParams() { |
| return params; |
| } |
| |
| public JType getReturns() { |
| return returns; |
| } |
| |
| public String getTarget() { |
| return target; |
| } |
| |
| public JMethod getTargetMethod() { |
| return targetMethod; |
| } |
| |
| public void resolve(List<JType> resolvedParams, JType resolvedReturn, JMethod targetMethod) { |
| this.params = resolvedParams; |
| this.returns = resolvedReturn; |
| this.targetMethod = targetMethod; |
| } |
| } |
| |
| 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, false); |
| } |
| } |
| |
| 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", |
| JClassType.NULL_CLASS, JReferenceType.NULL_TYPE, false, false, true, |
| AccessModifier.PUBLIC); |
| |
| static { |
| NULL_METHOD.setSynthetic(); |
| NULL_METHOD.freezeParamTypes(); |
| } |
| |
| protected transient String signature; |
| |
| /** |
| * The access modifier; stored as an int to reduce memory / serialization footprint. |
| */ |
| private int access; |
| |
| /** |
| * Special serialization treatment. |
| */ |
| private transient JAbstractMethodBody body = null; |
| private final JDeclaredType enclosingType; |
| private boolean isAbstract; |
| private boolean isFinal; |
| private final boolean isStatic; |
| private boolean isSynthetic = false; |
| private boolean isForwarding = 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 Set<JMethod> overriddenMethods = Sets.newLinkedHashSet(); |
| private Set<JMethod> overridingMethods = Sets.newLinkedHashSet(); |
| |
| private List<JParameter> params = Collections.emptyList(); |
| private JType returnType; |
| private List<JClassType> thrownExceptions = Collections.emptyList(); |
| |
| /** |
| * 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, AccessModifier access) { |
| super(info); |
| this.name = StringInterner.get().intern(name); |
| this.enclosingType = enclosingType; |
| this.returnType = returnType; |
| this.isAbstract = isAbstract; |
| this.isStatic = isStatic; |
| this.isFinal = isFinal; |
| this.access = access.ordinal(); |
| } |
| |
| /** |
| * Creates an externalized representation for a method that needs to be resolved. |
| * Useful to refer to methods of magic classes during GwtAstBuilder execution. |
| * |
| * @param fullClassName the class where the method is defined. |
| * @param signature the signature of the method (including its name). |
| * |
| */ |
| public static JMethod getExternalizedMethod(String fullClassName, String signature, |
| boolean isStatic) { |
| |
| JClassType cls = new JClassType(fullClassName); |
| return new JMethod(signature, cls, isStatic); |
| } |
| |
| /** |
| * Construct a bare-bones deserialized external method. |
| */ |
| private JMethod(String signature, JDeclaredType enclosingType, boolean isStatic) { |
| this(SourceOrigin.UNKNOWN, StringInterner.get().intern( |
| signature.substring(0, signature.indexOf('('))), enclosingType, null, false, isStatic, |
| false, AccessModifier.PUBLIC); |
| this.signature = signature; |
| } |
| |
| /** |
| * Add a method that this method overrides. |
| */ |
| public void addOverriddenMethod(JMethod overriddenMethod) { |
| assert canBePolymorphic() : this + " is not polymorphic"; |
| assert overriddenMethod != this : this + " cannot override itself"; |
| overriddenMethods.add(overriddenMethod); |
| } |
| |
| /** |
| * Add a method that overrides this method. |
| */ |
| public void addOverridingMethod(JMethod overridingMethod) { |
| assert canBePolymorphic() : this + " is not polymorphic"; |
| assert overridingMethod != this : this + " cannot override itself"; |
| overridingMethods.add(overridingMethod); |
| } |
| |
| 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); |
| } |
| |
| /** |
| * Returns {@code true} if this method is the first method in the method hierarchy that increases |
| * the visibility of a package private method. |
| */ |
| public boolean exposesPackagePrivateMethod() { |
| if (isPrivate() || isPackagePrivate()) { |
| return false; |
| } |
| |
| boolean hasPackageVisibleParent = false; |
| for (JMethod overriddenMethod : overriddenMethods) { |
| if (overriddenMethod.isInterfaceMethod()) { |
| continue; |
| } |
| if (!overriddenMethod.isPackagePrivate()) { |
| return false; // some other method already exposed this method. |
| } |
| hasPackageVisibleParent = true; |
| } |
| |
| return hasPackageVisibleParent; |
| } |
| |
| public AccessModifier getAccess() { |
| return AccessModifier.values()[access]; |
| } |
| |
| public JAbstractMethodBody getBody() { |
| assert !isExternal() : "External types do not have method bodies."; |
| return body; |
| } |
| |
| @Override |
| public JDeclaredType getEnclosingType() { |
| return enclosingType; |
| } |
| |
| @Override |
| public String getName() { |
| return name; |
| } |
| |
| @Override |
| public String getQualifiedName() { |
| return enclosingType.getName() + "." + getSignature(); |
| } |
| |
| public List<JType> getOriginalParamTypes() { |
| return originalParamTypes; |
| } |
| |
| public JType getOriginalReturnType() { |
| return originalReturnType; |
| } |
| |
| /** |
| * Returns the transitive closure of all the methods this method overrides; this set is ordered |
| * from most specific to least specific, where class methods appear before interface methods. |
| */ |
| public Set<JMethod> getOverriddenMethods() { |
| return overriddenMethods; |
| } |
| |
| /** |
| * Return all overridden methods including the method itself (where it is the first item). |
| */ |
| private Iterable<JMethod> getOverriddenMethodsIncludingSelf() { |
| return Iterables.concat(Collections.singleton(this), overriddenMethods); |
| } |
| |
| /** |
| * Returns the transitive closure of all the methods that override this method; caveat this |
| * list is only complete in monolithic compiles and should not be used in incremental compiles. |
| * The returned set is ordered in such a way that most specific overriding methods appear after |
| * less specific ones. |
| */ |
| public Set<JMethod> getOverridingMethods() { |
| return overridingMethods; |
| } |
| |
| /** |
| * Returns the parameters of this method. |
| */ |
| public List<JParameter> getParams() { |
| return params; |
| } |
| |
| public String getSignature() { |
| if (signature == null) { |
| signature = StringInterner.get().intern(JjsUtils.computeSignature( |
| name, getOriginalParamTypes(), getOriginalReturnType(), isConstructor())); |
| } |
| return signature; |
| } |
| |
| public String getJsniSignature(boolean includeEnclosingClass, boolean includeReturnType) { |
| StringBuilder sb = new StringBuilder(); |
| if (includeEnclosingClass) { |
| sb.append(getEnclosingType().getName()); |
| sb.append("::"); |
| } |
| sb.append(name); |
| sb.append('('); |
| for (JType type : getOriginalParamTypes()) { |
| sb.append(type.getJsniSignatureName()); |
| } |
| sb.append(')'); |
| if (includeReturnType) { |
| sb.append(originalReturnType.getJsniSignatureName()); |
| } |
| return sb.toString(); |
| } |
| |
| public List<JClassType> getThrownExceptions() { |
| return thrownExceptions; |
| } |
| |
| @Override |
| public JType getType() { |
| return returnType; |
| } |
| |
| @Override |
| public boolean isAbstract() { |
| return isAbstract; |
| } |
| |
| public boolean isConstructor() { |
| return false; |
| } |
| |
| public boolean isPackagePrivate() { |
| return access == AccessModifier.DEFAULT.ordinal(); |
| } |
| |
| @Override |
| public boolean isExternal() { |
| return getEnclosingType().isExternal(); |
| } |
| |
| @Override |
| public boolean isFinal() { |
| return isFinal; |
| } |
| |
| public boolean isForwarding() { |
| return isForwarding; |
| } |
| |
| public boolean isJsniMethod() { |
| if (body == null) { |
| return false; |
| } else { |
| return body.isJsniMethodBody(); |
| } |
| } |
| |
| @Override |
| public boolean isPrivate() { |
| return access == AccessModifier.PRIVATE.ordinal(); |
| } |
| |
| @Override |
| public boolean isPublic() { |
| return access == AccessModifier.PUBLIC.ordinal(); |
| } |
| |
| @Override |
| public boolean isStatic() { |
| return isStatic; |
| } |
| |
| @Override |
| public boolean isSynthetic() { |
| return isSynthetic; |
| } |
| |
| /** |
| * Returns <code>true</code> if this method can participate in instance |
| * dispatch. |
| */ |
| @Override |
| public boolean needsDynamicDispatch() { |
| return !isStatic(); |
| } |
| |
| /** |
| * Removes the parameter at the specified index. |
| */ |
| public void removeParam(int index) { |
| params = Lists.remove(params, index); |
| if (isJsniMethod()) { |
| ((JsniMethodBody) getBody()).getFunc().getParameters().remove(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); |
| } |
| } |
| |
| @Override |
| public void setFinal() { |
| setFinal(true); |
| } |
| |
| public void setFinal(boolean isFinal) { |
| this.isFinal = isFinal; |
| } |
| |
| public void setForwarding() { |
| isForwarding = 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); |
| } |
| |
| public void setSynthetic() { |
| isSynthetic = true; |
| } |
| |
| public void setType(JType newType) { |
| returnType = newType; |
| } |
| |
| @Override |
| public Set<String> getSuppressedWarnings() { |
| return suppressedWarnings; |
| } |
| |
| @Override |
| public void setSuppressedWarnings(Set<String> suppressedWarnings) { |
| this.suppressedWarnings = suppressedWarnings; |
| } |
| |
| @Override |
| public void traverse(JVisitor visitor, Context ctx) { |
| if (visitor.visit(this, ctx)) { |
| visitChildren(visitor); |
| } |
| visitor.endVisit(this, ctx); |
| } |
| |
| 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); |
| } |
| } |