com.google.gwt.dev.jjs.ast.JMethodCall Maven / Gradle / Ivy
/*
* 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 must 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 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 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 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 toAdd) {
args = Lists.addAll(args, toAdd);
}
/**
* Returns true
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 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 true
for calls that must be called statically,
* e.g. super.method() invocations, and super() and this() constructor calls.
*/
public boolean isStaticDispatchOnly() {
return polymorphism.isStaticDispatchOnly();
}
/**
* Returns true
for calls that should not be optimized.
*/
public boolean isVolatile() {
return polymorphism.isVolatile();
}
/**
* Override the return type.
*
* 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.
*
* 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);
}
}