/* * Copyright 2007 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.collect.Lists; import java.util.Arrays; import java.util.Collections; import java.util.List; /** * Java method call expression. */ public class JMethodCall extends JExpression { /** * Interesting facts about a method call's polymorphism. */ private static enum Polymorphism { /** * Optimizers have determined that call target is not overridden. */ CANNOT_BE_POLYMORPHIC, /** * Normal polymorphic dispatch. */ NORMAL, /** * An instance call that <i>must</i> be dispatch statically, e.g. * super.method() invocations, and super() and this() constructor calls. */ STATIC_DISPATCH_ONLY, /** * So-named because it's like a 'volatile' field, simply means "do not * attempt to optimize this call". */ VOLATILE; public boolean canBePolymorphic() { return (this != CANNOT_BE_POLYMORPHIC) && (this != STATIC_DISPATCH_ONLY); } public boolean isStaticDispatchOnly() { return this == STATIC_DISPATCH_ONLY; } public boolean isVolatile() { return this == VOLATILE; } } private List<JExpression> args = Collections.emptyList(); private JExpression instance; private JMethod method; private JType overriddenReturnType; private Polymorphism polymorphism = Polymorphism.NORMAL; private boolean markedAsSideAffectFree; /** * Initialize a new method call equivalent to another one. A new instance must * be specified, and the new object has no arguments on initialization. This * forces the caller to potentially deal with cloning objects if needed. */ public JMethodCall(JMethodCall other, JExpression instance, JExpression... args) { this(other, instance, Arrays.asList(args)); } /** * Initialize a new method call equivalent to another one. A new instance must * be specified, and the new object has no arguments on initialization. This * forces the caller to potentially deal with cloning objects if needed. */ public JMethodCall(JMethodCall other, JExpression instance, List<JExpression> args) { super(other.getSourceInfo()); this.instance = instance; this.method = other.method; this.overriddenReturnType = other.overriddenReturnType; this.polymorphism = other.polymorphism; this.markedAsSideAffectFree = other.markedAsSideAffectFree; addArgs(args); } /** * Create a method call. */ public JMethodCall( SourceInfo info, JExpression instance, JMethod method, List<JExpression> args) { super(info); assert (method != null); assert (instance != null || method.isStatic() || this instanceof JNewInstance); this.instance = instance; this.method = method; this.overriddenReturnType = null; addArgs(args); } /** * Create a method call. */ public JMethodCall(SourceInfo info, JExpression instance, JMethod method, JExpression... args) { this(info, instance, method, Arrays.asList(args)); } /** * Inserts an argument at the specified index. */ public void addArg(int index, JExpression toAdd) { args = Lists.add(args, index, toAdd); } /** * Adds an argument to this method. */ public void addArg(JExpression toAdd) { args = Lists.add(args, toAdd); } /** * Adds an argument to this method. */ public void addArgs(JExpression... toAdd) { args = Lists.addAll(args, toAdd); } /** * Adds arguments to this method. */ public void addArgs(List<JExpression> toAdd) { args = Lists.addAll(args, toAdd); } /** * Returns <code>true</code> if the call can dispatch to more than possible * target method. */ public boolean canBePolymorphic() { return polymorphism.canBePolymorphic() && !method.isFinal() && method.canBePolymorphic(); } /** * Creates a new method call to the same method using the same instance but without parameters. */ public JMethodCall cloneWithoutParameters() { return new JMethodCall(this, instance); } /** * Returns the call arguments. */ public List<JExpression> getArgs() { return args; } public JExpression getInstance() { return instance; } public JMethod getTarget() { return method; } @Override public JType getType() { return overriddenReturnType != null ? overriddenReturnType : method.getType(); } public void markSideEffectFree() { markedAsSideAffectFree = true; } @Override public boolean hasSideEffects() { if (markedAsSideAffectFree) { return false; } // TODO(later): optimize? Be sure to check for clinit when we do. return isStaticDispatchOnly() || method.isStatic() ? method.hasSideEffects() : true; } /** * Returns <code>true</code> for calls that <i>must</i> be called statically, * e.g. super.method() invocations, and super() and this() constructor calls. */ public boolean isStaticDispatchOnly() { return polymorphism.isStaticDispatchOnly(); } /** * Returns <code>true</code> for calls that should not be optimized. */ public boolean isVolatile() { return polymorphism.isVolatile(); } /** * Override the return type. * <p> * The method call expression will have {@code overridentReturnType} as its type ignoring the * return type of the target method. This is used during normalizing transformations to preserve * type semantics when calling externally-defined compiler implementation methods. * <p> * For example, Cast.dynamicCast() returns Object but that method is used to implement the cast * operation. Using a stronger type on the call expression allows us to preserve type information * during the latter phases of compilation. */ public void overrideReturnType(JType overridenReturnType) { assert this.overriddenReturnType == null; this.overriddenReturnType = overridenReturnType; } /** * Resolve an external reference during AST stitching. */ public void resolve(JMethod newMethod) { assert newMethod.replaces(method); method = newMethod; } /** * Sets the argument at the specified index. */ public void setArg(int index, JExpression arg) { args = Lists.set(args, index, arg); } /** * See {@link #canBePolymorphic()}. */ public void setCannotBePolymorphic() { assert polymorphism == Polymorphism.NORMAL; assert !method.isAbstract() : "Cannot set static dispacth to an abstract method: " + method; polymorphism = Polymorphism.CANNOT_BE_POLYMORPHIC; } /** * See {@link #isStaticDispatchOnly()}. */ public void setStaticDispatchOnly() { assert polymorphism == Polymorphism.NORMAL; polymorphism = Polymorphism.STATIC_DISPATCH_ONLY; } /** * See {@link #isVolatile()}. */ public void setVolatile() { assert polymorphism == Polymorphism.NORMAL; polymorphism = Polymorphism.VOLATILE; } @Override public void traverse(JVisitor visitor, Context ctx) { if (visitor.visit(this, ctx)) { visitChildren(visitor); } visitor.endVisit(this, ctx); } protected void visitChildren(JVisitor visitor) { if (instance != null) { instance = visitor.accept(instance); } args = visitor.acceptImmutable(args); } }