com.google.gwt.dev.jjs.ast.JMethodCall.java Source code

Java tutorial

Introduction

Here is the source code for com.google.gwt.dev.jjs.ast.JMethodCall.java

Source

/*
 * 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.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 final JType overrideReturnType;
    private Polymorphism polymorphism = Polymorphism.NORMAL;

    /**
     * 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) {
        super(other.getSourceInfo());
        this.instance = instance;
        this.method = other.method;
        this.overrideReturnType = other.overrideReturnType;
        this.polymorphism = other.polymorphism;
    }

    public JMethodCall(SourceInfo info, JExpression instance, JMethod method) {
        super(info);
        assert (method != null);
        assert (instance != null || method.isStatic() || this instanceof JNewInstance);
        this.instance = instance;
        this.method = method;
        this.overrideReturnType = null;
    }

    /**
     * Create a method call whose type is overridden to the specified type,
     * ignoring the return type of the target method. This constructor 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 JMethodCall(SourceInfo info, JExpression instance, JMethod method, JType overrideReturnType) {
        super(info);
        assert (method != null);
        assert (instance != null || method.isStatic());
        this.instance = instance;
        this.method = method;
        assert (overrideReturnType != null);
        this.overrideReturnType = overrideReturnType;
    }

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

    /**
     * Returns the call arguments.
     */
    public List<JExpression> getArgs() {
        return args;
    }

    public JExpression getInstance() {
        return instance;
    }

    public JMethod getTarget() {
        return method;
    }

    public JType getType() {
        if (overrideReturnType != null) {
            return overrideReturnType;
        } else {
            return method.getType();
        }
    }

    @Override
    public boolean hasSideEffects() {
        // TODO(later): optimize? Be sure to check for clinit when we do.
        return 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();
    }

    /**
     * Removes the argument at the specified index.
     */
    public void removeArg(int index) {
        args = Lists.remove(args, index);
    }

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

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