blob: a58898a1906df080b22c4f693fa1c258ffe18f73 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* 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 org.jetbrains.java.decompiler.struct.gen;
import org.jetbrains.java.decompiler.code.CodeConstants;
import org.jetbrains.java.decompiler.util.InterpreterUtil;
public class VarType { // TODO: optimize switch
public static final int FALSEBOOLEAN = 1;
public static final VarType VARTYPE_UNKNOWN = new VarType(CodeConstants.TYPE_UNKNOWN);
public static final VarType VARTYPE_INT = new VarType(CodeConstants.TYPE_INT);
public static final VarType VARTYPE_FLOAT = new VarType(CodeConstants.TYPE_FLOAT);
public static final VarType VARTYPE_LONG = new VarType(CodeConstants.TYPE_LONG);
public static final VarType VARTYPE_DOUBLE = new VarType(CodeConstants.TYPE_DOUBLE);
public static final VarType VARTYPE_BYTE = new VarType(CodeConstants.TYPE_BYTE);
public static final VarType VARTYPE_CHAR = new VarType(CodeConstants.TYPE_CHAR);
public static final VarType VARTYPE_SHORT = new VarType(CodeConstants.TYPE_SHORT);
public static final VarType VARTYPE_BOOLEAN = new VarType(CodeConstants.TYPE_BOOLEAN);
public static final VarType VARTYPE_BYTECHAR = new VarType(CodeConstants.TYPE_BYTECHAR);
public static final VarType VARTYPE_SHORTCHAR = new VarType(CodeConstants.TYPE_SHORTCHAR);
public static final VarType VARTYPE_NULL = new VarType(CodeConstants.TYPE_NULL, 0, null);
public static final VarType VARTYPE_GROUP2EMPTY = new VarType(CodeConstants.TYPE_GROUP2EMPTY);
public static final VarType VARTYPE_STRING = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/String");
public static final VarType VARTYPE_CLASS = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/Class");
public static final VarType VARTYPE_OBJECT = new VarType(CodeConstants.TYPE_OBJECT, 0, "java/lang/Object");
public static final VarType VARTYPE_VOID = new VarType(CodeConstants.TYPE_VOID);
public int type;
public int type_family;
public int arraydim;
public String value;
public int stack_size;
public int convinfo;
public VarType(int type) {
this.type = type;
this.arraydim = 0;
value = getChar(type);
setStackSize(type);
setFamily();
}
public VarType(int type, int arraydim) {
this(type);
this.arraydim = arraydim;
setFamily();
}
public VarType(int type, int arraydim, String value) {
this(type);
this.arraydim = arraydim;
this.value = value;
setFamily();
}
public VarType(String strtype) {
this(strtype, false);
}
public VarType(String strtype, boolean cltype) {
parseTypeString(strtype, cltype);
setStackSize(type);
setFamily();
}
public void decArrayDim() {
if (arraydim > 0) {
arraydim--;
setFamily();
}
else {
// throw new RuntimeException("array dimension equals 0!"); FIXME: investigate this case
}
}
public String toString() {
String res = "";
for (int i = 0; i < arraydim; i++) {
res += "[";
}
if (type == CodeConstants.TYPE_OBJECT) {
res += "L" + value + ";";
}
else {
res += value;
}
return res;
}
public VarType copy() {
VarType v = new VarType(type, arraydim, value);
v.convinfo = convinfo;
return v;
}
public boolean isFalseBoolean() {
return (convinfo & FALSEBOOLEAN) != 0;
}
public boolean isSuperset(VarType val) {
return this.equals(val) || this.isStrictSuperset(val);
}
public boolean isStrictSuperset(VarType val) {
int valtype = val.type;
if (valtype == CodeConstants.TYPE_UNKNOWN && type != CodeConstants.TYPE_UNKNOWN) {
return true;
}
if (val.arraydim > 0) {
return this.equals(VARTYPE_OBJECT);
}
else if (arraydim > 0) {
return (valtype == CodeConstants.TYPE_NULL);
}
boolean res = false;
switch (type) {
case CodeConstants.TYPE_INT:
res |= (valtype == CodeConstants.TYPE_SHORT ||
valtype == CodeConstants.TYPE_CHAR);
case CodeConstants.TYPE_SHORT:
res |= (valtype == CodeConstants.TYPE_BYTE);
case CodeConstants.TYPE_CHAR:
res |= (valtype == CodeConstants.TYPE_SHORTCHAR);
case CodeConstants.TYPE_BYTE:
case CodeConstants.TYPE_SHORTCHAR:
res |= (valtype == CodeConstants.TYPE_BYTECHAR);
case CodeConstants.TYPE_BYTECHAR:
res |= (valtype == CodeConstants.TYPE_BOOLEAN);
break;
case CodeConstants.TYPE_OBJECT:
if (valtype == CodeConstants.TYPE_NULL) {
return true;
}
else if (this.equals(VARTYPE_OBJECT)) {
return valtype == CodeConstants.TYPE_OBJECT &&
!val.equals(VARTYPE_OBJECT);
}
}
return res;
}
// type1 and type2 must not be null
public static VarType getCommonMinType(VarType type1, VarType type2) {
if (type1.type == CodeConstants.TYPE_BOOLEAN && type2.type == CodeConstants.TYPE_BOOLEAN) { // special case booleans
return type1.isFalseBoolean() ? type2 : type1;
}
if (type1.isSuperset(type2)) {
return type2;
}
else if (type2.isSuperset(type1)) {
return type1;
}
else if (type1.type_family == type2.type_family) {
switch (type1.type_family) {
case CodeConstants.TYPE_FAMILY_INTEGER:
if ((type1.type == CodeConstants.TYPE_CHAR && type2.type == CodeConstants.TYPE_SHORT)
|| (type1.type == CodeConstants.TYPE_SHORT && type2.type == CodeConstants.TYPE_CHAR)) {
return VARTYPE_SHORTCHAR;
}
else {
return VARTYPE_BYTECHAR;
}
case CodeConstants.TYPE_FAMILY_OBJECT:
return VARTYPE_NULL;
}
}
return null;
}
// type1 and type2 must not be null
public static VarType getCommonSupertype(VarType type1, VarType type2) {
if (type1.type == CodeConstants.TYPE_BOOLEAN && type2.type == CodeConstants.TYPE_BOOLEAN) { // special case booleans
return type1.isFalseBoolean() ? type1 : type2;
}
if (type1.isSuperset(type2)) {
return type1;
}
else if (type2.isSuperset(type1)) {
return type2;
}
else if (type1.type_family == type2.type_family) {
switch (type1.type_family) {
case CodeConstants.TYPE_FAMILY_INTEGER:
if ((type1.type == CodeConstants.TYPE_SHORTCHAR && type2.type == CodeConstants.TYPE_BYTE)
|| (type1.type == CodeConstants.TYPE_BYTE && type2.type == CodeConstants.TYPE_SHORTCHAR)) {
return VARTYPE_SHORT;
}
else {
return VARTYPE_INT;
}
case CodeConstants.TYPE_FAMILY_OBJECT:
return VARTYPE_OBJECT;
}
}
return null;
}
public static VarType getMinTypeInFamily(int family) {
switch (family) {
case CodeConstants.TYPE_FAMILY_BOOLEAN:
return VARTYPE_BOOLEAN;
case CodeConstants.TYPE_FAMILY_INTEGER:
return VARTYPE_BYTECHAR;
case CodeConstants.TYPE_FAMILY_OBJECT:
return VARTYPE_NULL;
case CodeConstants.TYPE_FAMILY_FLOAT:
return VARTYPE_FLOAT;
case CodeConstants.TYPE_FAMILY_LONG:
return VARTYPE_LONG;
case CodeConstants.TYPE_FAMILY_DOUBLE:
return VARTYPE_DOUBLE;
case CodeConstants.TYPE_FAMILY_UNKNOWN:
return VARTYPE_UNKNOWN;
default:
throw new RuntimeException("invalid type family!");
}
}
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (o == null || !(o instanceof VarType)) {
return false;
}
VarType vt = (VarType)o;
return type == vt.type && arraydim == vt.arraydim && InterpreterUtil.equalObjects(value, vt.value);
}
private void parseTypeString(String strtype, boolean cltype) {
for (int i = 0; i < strtype.length(); i++) {
switch (strtype.charAt(i)) {
case '[':
arraydim++;
break;
case 'L':
if (strtype.charAt(strtype.length() - 1) == ';') {
type = CodeConstants.TYPE_OBJECT;
value = strtype.substring(i + 1, strtype.length() - 1);
return;
}
default:
value = strtype.substring(i, strtype.length());
if ((cltype && i == 0) || value.length() > 1) {
type = CodeConstants.TYPE_OBJECT;
}
else {
type = getType(value.charAt(0));
}
return;
}
}
}
private void setStackSize(int type) {
if (arraydim > 0) {
stack_size = 1;
}
else {
stack_size = (type == CodeConstants.TYPE_DOUBLE ||
type == CodeConstants.TYPE_LONG) ? 2 :
((type == CodeConstants.TYPE_VOID ||
type == CodeConstants.TYPE_GROUP2EMPTY) ? 0 : 1);
}
}
private static int getType(char c) {
switch (c) {
case 'B':
return CodeConstants.TYPE_BYTE;
case 'C':
return CodeConstants.TYPE_CHAR;
case 'D':
return CodeConstants.TYPE_DOUBLE;
case 'F':
return CodeConstants.TYPE_FLOAT;
case 'I':
return CodeConstants.TYPE_INT;
case 'J':
return CodeConstants.TYPE_LONG;
case 'S':
return CodeConstants.TYPE_SHORT;
case 'Z':
return CodeConstants.TYPE_BOOLEAN;
case 'V':
return CodeConstants.TYPE_VOID;
case 'G':
return CodeConstants.TYPE_GROUP2EMPTY;
case 'N':
return CodeConstants.TYPE_NOTINITIALIZED;
case 'A':
return CodeConstants.TYPE_ADDRESS;
case 'X':
return CodeConstants.TYPE_BYTECHAR;
case 'Y':
return CodeConstants.TYPE_SHORTCHAR;
case 'U':
return CodeConstants.TYPE_UNKNOWN;
default:
throw new RuntimeException("Invalid type");
}
}
private static String getChar(int type) {
switch (type) {
case CodeConstants.TYPE_BYTE:
return "B";
case CodeConstants.TYPE_CHAR:
return "C";
case CodeConstants.TYPE_DOUBLE:
return "D";
case CodeConstants.TYPE_FLOAT:
return "F";
case CodeConstants.TYPE_INT:
return "I";
case CodeConstants.TYPE_LONG:
return "J";
case CodeConstants.TYPE_SHORT:
return "S";
case CodeConstants.TYPE_BOOLEAN:
return "Z";
case CodeConstants.TYPE_VOID:
return "V";
case CodeConstants.TYPE_GROUP2EMPTY:
return "G";
case CodeConstants.TYPE_NOTINITIALIZED:
return "N";
case CodeConstants.TYPE_ADDRESS:
return "A";
case CodeConstants.TYPE_BYTECHAR:
return "X";
case CodeConstants.TYPE_SHORTCHAR:
return "Y";
case CodeConstants.TYPE_UNKNOWN:
return "U";
case CodeConstants.TYPE_NULL:
case CodeConstants.TYPE_OBJECT:
return null;
default:
throw new RuntimeException("Invalid type");
}
}
public void setFamily() {
if (arraydim > 0) {
this.type_family = CodeConstants.TYPE_FAMILY_OBJECT;
return;
}
switch (type) {
case CodeConstants.TYPE_BYTE:
case CodeConstants.TYPE_BYTECHAR:
case CodeConstants.TYPE_SHORTCHAR:
case CodeConstants.TYPE_CHAR:
case CodeConstants.TYPE_SHORT:
case CodeConstants.TYPE_INT:
this.type_family = CodeConstants.TYPE_FAMILY_INTEGER;
break;
case CodeConstants.TYPE_DOUBLE:
this.type_family = CodeConstants.TYPE_FAMILY_DOUBLE;
break;
case CodeConstants.TYPE_FLOAT:
this.type_family = CodeConstants.TYPE_FAMILY_FLOAT;
break;
case CodeConstants.TYPE_LONG:
this.type_family = CodeConstants.TYPE_FAMILY_LONG;
break;
case CodeConstants.TYPE_BOOLEAN:
this.type_family = CodeConstants.TYPE_FAMILY_BOOLEAN;
break;
case CodeConstants.TYPE_NULL:
case CodeConstants.TYPE_OBJECT:
this.type_family = CodeConstants.TYPE_FAMILY_OBJECT;
break;
default:
this.type_family = CodeConstants.TYPE_FAMILY_UNKNOWN;
}
}
}