blob: e3611d2421a863b4dee35742379a24c13178ed2a [file] [log] [blame]
/*
* 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.jjs.SourceInfo;
import com.google.gwt.dev.jjs.SourceOrigin;
/**
* Base class for any reference type.
*/
public abstract class JReferenceType extends JType implements CanBeAbstract {
public static final JReferenceType NULL_TYPE =
new JReferenceType(SourceOrigin.UNKNOWN, "null") {
@Override
AnalysisResult getAnalysisResult() {
return AnalysisResult.NULLABLE_EXACT;
}
@Override
public String getJavahSignatureName() {
return "N";
}
@Override
public String getJsniSignatureName() {
return "N";
}
@Override
public JEnumType isEnumOrSubclass() {
return null;
}
@Override
public boolean isAbstract() {
return false;
}
@Override
public boolean isFinal() {
return true;
}
@Override
public boolean isJsoType() {
return false;
}
@Override
public boolean isArrayType() {
return false;
}
@Override
public boolean isNullType() {
return true;
}
@Override
public boolean isJsType() {
return false;
}
@Override
public boolean isJsFunction() {
return false;
}
@Override
public boolean isJsNative() {
return false;
}
@Override
public boolean canBeImplementedExternally() {
return false;
}
@Override
public boolean canBeReferencedExternally() {
return false;
}
@Override
public boolean isJavaLangObject() {
return false;
}
@Override
public void traverse(JVisitor visitor, Context ctx) {
if (visitor.visit(this, ctx)) {
}
visitor.endVisit(this, ctx);
}
private Object readResolve() {
return NULL_TYPE;
}
@Override
public JReferenceType strengthenToNonNull() {
throw new UnsupportedOperationException();
}
};
private transient AnalysisDecoratedTypePool analysisDecoratedTypePool = null;
enum AnalysisResult {
NULLABLE_NOT_EXACT(true, false),
NOT_NULLABLE_NOT_EXACT(false, false),
NULLABLE_EXACT(true, true),
NOT_NULLABLE_EXACT(false, true);
private final boolean isNullable;
private final boolean isExact;
AnalysisResult(boolean isNullable, boolean isExact) {
this.isNullable = isNullable;
this.isExact = isExact;
}
private boolean isNullable() {
return isNullable;
}
private boolean isExact() {
return isExact;
}
}
/**
* A reference type decorated with the result of static analysis. Only two analysis properties
* are computed (mostly during type propagation in TypeTightener: nullness and exactness.
*/
private static class JAnalysisDecoratedType extends JReferenceType {
private final AnalysisResult analysisResult;
private final JReferenceType ref;
private JAnalysisDecoratedType(JReferenceType ref, AnalysisResult analysisResult) {
super(ref.getSourceInfo(), ref.getName());
this.analysisResult = analysisResult;
assert ref.getUnderlyingType().getAnalysisResult() != analysisResult :
"An analysis type for " + ref +
" should not have been constructed as it is equivalent to the original type";
assert !ref.isNullType();
assert !(ref instanceof JAnalysisDecoratedType);
this.ref = ref;
}
@Override
AnalysisDecoratedTypePool getAnalysisDecoratedTypePool() {
return ref.getAnalysisDecoratedTypePool();
}
@Override
AnalysisResult getAnalysisResult() {
return analysisResult;
}
@Override
public String getJavahSignatureName() {
return ref.getJavahSignatureName();
}
@Override
public String getJsniSignatureName() {
return ref.getJsniSignatureName();
}
@Override
public JEnumType isEnumOrSubclass() {
return ref.isEnumOrSubclass();
}
@Override
public JReferenceType getUnderlyingType() {
return ref;
}
@Override
public boolean isAbstract() {
return ref.isAbstract();
}
@Override
public boolean isArrayType() {
return ref.isArrayType();
}
@Override
public boolean isJsType() {
return ref.isJsType();
}
@Override
public boolean isJsFunction() {
return ref.isJsFunction();
}
@Override
public boolean isJsoType() {
return ref.isJsoType();
}
@Override
public boolean isJsNative() {
return ref.isJsNative();
}
@Override
public boolean canBeImplementedExternally() {
return ref.canBeImplementedExternally();
}
@Override
public boolean canBeReferencedExternally() {
return ref.canBeReferencedExternally();
}
@Override
public boolean isJavaLangObject() {
return ref.isJavaLangObject();
}
@Override
public boolean isExternal() {
return ref.isExternal();
}
@Override
public boolean isFinal() {
return ref.isFinal();
}
@Override
public void traverse(JVisitor visitor, Context ctx) {
visitor.accept(ref);
}
private Object readResolve() {
// Reuse the instance stored in the ref type to make sure there is only one analysis result
// per type.
return getAnalysisDecoratedTypePool().getAnalysisDecoratedType(ref, analysisResult);
}
@Override
public String getDescription() {
return super.getDescription() + (!canBeNull() ? " (non-null)" : "") +
(!canBeSubclass() ? "(exact) " : "");
}
}
/**
* Each {@link JReferenceType} has access to the corresponding singletons (one per type of
* analysis result).
*/
private static class AnalysisDecoratedTypePool {
private final JAnalysisDecoratedType[] decoratedAnalysisTypePool =
new JAnalysisDecoratedType[AnalysisResult.values().length - 1];
public JReferenceType getAnalysisDecoratedType(JReferenceType type, AnalysisResult request) {
JReferenceType underlyingType = type.getUnderlyingType();
if (underlyingType.getAnalysisResult() == request) {
return underlyingType;
}
assert request != AnalysisResult.NULLABLE_NOT_EXACT;
int poolIndex = request.ordinal() - 1;
JAnalysisDecoratedType result = decoratedAnalysisTypePool[poolIndex];
if (result == null) {
result = decoratedAnalysisTypePool[poolIndex] =
new JAnalysisDecoratedType(underlyingType, request);
}
return result;
}
}
public JReferenceType(SourceInfo info, String name) {
super(info, name);
}
@Override
public final boolean canBeNull() {
return getAnalysisResult().isNullable();
}
@Override
public final boolean canBeSubclass() {
boolean exact = getAnalysisResult().isExact();
assert !exact || canBeStrengthenedToExactType() :
"A JSO or native type can never be EXACT but " + name + " is.";
return !exact;
}
@Override
public final JLiteral getDefaultValue() {
return JNullLiteral.INSTANCE;
}
@Override
public String getJavahSignatureName() {
return "L" + name.replaceAll("_", "_1").replace('.', '_') + "_2";
}
@Override
public String getJsniSignatureName() {
return "L" + name.replace('.', '/') + ';';
}
@Override
public boolean isPrimitiveType() {
return false;
}
public JReferenceType weakenToNullable() {
if (getUnderlyingType() == this) {
// Underlying types cannot be weakened.
return this;
}
switch (getAnalysisResult()) {
case NOT_NULLABLE_NOT_EXACT:
return getAnalysisDecoratedTypePool().getAnalysisDecoratedType(
this, AnalysisResult.NULLABLE_NOT_EXACT);
case NOT_NULLABLE_EXACT:
return getAnalysisDecoratedTypePool().getAnalysisDecoratedType(
this, AnalysisResult.NULLABLE_EXACT);
case NULLABLE_EXACT:
case NULLABLE_NOT_EXACT:
return this;
}
throw new AssertionError("Unknown AnalysisResult " + getAnalysisResult().toString());
}
public JReferenceType weakenToNonExact() {
if (getUnderlyingType() == this) {
// Underlying types cannot be weakened.
return this;
}
switch (getAnalysisResult()) {
case NULLABLE_EXACT:
return getAnalysisDecoratedTypePool().getAnalysisDecoratedType(
this, AnalysisResult.NULLABLE_NOT_EXACT);
case NOT_NULLABLE_EXACT:
return getAnalysisDecoratedTypePool().getAnalysisDecoratedType(
this, AnalysisResult.NOT_NULLABLE_NOT_EXACT);
case NOT_NULLABLE_NOT_EXACT:
case NULLABLE_NOT_EXACT:
return this;
}
throw new AssertionError("Unknown AnalysisResult " + getAnalysisResult().toString());
}
private boolean canBeStrengthenedToExactType() {
return !isJsoType() && !canBeImplementedExternally();
}
private boolean canBeStrengthenedToNonNull() {
// JSOs can not be strengthened because there is code that assumes that null is a JSO, and
// instance methods calls never throw NPE on null.
// Some methods like JavaScriptObject.cast() will confuse the compiler, due to modeling the
// return type as non-null.
return !isJsoType();
}
@Override
public JReferenceType strengthenToNonNull() {
if (!canBeStrengthenedToNonNull()) {
return this;
}
switch (getAnalysisResult()) {
case NULLABLE_NOT_EXACT:
return getAnalysisDecoratedTypePool().getAnalysisDecoratedType(
this, AnalysisResult.NOT_NULLABLE_NOT_EXACT);
case NULLABLE_EXACT:
return getAnalysisDecoratedTypePool().getAnalysisDecoratedType(
this, AnalysisResult.NOT_NULLABLE_EXACT);
case NOT_NULLABLE_NOT_EXACT:
case NOT_NULLABLE_EXACT:
return this;
}
throw new AssertionError("Unknown AnalysisResult " + getAnalysisResult().toString());
}
public JReferenceType strengthenToExact() {
if (!canBeStrengthenedToExactType()) {
return this;
}
switch (getAnalysisResult()) {
case NOT_NULLABLE_NOT_EXACT:
return getAnalysisDecoratedTypePool().getAnalysisDecoratedType(
this, AnalysisResult.NOT_NULLABLE_EXACT);
case NULLABLE_NOT_EXACT:
return getAnalysisDecoratedTypePool().getAnalysisDecoratedType(
this, AnalysisResult.NULLABLE_EXACT);
case NULLABLE_EXACT:
case NOT_NULLABLE_EXACT:
return this;
}
throw new AssertionError("Unknown AnalysisResult " + getAnalysisResult().toString());
}
/**
* If this type is a non-null type, returns the underlying (original) type.
*/
@Override
public JReferenceType getUnderlyingType() {
return this;
}
@Override
public boolean replaces(JType originalType) {
return super.replaces(originalType) && canBeNull() == originalType.canBeNull();
}
AnalysisDecoratedTypePool getAnalysisDecoratedTypePool() {
assert !(this instanceof JAnalysisDecoratedType);
if (analysisDecoratedTypePool == null) {
analysisDecoratedTypePool = new AnalysisDecoratedTypePool();
}
return analysisDecoratedTypePool;
}
AnalysisResult getAnalysisResult() {
if (isFinal() && canBeStrengthenedToExactType()) {
return AnalysisResult.NULLABLE_EXACT;
}
return AnalysisResult.NULLABLE_NOT_EXACT;
}
}