pollymc/libraries/classparser/src/constants.h
2018-07-15 14:51:05 +02:00

232 lines
6.6 KiB
C++

#pragma once
#include "errors.h"
#include <sstream>
namespace java
{
enum class constant_type_t : uint8_t
{
j_hole = 0, // HACK: this is a hole in the array, because java is crazy
j_string_data = 1,
j_int = 3,
j_float = 4,
j_long = 5,
j_double = 6,
j_class = 7,
j_string = 8,
j_fieldref = 9,
j_methodref = 10,
j_interface_methodref = 11,
j_nameandtype = 12
// FIXME: missing some constant types, see https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.4
};
struct ref_type_t
{
/**
* Class reference:
* an index within the constant pool to a UTF-8 string containing
* the fully qualified class name (in internal format)
* Used for j_class, j_fieldref, j_methodref and j_interface_methodref
*/
uint16_t class_idx;
// used for j_fieldref, j_methodref and j_interface_methodref
uint16_t name_and_type_idx;
};
struct name_and_type_t
{
uint16_t name_index;
uint16_t descriptor_index;
};
class constant
{
public:
constant_type_t type = constant_type_t::j_hole;
constant(util::membuffer &buf)
{
buf.read(type);
// load data depending on type
switch (type)
{
case constant_type_t::j_float:
buf.read_be(data.int_data);
break;
case constant_type_t::j_int:
buf.read_be(data.int_data); // same as float data really
break;
case constant_type_t::j_double:
buf.read_be(data.long_data);
break;
case constant_type_t::j_long:
buf.read_be(data.long_data); // same as double
break;
case constant_type_t::j_class:
buf.read_be(data.ref_type.class_idx);
break;
case constant_type_t::j_fieldref:
case constant_type_t::j_methodref:
case constant_type_t::j_interface_methodref:
buf.read_be(data.ref_type.class_idx);
buf.read_be(data.ref_type.name_and_type_idx);
break;
case constant_type_t::j_string:
buf.read_be(data.index);
break;
case constant_type_t::j_string_data:
// HACK HACK: for now, we call these UTF-8 and do no further processing.
// Later, we should do some decoding. It's really modified UTF-8
// * U+0000 is represented as 0xC0,0x80 invalid character
// * any single zero byte ends the string
// * characters above U+10000 are encoded like in CESU-8
buf.read_jstr(str_data);
break;
case constant_type_t::j_nameandtype:
buf.read_be(data.name_and_type.name_index);
buf.read_be(data.name_and_type.descriptor_index);
break;
default:
// invalid constant type!
throw new classfile_exception();
}
}
constant(int)
{
}
std::string toString()
{
std::ostringstream ss;
switch (type)
{
case constant_type_t::j_hole:
ss << "Fake legacy entry";
break;
case constant_type_t::j_float:
ss << "Float: " << data.float_data;
break;
case constant_type_t::j_double:
ss << "Double: " << data.double_data;
break;
case constant_type_t::j_int:
ss << "Int: " << data.int_data;
break;
case constant_type_t::j_long:
ss << "Long: " << data.long_data;
break;
case constant_type_t::j_string_data:
ss << "StrData: " << str_data;
break;
case constant_type_t::j_string:
ss << "Str: " << data.index;
break;
case constant_type_t::j_fieldref:
ss << "FieldRef: " << data.ref_type.class_idx << " " << data.ref_type.name_and_type_idx;
break;
case constant_type_t::j_methodref:
ss << "MethodRef: " << data.ref_type.class_idx << " " << data.ref_type.name_and_type_idx;
break;
case constant_type_t::j_interface_methodref:
ss << "IfMethodRef: " << data.ref_type.class_idx << " " << data.ref_type.name_and_type_idx;
break;
case constant_type_t::j_class:
ss << "Class: " << data.ref_type.class_idx;
break;
case constant_type_t::j_nameandtype:
ss << "NameAndType: " << data.name_and_type.name_index << " "
<< data.name_and_type.descriptor_index;
break;
default:
ss << "Invalid entry (" << int(type) << ")";
break;
}
return ss.str();
}
std::string str_data; /** String data in 'modified utf-8'.*/
// store everything here.
union
{
int32_t int_data;
int64_t long_data;
float float_data;
double double_data;
uint16_t index;
ref_type_t ref_type;
name_and_type_t name_and_type;
} data = {0};
};
/**
* A helper class that represents the custom container used in Java class file for storage of
* constants
*/
class constant_pool
{
public:
/**
* Create a pool of constants
*/
constant_pool()
{
}
/**
* Load a java constant pool
*/
void load(util::membuffer &buf)
{
// FIXME: @SANITY this should check for the end of buffer.
uint16_t length = 0;
buf.read_be(length);
length--;
const constant *last_constant = nullptr;
while (length)
{
const constant &cnst = constant(buf);
constants.push_back(cnst);
last_constant = &constants[constants.size() - 1];
if (last_constant->type == constant_type_t::j_double ||
last_constant->type == constant_type_t::j_long)
{
// push in a fake constant to preserve indexing
constants.push_back(constant(0));
length -= 2;
}
else
{
length--;
}
}
}
typedef std::vector<java::constant> container_type;
/**
* Access constants based on jar file index numbers (index of the first element is 1)
*/
java::constant &operator[](std::size_t constant_index)
{
if (constant_index == 0 || constant_index > constants.size())
{
throw new classfile_exception();
}
return constants[constant_index - 1];
}
;
container_type::const_iterator begin() const
{
return constants.begin();
}
;
container_type::const_iterator end() const
{
return constants.end();
}
private:
container_type constants;
};
}