blob: 56a4254710a40a903bcf2bc280bdeb83a9af81bb [file] [log] [blame]
/*
* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.vm.ci.meta;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
* Class for recording assumptions made during compilation.
*/
public final class Assumptions implements Iterable<Assumptions.Assumption> {
/**
* Abstract base class for assumptions. An assumption assumes a property of the runtime that may
* be invalidated by subsequent execution (e.g., that a class has no subclasses implementing
* {@link NoFinalizableSubclass Object.finalize()}).
*/
public abstract static class Assumption {
}
/**
* A class for providing information that is only valid in association with a set of
* {@link Assumption}s. It is permissible for AssumptionResults to have no assumptions at all.
* For instance, if {@link ResolvedJavaType#isLeaf()} returns true for a type
* {@link ResolvedJavaType#findLeafConcreteSubtype()} can return an AssumptionResult with no
* assumptions since the leaf information is statically true.
*
* @param <T>
*/
public static class AssumptionResult<T> {
Assumption[] assumptions;
final T result;
private static final Assumption[] EMPTY = new Assumption[0];
public AssumptionResult(T result, Assumption... assumptions) {
this.result = result;
this.assumptions = assumptions;
}
public AssumptionResult(T result) {
this(result, EMPTY);
}
public T getResult() {
return result;
}
public boolean isAssumptionFree() {
return assumptions.length == 0;
}
public void add(AssumptionResult<T> other) {
Assumption[] newAssumptions = Arrays.copyOf(this.assumptions, this.assumptions.length + other.assumptions.length);
System.arraycopy(other.assumptions, 0, newAssumptions, this.assumptions.length, other.assumptions.length);
this.assumptions = newAssumptions;
}
public boolean canRecordTo(Assumptions target) {
/*
* We can use the result if it is either assumption free, or if we have a valid
* Assumptions object where we can record assumptions.
*/
return assumptions.length == 0 || target != null;
}
public void recordTo(Assumptions target) {
assert canRecordTo(target);
if (assumptions.length > 0) {
for (Assumption assumption : assumptions) {
target.record(assumption);
}
}
}
}
/**
* An assumption that a given class has no subclasses implementing {@link Object#finalize()}).
*/
public static final class NoFinalizableSubclass extends Assumption {
private ResolvedJavaType receiverType;
public NoFinalizableSubclass(ResolvedJavaType receiverType) {
this.receiverType = receiverType;
}
@Override
public int hashCode() {
return 31 + receiverType.hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof NoFinalizableSubclass) {
NoFinalizableSubclass other = (NoFinalizableSubclass) obj;
return other.receiverType.equals(receiverType);
}
return false;
}
@Override
public String toString() {
return "NoFinalizableSubclass[receiverType=" + receiverType.toJavaName() + "]";
}
}
/**
* An assumption that a given abstract or interface type has one direct concrete subtype. There
* is no requirement that the subtype is a leaf type.
*/
public static final class ConcreteSubtype extends Assumption {
/**
* Type the assumption is made about.
*/
public final ResolvedJavaType context;
/**
* Assumed concrete sub-type of the context type.
*/
public final ResolvedJavaType subtype;
public ConcreteSubtype(ResolvedJavaType context, ResolvedJavaType subtype) {
this.context = context;
this.subtype = subtype;
assert context.isAbstract();
assert subtype.isConcrete() || context.isInterface() : subtype.toString() + " : " + context.toString();
assert !subtype.isArray() || subtype.getElementalType().isFinalFlagSet() : subtype.toString() + " : " + context.toString();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + context.hashCode();
result = prime * result + subtype.hashCode();
return result;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ConcreteSubtype) {
ConcreteSubtype other = (ConcreteSubtype) obj;
return other.context.equals(context) && other.subtype.equals(subtype);
}
return false;
}
@Override
public String toString() {
return "ConcreteSubtype[context=" + context.toJavaName() + ", subtype=" + subtype.toJavaName() + "]";
}
}
/**
* An assumption that a given type has no subtypes.
*/
public static final class LeafType extends Assumption {
/**
* Type the assumption is made about.
*/
public final ResolvedJavaType context;
public LeafType(ResolvedJavaType context) {
assert !context.isLeaf() : "assumption isn't required for leaf types";
this.context = context;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + context.hashCode();
return result;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof LeafType) {
LeafType other = (LeafType) obj;
return other.context.equals(context);
}
return false;
}
@Override
public String toString() {
return "LeafSubtype[context=" + context.toJavaName() + "]";
}
}
/**
* An assumption that a given virtual method has a given unique implementation.
*/
public static final class ConcreteMethod extends Assumption {
/**
* A virtual (or interface) method whose unique implementation for the receiver type in
* {@link #context} is {@link #impl}.
*/
public final ResolvedJavaMethod method;
/**
* A receiver type.
*/
public final ResolvedJavaType context;
/**
* The unique implementation of {@link #method} for {@link #context}.
*/
public final ResolvedJavaMethod impl;
public ConcreteMethod(ResolvedJavaMethod method, ResolvedJavaType context, ResolvedJavaMethod impl) {
this.method = method;
this.context = context;
this.impl = impl;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + method.hashCode();
result = prime * result + context.hashCode();
result = prime * result + impl.hashCode();
return result;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ConcreteMethod) {
ConcreteMethod other = (ConcreteMethod) obj;
return other.method.equals(method) && other.context.equals(context) && other.impl.equals(impl);
}
return false;
}
@Override
public String toString() {
return "ConcreteMethod[method=" + method.format("%H.%n(%p)%r") + ", context=" + context.toJavaName() + ", impl=" + impl.format("%H.%n(%p)%r") + "]";
}
}
/**
* An assumption that a given call site's method handle did not change.
*/
public static final class CallSiteTargetValue extends Assumption {
public final JavaConstant callSite;
public final JavaConstant methodHandle;
public CallSiteTargetValue(JavaConstant callSite, JavaConstant methodHandle) {
this.callSite = callSite;
this.methodHandle = methodHandle;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + callSite.hashCode();
result = prime * result + methodHandle.hashCode();
return result;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof CallSiteTargetValue) {
CallSiteTargetValue other = (CallSiteTargetValue) obj;
return callSite.equals(other.callSite) && methodHandle.equals(other.methodHandle);
}
return false;
}
@Override
public String toString() {
return "CallSiteTargetValue[callSite=" + callSite + ", methodHandle=" + methodHandle + "]";
}
}
private final Set<Assumption> assumptions = new HashSet<>();
/**
* Returns whether any assumptions have been registered.
*
* @return {@code true} if at least one assumption has been registered, {@code false} otherwise.
*/
public boolean isEmpty() {
return assumptions.isEmpty();
}
@Override
public int hashCode() {
throw new UnsupportedOperationException("hashCode");
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof Assumptions) {
Assumptions that = (Assumptions) obj;
if (!this.assumptions.equals(that.assumptions)) {
return false;
}
return true;
}
return false;
}
@Override
public Iterator<Assumption> iterator() {
return assumptions.iterator();
}
/**
* Records an assumption that the specified type has no finalizable subclasses.
*
* @param receiverType the type that is assumed to have no finalizable subclasses
*/
public void recordNoFinalizableSubclassAssumption(ResolvedJavaType receiverType) {
record(new NoFinalizableSubclass(receiverType));
}
/**
* Records that {@code subtype} is the only concrete subtype in the class hierarchy below
* {@code context}.
*
* @param context the root of the subtree of the class hierarchy that this assumptions is about
* @param subtype the one concrete subtype
*/
public void recordConcreteSubtype(ResolvedJavaType context, ResolvedJavaType subtype) {
record(new ConcreteSubtype(context, subtype));
}
/**
* Records that {@code impl} is the only possible concrete target for a virtual call to
* {@code method} with a receiver of type {@code context}.
*
* @param method a method that is the target of a virtual call
* @param context the receiver type of a call to {@code method}
* @param impl the concrete method that is the only possible target for the virtual call
*/
public void recordConcreteMethod(ResolvedJavaMethod method, ResolvedJavaType context, ResolvedJavaMethod impl) {
record(new ConcreteMethod(method, context, impl));
}
public void record(Assumption assumption) {
assumptions.add(assumption);
}
/**
* Gets a copy of the assumptions recorded in this object as an array.
*/
public Assumption[] toArray() {
return assumptions.toArray(new Assumption[assumptions.size()]);
}
/**
* Copies assumptions recorded by another {@link Assumptions} object into this object.
*/
public void record(Assumptions other) {
assert other != this;
assumptions.addAll(other.assumptions);
}
@Override
public String toString() {
return "Assumptions[" + assumptions + "]";
}
}