blob: b42e7179c4099752932ead4e8cb8dd3216e6220a [file] [log] [blame]
/*
* Copyright (C) 2007 The Android Open Source Project
*
* 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.android.dx.rop.cst;
import com.android.dx.command.dexer.Main;
import com.android.dx.rop.type.Type;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* Constants that represent an arbitrary type (reference or primitive).
*/
public final class CstType extends TypedConstant {
/**
* Intern table for instances.
*
* <p>The initial capacity is based on a medium-size project.
*/
private static final ConcurrentMap<Type, CstType> interns =
new ConcurrentHashMap<>(1_000, 0.75f, Main.CONCURRENCY_LEVEL);
/** {@code non-null;} instance corresponding to the class {@code Object} */
public static final CstType OBJECT = new CstType(Type.OBJECT);
/** {@code non-null;} instance corresponding to the class {@code Boolean} */
public static final CstType BOOLEAN = new CstType(Type.BOOLEAN_CLASS);
/** {@code non-null;} instance corresponding to the class {@code Byte} */
public static final CstType BYTE = new CstType(Type.BYTE_CLASS);
/** {@code non-null;} instance corresponding to the class {@code Character} */
public static final CstType CHARACTER = new CstType(Type.CHARACTER_CLASS);
/** {@code non-null;} instance corresponding to the class {@code Double} */
public static final CstType DOUBLE = new CstType(Type.DOUBLE_CLASS);
/** {@code non-null;} instance corresponding to the class {@code Float} */
public static final CstType FLOAT = new CstType(Type.FLOAT_CLASS);
/** {@code non-null;} instance corresponding to the class {@code Long} */
public static final CstType LONG = new CstType(Type.LONG_CLASS);
/** {@code non-null;} instance corresponding to the class {@code Integer} */
public static final CstType INTEGER = new CstType(Type.INTEGER_CLASS);
/** {@code non-null;} instance corresponding to the class {@code Short} */
public static final CstType SHORT = new CstType(Type.SHORT_CLASS);
/** {@code non-null;} instance corresponding to the class {@code Void} */
public static final CstType VOID = new CstType(Type.VOID_CLASS);
/** {@code non-null;} instance corresponding to the type {@code boolean[]} */
public static final CstType BOOLEAN_ARRAY = new CstType(Type.BOOLEAN_ARRAY);
/** {@code non-null;} instance corresponding to the type {@code byte[]} */
public static final CstType BYTE_ARRAY = new CstType(Type.BYTE_ARRAY);
/** {@code non-null;} instance corresponding to the type {@code char[]} */
public static final CstType CHAR_ARRAY = new CstType(Type.CHAR_ARRAY);
/** {@code non-null;} instance corresponding to the type {@code double[]} */
public static final CstType DOUBLE_ARRAY = new CstType(Type.DOUBLE_ARRAY);
/** {@code non-null;} instance corresponding to the type {@code float[]} */
public static final CstType FLOAT_ARRAY = new CstType(Type.FLOAT_ARRAY);
/** {@code non-null;} instance corresponding to the type {@code long[]} */
public static final CstType LONG_ARRAY = new CstType(Type.LONG_ARRAY);
/** {@code non-null;} instance corresponding to the type {@code int[]} */
public static final CstType INT_ARRAY = new CstType(Type.INT_ARRAY);
/** {@code non-null;} instance corresponding to the type {@code short[]} */
public static final CstType SHORT_ARRAY = new CstType(Type.SHORT_ARRAY);
/**
* {@code non-null;} instance corresponding to the type {@code java.lang.invoke.MethodHandle}
*/
public static final CstType METHOD_HANDLE = new CstType(Type.METHOD_HANDLE);
static {
initInterns();
}
private static void initInterns() {
internInitial(OBJECT);
internInitial(BOOLEAN);
internInitial(BYTE);
internInitial(CHARACTER);
internInitial(DOUBLE);
internInitial(FLOAT);
internInitial(LONG);
internInitial(INTEGER);
internInitial(SHORT);
internInitial(VOID);
internInitial(BOOLEAN_ARRAY);
internInitial(BYTE_ARRAY);
internInitial(CHAR_ARRAY);
internInitial(DOUBLE_ARRAY);
internInitial(FLOAT_ARRAY);
internInitial(LONG_ARRAY);
internInitial(INT_ARRAY);
internInitial(SHORT_ARRAY);
internInitial(METHOD_HANDLE);
}
private static void internInitial(CstType cst) {
if (interns.putIfAbsent(cst.getClassType(), cst) != null) {
throw new IllegalStateException("Attempted re-init of " + cst);
}
}
/** {@code non-null;} the underlying type */
private final Type type;
/**
* {@code null-ok;} the type descriptor corresponding to this instance, if
* calculated
*/
private CstString descriptor;
/**
* Returns an instance of this class that represents the wrapper
* class corresponding to a given primitive type. For example, if
* given {@link Type#INT}, this method returns the class reference
* {@code java.lang.Integer}.
*
* @param primitiveType {@code non-null;} the primitive type
* @return {@code non-null;} the corresponding wrapper class
*/
public static CstType forBoxedPrimitiveType(Type primitiveType) {
switch (primitiveType.getBasicType()) {
case Type.BT_BOOLEAN: return BOOLEAN;
case Type.BT_BYTE: return BYTE;
case Type.BT_CHAR: return CHARACTER;
case Type.BT_DOUBLE: return DOUBLE;
case Type.BT_FLOAT: return FLOAT;
case Type.BT_INT: return INTEGER;
case Type.BT_LONG: return LONG;
case Type.BT_SHORT: return SHORT;
case Type.BT_VOID: return VOID;
}
throw new IllegalArgumentException("not primitive: " + primitiveType);
}
/**
* Returns an interned instance of this class for the given type.
*
* @param type {@code non-null;} the underlying type
* @return {@code non-null;} an appropriately-constructed instance
*/
public static CstType intern(Type type) {
CstType cst = new CstType(type);
CstType result = interns.putIfAbsent(type, cst);
return result != null ? result : cst;
}
/**
* Constructs an instance.
*
* @param type {@code non-null;} the underlying type
*/
public CstType(Type type) {
if (type == null) {
throw new NullPointerException("type == null");
}
if (type == Type.KNOWN_NULL) {
throw new UnsupportedOperationException(
"KNOWN_NULL is not representable");
}
this.type = type;
this.descriptor = null;
}
/** {@inheritDoc} */
@Override
public boolean equals(Object other) {
if (!(other instanceof CstType)) {
return false;
}
return type == ((CstType) other).type;
}
/** {@inheritDoc} */
@Override
public int hashCode() {
return type.hashCode();
}
/** {@inheritDoc} */
@Override
protected int compareTo0(Constant other) {
String thisDescriptor = type.getDescriptor();
String otherDescriptor = ((CstType) other).type.getDescriptor();
return thisDescriptor.compareTo(otherDescriptor);
}
/** {@inheritDoc} */
@Override
public String toString() {
return "type{" + toHuman() + '}';
}
/** {@inheritDoc} */
@Override
public Type getType() {
return Type.CLASS;
}
/** {@inheritDoc} */
@Override
public String typeName() {
return "type";
}
/** {@inheritDoc} */
@Override
public boolean isCategory2() {
return false;
}
/** {@inheritDoc} */
@Override
public String toHuman() {
return type.toHuman();
}
/**
* Gets the underlying type (as opposed to the type corresponding
* to this instance as a constant, which is always
* {@code Class}).
*
* @return {@code non-null;} the type corresponding to the name
*/
public Type getClassType() {
return type;
}
/**
* Gets the type descriptor for this instance.
*
* @return {@code non-null;} the descriptor
*/
public CstString getDescriptor() {
if (descriptor == null) {
descriptor = new CstString(type.getDescriptor());
}
return descriptor;
}
/**
* Returns a human readable package name for this type, like "java.util".
* If this is an array type, this returns the package name of the array's
* component type. If this is a primitive type, this returns "default".
*/
public String getPackageName() {
// descriptor is a string like "[[Ljava/util/String;"
String descriptor = getDescriptor().getString();
int lastSlash = descriptor.lastIndexOf('/');
int lastLeftSquare = descriptor.lastIndexOf('['); // -1 unless this is an array
if (lastSlash == -1) {
return "default";
} else {
// +2 to skip the '[' and the 'L' prefix
return descriptor.substring(lastLeftSquare + 2, lastSlash).replace('/', '.');
}
}
public static void clearInternTable() {
interns.clear();
initInterns();
}
}