/* * 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 isJsFunctionImplementation() { 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 isJsFunctionImplementation() { return ref.isJsFunctionImplementation(); } @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; } }