/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include <sal/config.h>
#include <algorithm>
#include <cassert>
#include <cstring>
#include <set>
#include <string_view>
#include <utility>
#include <vector>
#include <o3tl/string_view.hxx>
#include <osl/endian.h>
#include <osl/file.h>
#include <rtl/character.hxx>
#include <rtl/ref.hxx>
#include <rtl/textenc.h>
#include <rtl/textcvt.h>
#include <rtl/ustring.hxx>
#include <sal/log.hxx>
#include <sal/types.h>
#include <salhelper/simplereferenceobject.hxx>
#include <unoidl/unoidl.hxx>
#include "unoidlprovider.hxx"
namespace unoidl::detail {
class MappedFile: public salhelper::SimpleReferenceObject {
public:
explicit MappedFile(OUString fileUrl);
sal_uInt8 read8(sal_uInt32 offset) const;
sal_uInt16 read16(sal_uInt32 offset) const;
sal_uInt32 read32(sal_uInt32 offset) const;
sal_uInt64 read64(sal_uInt32 offset) const;
float readIso60599Binary32(sal_uInt32 offset) const;
double readIso60599Binary64(sal_uInt32 offset) const;
OUString readNulName(sal_uInt32 offset) /*const*/;
OUString readIdxName(sal_uInt32 * offset) const
{ return readIdxString(offset, RTL_TEXTENCODING_ASCII_US); }
OUString readIdxString(sal_uInt32 * offset) const
{ return readIdxString(offset, RTL_TEXTENCODING_UTF8); }
OUString uri;
oslFileHandle handle;
sal_uInt64 size;
void * address;
private:
virtual ~MappedFile() override;
sal_uInt8 get8(sal_uInt32 offset) const;
sal_uInt16 get16(sal_uInt32 offset) const;
sal_uInt32 get32(sal_uInt32 offset) const;
sal_uInt64 get64(sal_uInt32 offset) const;
float getIso60599Binary32(sal_uInt32 offset) const;
double getIso60599Binary64(sal_uInt32 offset) const;
OUString readIdxString(sal_uInt32 * offset, rtl_TextEncoding encoding)
const;
};
namespace {
// sizeof (Memory16) == 2
struct Memory16 {
unsigned char byte[2];
sal_uInt16 getUnsigned16() const {
return static_cast< sal_uInt16 >(byte[0])
| (static_cast< sal_uInt16 >(byte[1]) << 8);
}
};
// sizeof (Memory32) == 4
struct Memory32 {
unsigned char byte[4];
sal_uInt32 getUnsigned32() const {
return static_cast< sal_uInt32 >(byte[0])
| (static_cast< sal_uInt32 >(byte[1]) << 8)
| (static_cast< sal_uInt32 >(byte[2]) << 16)
| (static_cast< sal_uInt32 >(byte[3]) << 24);
}
float getIso60599Binary32() const {
union {
unsigned char buf[4];
float f; // assuming float is ISO 60599 binary32
} sa;
#if defined OSL_LITENDIAN
sa.buf[0] = byte[0];
sa.buf[1] = byte[1];
sa.buf[2] = byte[2];
sa.buf[3] = byte[3];
#else
sa.buf[0] = byte[3];
sa.buf[1] = byte[2];
sa.buf[2] = byte[1];
sa.buf[3] = byte[0];
#endif
return sa.f;
}
};
// sizeof (Memory64) == 8
struct Memory64 {
unsigned char byte[8];
sal_uInt64 getUnsigned64() const {
return static_cast< sal_uInt64 >(byte[0])
| (static_cast< sal_uInt64 >(byte[1]) << 8)
| (static_cast< sal_uInt64 >(byte[2]) << 16)
| (static_cast< sal_uInt64 >(byte[3]) << 24)
| (static_cast< sal_uInt64 >(byte[4]) << 32)
| (static_cast< sal_uInt64 >(byte[5]) << 40)
| (static_cast< sal_uInt64 >(byte[6]) << 48)
| (static_cast< sal_uInt64 >(byte[7]) << 56);
}
double getIso60599Binary64() const {
union {
unsigned char buf[8];
double d; // assuming double is ISO 60599 binary64
} sa;
#if defined OSL_LITENDIAN
sa.buf[0] = byte[0];
sa.buf[1] = byte[1];
sa.buf[2] = byte[2];
sa.buf[3] = byte[3];
sa.buf[4] = byte[4];
sa.buf[5] = byte[5];
sa.buf[6] = byte[6];
sa.buf[7] = byte[7];
#else
sa.buf[0] = byte[7];
sa.buf[1] = byte[6];
sa.buf[2] = byte[5];
sa.buf[3] = byte[4];
sa.buf[4] = byte[3];
sa.buf[5] = byte[2];
sa.buf[6] = byte[1];
sa.buf[7] = byte[0];
#endif
return sa.d;
}
};
bool isSimpleType(std::u16string_view type) {
return type == u"void" || type == u"boolean" || type == u"byte"
|| type == u"short" || type == u"unsigned short" || type == u"long"
|| type == u"unsigned long" || type == u"hyper"
|| type == u"unsigned hyper" || type == u"float" || type == u"double"
|| type == u"char" || type == u"string" || type == u"type"
|| type == u"any";
}
// For backwards compatibility, does not strictly check segments to match
//
// <segment> ::= <blocks> | <block>
// <blocks> ::= <capital> <other>* ("_" <block>)*
// <block> ::= <other>+
// <other> ::= <capital> | "a"--"z" | "0"--"9"
// <capital> ::= "A"--"Z"
//
bool isIdentifier(std::u16string_view type, bool scoped) {
if (type.empty()) {
return false;
}
for (size_t i = 0; i != type.size(); ++i) {
sal_Unicode c = type[i];
if (c == '.') {
if (!scoped || i == 0 || i == type.size() - 1
|| type[i - 1] == '.')
{
return false;
}
} else if (!rtl::isAsciiAlphanumeric(c) && c != '_') {
return false;
}
}
return true;
}
void checkTypeName(
rtl::Reference< MappedFile > const & file, std::u16string_view type)
{
std::u16string_view nucl(type);
bool args = false;
while (o3tl::starts_with(nucl, u"[]", &nucl)) {}
size_t i = nucl.find('<');
if (i != std::u16string_view::npos) {
std::u16string_view tmpl(nucl.substr(0, i));
do {
++i; // skip '<' or ','
size_t j = i;
for (size_t level = 0; j != nucl.size(); ++j) {
sal_Unicode c = nucl[j];
if (c == ',') {
if (level == 0) {
break;
}
} else if (c == '<') {
++level;
} else if (c == '>') {
if (level == 0) {
break;
}
--level;
}
}
if (j != nucl.size()) {
checkTypeName(file, nucl.substr(i, j - i));
args = true;
}
i = j;
} while (i != nucl.size() && nucl[i] != '>');
if (i != nucl.size() - 1 || nucl[i] != '>' || !args) {
tmpl = {}; // bad input
}
nucl = tmpl;
}
if (isSimpleType(nucl) ? args : !isIdentifier(nucl, true)) {
throw FileFormatException(
file->uri, OUString::Concat("UNOIDL format: bad type \"") + type + "\"");
}
}
void checkEntityName(
rtl::Reference< MappedFile > const & file, std::u16string_view name)
{
if (isSimpleType(name) || !isIdentifier(name, false)) {
throw FileFormatException(
file->uri, OUString::Concat("UNOIDL format: bad entity name \"") + name + "\"");
}
}
}
MappedFile::MappedFile(OUString fileUrl): uri(std::move(fileUrl)), handle(nullptr) {
oslFileError e = osl_openFile(uri.pData, &handle, osl_File_OpenFlag_Read);
switch (e) {
case osl_File_E_None:
break;
case osl_File_E_NOENT:
throw NoSuchFileException(uri);
default:
throw FileFormatException(uri, "cannot open: " + OUString::number(e));
}
e = osl_getFileSize(handle, &size);
if (e == osl_File_E_None) {
e = osl_mapFile(
handle, &address, size, 0, osl_File_MapFlag_RandomAccess);
}
if (e != osl_File_E_None) {
oslFileError e2 = osl_closeFile(handle);
SAL_WARN_IF(
e2 != osl_File_E_None, "unoidl",
"cannot close " << uri << ": " << +e2);
throw FileFormatException(uri, "cannot mmap: " + OUString::number(e));
}
}
sal_uInt8 MappedFile::read8(sal_uInt32 offset) const {
assert(size >= 8);
if (offset > size - 1) {
throw FileFormatException(
uri, u"UNOIDL format: offset for 8-bit value too large"_ustr);
}
return get8(offset);
}
sal_uInt16 MappedFile::read16(sal_uInt32 offset) const {
assert(size >= 8);
if (offset > size - 2) {
throw FileFormatException(
uri, u"UNOIDL format: offset for 16-bit value too large"_ustr);
}
return get16(offset);
}
sal_uInt32 MappedFile::read32(sal_uInt32 offset) const {
assert(size >= 8);
if (offset > size - 4) {
throw FileFormatException(
uri, u"UNOIDL format: offset for 32-bit value too large"_ustr);
}
return get32(offset);
}
sal_uInt64 MappedFile::read64(sal_uInt32 offset) const {
assert(size >= 8);
if (offset > size - 8) {
throw FileFormatException(
uri, u"UNOIDL format: offset for 64-bit value too large"_ustr);
}
return get64(offset);
}
float MappedFile::readIso60599Binary32(sal_uInt32 offset) const {
assert(size >= 8);
if (offset > size - 4) {
throw FileFormatException(
uri, u"UNOIDL format: offset for 32-bit value too large"_ustr);
}
return getIso60599Binary32(offset);
}
double MappedFile::readIso60599Binary64(sal_uInt32 offset) const {
assert(size >= 8);
if (offset > size - 8) {
throw FileFormatException(
uri, u"UNOIDL format: offset for 64-bit value too large"_ustr);
}
return getIso60599Binary64(offset);
}
OUString MappedFile::readNulName(sal_uInt32 offset) {
if (offset > size) {
throw FileFormatException(
uri, u"UNOIDL format: offset for string too large"_ustr);
}
sal_uInt64 end = offset;
for (;; ++end) {
if (end == size) {
throw FileFormatException(
uri, u"UNOIDL format: string misses trailing NUL"_ustr);
}
if (static_cast< char const * >(address)[end] == 0) {
break;
}
}
if (end - offset > SAL_MAX_INT32) {
throw FileFormatException(uri, u"UNOIDL format: string too long"_ustr);
}
OUString name;
if (!rtl_convertStringToUString(
&name.pData, static_cast< char const * >(address) + offset,
end - offset, RTL_TEXTENCODING_ASCII_US,
(RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR
| RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
| RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)))
{
throw FileFormatException(uri, u"UNOIDL format: name is not ASCII"_ustr);
}
checkEntityName(this, name);
return name;
}
MappedFile::~MappedFile() {
oslFileError e = osl_unmapMappedFile(handle, address, size);
SAL_WARN_IF(e != osl_File_E_None, "unoidl", "cannot unmap: " << +e);
e = osl_closeFile(handle);
SAL_WARN_IF(e != osl_File_E_None, "unoidl", "cannot close: " << +e);
}
sal_uInt8 MappedFile::get8(sal_uInt32 offset) const {
assert(size >= 8);
assert(offset <= size - 1);
return static_cast< char const * >(address)[offset];
}
sal_uInt16 MappedFile::get16(sal_uInt32 offset) const {
assert(size >= 8);
assert(offset <= size - 2);
return reinterpret_cast< Memory16 const * >(
static_cast< char const * >(address) + offset)->getUnsigned16();
}
sal_uInt32 MappedFile::get32(sal_uInt32 offset) const {
assert(size >= 8);
assert(offset <= size - 4);
return reinterpret_cast< Memory32 const * >(
static_cast< char const * >(address) + offset)->getUnsigned32();
}
sal_uInt64 MappedFile::get64(sal_uInt32 offset) const {
assert(size >= 8);
assert(offset <= size - 8);
return reinterpret_cast< Memory64 const * >(
static_cast< char const * >(address) + offset)->getUnsigned64();
}
float MappedFile::getIso60599Binary32(sal_uInt32 offset) const {
assert(size >= 8);
assert(offset <= size - 4);
return reinterpret_cast< Memory32 const * >(
static_cast< char const * >(address) + offset)->getIso60599Binary32();
}
double MappedFile::getIso60599Binary64(sal_uInt32 offset) const {
assert(size >= 8);
assert(offset <= size - 8);
return reinterpret_cast< Memory64 const * >(
static_cast< char const * >(address) + offset)->getIso60599Binary64();
}
OUString MappedFile::readIdxString(
sal_uInt32 * offset, rtl_TextEncoding encoding) const
{
assert(offset != nullptr);
sal_uInt32 len = read32(*offset);
sal_uInt32 off;
if ((len & 0x80000000) == 0) {
off = *offset;
*offset += 4 + len;
} else {
*offset += 4;
off = len & ~0x80000000;
len = read32(off);
if ((len & 0x80000000) != 0) {
throw FileFormatException(
uri, u"UNOIDL format: string length high bit set"_ustr);
}
}
if (len > SAL_MAX_INT32 || len > size - off - 4) {
throw FileFormatException(
uri, u"UNOIDL format: size of string is too large"_ustr);
}
OUString name;
if (!rtl_convertStringToUString(
&name.pData, static_cast< char const * >(address) + off + 4, len,
encoding,
(RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR
| RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
| RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)))
{
throw FileFormatException(
uri, u"UNOIDL format: string bytes do not match encoding"_ustr);
}
return name;
}
// sizeof (MapEntry) == 8
struct MapEntry {
Memory32 name;
Memory32 data;
};
static bool operator <(const Map& map1, const Map& map2) {
return map1.begin < map2.begin
|| (map1.begin == map2.begin && map1.size < map2.size);
}
namespace {
enum Compare { COMPARE_LESS, COMPARE_GREATER, COMPARE_EQUAL };
Compare compare(
rtl::Reference< MappedFile > const & file, std::u16string_view name,
sal_Int32 nameOffset, sal_Int32 nameLength, MapEntry const * entry)
{
assert(file.is());
assert(entry != nullptr);
sal_uInt32 off = entry->name.getUnsigned32();
if (off > file->size - 1) { // at least a trailing NUL
throw FileFormatException(
file->uri, u"UNOIDL format: string offset too large"_ustr);
}
assert(nameLength >= 0);
sal_uInt64 min = std::min(
static_cast< sal_uInt64 >(nameLength), file->size - off);
for (sal_uInt64 i = 0; i != min; ++i) {
sal_Unicode c1 = name[nameOffset + i];
sal_Unicode c2 = static_cast< unsigned char const * >(file->address)[
off + i];
if (c1 < c2) {
return COMPARE_LESS;
} else if (c1 > c2 || c2 == 0) {
// ...the "|| c2 == 0" is for the odd case where name erroneously
// contains NUL characters
return COMPARE_GREATER;
}
}
if (static_cast< sal_uInt64 >(nameLength) == min) {
if (file->size - off == min) {
throw FileFormatException(
file->uri, u"UNOIDL format: string misses trailing NUL"_ustr);
}
return
static_cast< unsigned char const * >(file->address)[off + min] == 0
? COMPARE_EQUAL : COMPARE_LESS;
} else {
return COMPARE_GREATER;
}
}
sal_uInt32 findInMap(
rtl::Reference< MappedFile > const & file, MapEntry const * mapBegin,
sal_uInt32 mapSize, OUString const & name, sal_Int32 nameOffset,
sal_Int32 nameLength)
{
if (mapSize == 0) {
return 0;
}
sal_uInt32 n = mapSize / 2;
MapEntry const * p = mapBegin + n;
switch (compare(file, name, nameOffset, nameLength, p)) {
case COMPARE_LESS:
return findInMap(file, mapBegin, n, name, nameOffset, nameLength);
case COMPARE_GREATER:
return findInMap(
file, p + 1, mapSize - n - 1, name, nameOffset, nameLength);
default: // COMPARE_EQUAL
break;
}
sal_uInt32 off = mapBegin[n].data.getUnsigned32();
if (off == 0) {
throw FileFormatException(
file->uri, u"UNOIDL format: map entry data offset is null"_ustr);
}
return off;
}
#if defined(__COVERITY__) && __COVERITY_MAJOR__ <= 2023
extern "C" void __coverity_tainted_data_sanitize__(void *);
#endif
std::vector< OUString > readAnnotations(
bool annotated, rtl::Reference< MappedFile > const & file,
sal_uInt32 offset, sal_uInt32 * newOffset = nullptr)
{
std::vector< OUString > ans;
if (annotated) {
sal_uInt32 n = file->read32(offset);
#if defined(__COVERITY__) && __COVERITY_MAJOR__ <= 2023
__coverity_tainted_data_sanitize__(&n);
#endif
offset += 4;
for (sal_uInt32 i = 0; i != n; ++i) {
ans.push_back(file->readIdxString(&offset));
}
}
if (newOffset != nullptr) {
*newOffset = offset;
}
return ans;
}
ConstantValue readConstant(
rtl::Reference< MappedFile > const & file, sal_uInt32 offset,
sal_uInt32 * newOffset, bool * annotated)
{
assert(file.is());
int v = file->read8(offset);
int type = v & 0x7F;
if (annotated != nullptr) {
*annotated = (v & 0x80) != 0;
}
switch (type) {
case 0: // BOOLEAN
v = file->read8(offset + 1);
if (newOffset != nullptr) {
*newOffset = offset + 2;
}
switch (v) {
case 0:
return ConstantValue(false);
case 1:
return ConstantValue(true);
default:
throw FileFormatException(
file->uri,
("UNOIDL format: bad boolean constant value "
+ OUString::number(v)));
}
case 1: // BYTE
if (newOffset != nullptr) {
*newOffset = offset + 2;
}
return ConstantValue(static_cast< sal_Int8 >(file->read8(offset + 1)));
//TODO: implementation-defined behavior of conversion from sal_uInt8
// to sal_Int8 relies on two's complement representation
case 2: // SHORT
if (newOffset != nullptr) {
*newOffset = offset + 3;
}
return ConstantValue(
static_cast< sal_Int16 >(file->read16(offset + 1)));
//TODO: implementation-defined behavior of conversion from
// sal_uInt16 to sal_Int16 relies on two's complement representation
case 3: // UNSIGNED SHORT
if (newOffset != nullptr) {
*newOffset = offset + 3;
}
return ConstantValue(file->read16(offset + 1));
case 4: // LONG
if (newOffset != nullptr) {
*newOffset = offset + 5;
}
return ConstantValue(
static_cast< sal_Int32 >(file->read32(offset + 1)));
//TODO: implementation-defined behavior of conversion from
// sal_uInt32 to sal_Int32 relies on two's complement representation
case 5: // UNSIGNED LONG
if (newOffset != nullptr) {
*newOffset = offset + 5;
}
return ConstantValue(file->read32(offset + 1));
case 6: // HYPER
if (newOffset != nullptr) {
*newOffset = offset + 9;
}
return ConstantValue(
static_cast< sal_Int64 >(file->read64(offset + 1)));
//TODO: implementation-defined behavior of conversion from
// sal_uInt64 to sal_Int64 relies on two's complement representation
case 7: // UNSIGNED HYPER
if (newOffset != nullptr) {
*newOffset = offset + 9;
}
return ConstantValue(file->read64(offset + 1));
case 8: // FLOAT
if (newOffset != nullptr) {
*newOffset = offset + 5;
}
return ConstantValue(file->readIso60599Binary32(offset + 1));
case 9: // DOUBLE
if (newOffset != nullptr) {
*newOffset = offset + 9;
}
return ConstantValue(file->readIso60599Binary64(offset + 1));
default:
throw FileFormatException(
file->uri,
"UNOIDL format: bad constant type byte " + OUString::number(v));
}
}
rtl::Reference< Entity > readEntity(
rtl::Reference< MappedFile > const & file, sal_uInt32 offset,
std::set<Map> && trace);
class UnoidlModuleEntity;
class UnoidlCursor: public MapCursor {
public:
UnoidlCursor(
rtl::Reference< MappedFile > file,
rtl::Reference<UnoidlProvider> reference1,
rtl::Reference<UnoidlModuleEntity> reference2,
NestedMap const & map):
file_(std::move(file)), reference1_(std::move(reference1)), reference2_(std::move(reference2)),
map_(map), index_(0)
{}
private:
virtual ~UnoidlCursor() noexcept override {}
virtual rtl::Reference< Entity > getNext(OUString * name) override;
rtl::Reference< MappedFile > file_;
rtl::Reference<UnoidlProvider> reference1_; // HACK to keep alive whatever
rtl::Reference<UnoidlModuleEntity> reference2_; // owner of map_
NestedMap const & map_;
sal_uInt32 index_;
};
rtl::Reference< Entity > UnoidlCursor::getNext(OUString * name) {
assert(name != nullptr);
rtl::Reference< Entity > ent;
if (index_ != map_.map.size) {
*name = file_->readNulName(map_.map.begin[index_].name.getUnsigned32());
ent = readEntity(
file_, map_.map.begin[index_].data.getUnsigned32(), std::set(map_.trace));
++index_;
}
return ent;
}
class UnoidlModuleEntity: public ModuleEntity {
public:
UnoidlModuleEntity(
rtl::Reference< MappedFile > const & file, sal_uInt32 mapOffset,
sal_uInt32 mapSize, std::set<Map> && trace):
file_(file)
{
assert(file.is());
map_.map.begin = reinterpret_cast<MapEntry const *>(
static_cast<char const *>(file_->address) + mapOffset);
map_.map.size = mapSize;
map_.trace = std::move(trace);
if (!map_.trace.insert(map_.map).second) {
throw FileFormatException(
file_->uri, u"UNOIDL format: recursive map"_ustr);
}
}
private:
virtual ~UnoidlModuleEntity() noexcept override {}
virtual std::vector< OUString > getMemberNames() const override;
virtual rtl::Reference< MapCursor > createCursor() const override {
return new UnoidlCursor(
file_, rtl::Reference<UnoidlProvider>(),
const_cast<UnoidlModuleEntity *>(this), map_);
}
rtl::Reference< MappedFile > file_;
NestedMap map_;
};
std::vector< OUString > UnoidlModuleEntity::getMemberNames() const {
std::vector< OUString > names;
for (sal_uInt32 i = 0; i != map_.map.size; ++i) {
names.push_back(
file_->readNulName(map_.map.begin[i].name.getUnsigned32()));
}
return names;
}
rtl::Reference< Entity > readEntity(
rtl::Reference< MappedFile > const & file, sal_uInt32 offset,
std::set<Map> && trace)
{
assert(file.is());
int v = file->read8(offset);
int type = v & 0x3F;
bool published = (v & 0x80) != 0;
bool annotated = (v & 0x40) != 0;
bool flag = (v & 0x20) != 0;
switch (type) {
case 0: // module
{
if (v != 0) {
throw FileFormatException(
file->uri,
("UNOIDL format: bad module type byte "
+ OUString::number(v)));
}
sal_uInt32 n = file->read32(offset + 1);
if (n > SAL_MAX_INT32) {
throw FileFormatException(
file->uri, u"UNOIDL format: too many items in module"_ustr);
}
if (sal_uInt64(offset) + 5 + 8 * sal_uInt64(n) > file->size)
// cannot overflow
{
throw FileFormatException(
file->uri,
u"UNOIDL format: module map offset + size too large"_ustr);
}
return new UnoidlModuleEntity(file, offset + 5, n, std::move(trace));
}
case 1: // enum type
{
sal_uInt32 n = file->read32(offset + 1);
if (n == 0) {
throw FileFormatException(
file->uri, u"UNOIDL format: enum type with no members"_ustr);
}
if (n > SAL_MAX_INT32) {
throw FileFormatException(
file->uri, u"UNOIDL format: too many members of enum type"_ustr);
}
offset += 5;
std::vector< EnumTypeEntity::Member > mems;
mems.reserve(n);
for (sal_uInt32 i = 0; i != n; ++i) {
OUString memName(file->readIdxName(&offset));
checkEntityName(file, memName);
sal_Int32 memValue = static_cast< sal_Int32 >(
file->read32(offset));
//TODO: implementation-defined behavior of conversion from
// sal_uInt32 to sal_Int32 relies on two's complement
// representation
offset += 4;
mems.emplace_back(
memName, memValue,
readAnnotations(annotated, file, offset, &offset));
}
return new EnumTypeEntity(
published, std::move(mems), readAnnotations(annotated, file, offset));
}
case 2: // plain struct type without base
case 2 | 0x20: // plain struct type with base
{
++offset;
OUString base;
if (flag) {
base = file->readIdxName(&offset);
if (base.isEmpty()) {
throw FileFormatException(
file->uri,
(u"UNOIDL format: empty base type name of plain struct"
" type"_ustr));
}
checkTypeName(file, base);
}
sal_uInt32 n = file->read32(offset);
if (n > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
(u"UNOIDL format: too many direct members of plain struct"
" type"_ustr));
}
offset += 4;
std::vector< PlainStructTypeEntity::Member > mems;
mems.reserve(n);
for (sal_uInt32 i = 0; i != n; ++i) {
OUString memName(file->readIdxName(&offset));
checkEntityName(file, memName);
OUString memType(file->readIdxName(&offset));
checkTypeName(file, memType);
mems.emplace_back(
memName, memType,
readAnnotations(annotated, file, offset, &offset));
}
return new PlainStructTypeEntity(
published, base, std::move(mems),
readAnnotations(annotated, file, offset));
}
case 3: // polymorphic struct type template
{
sal_uInt32 n = file->read32(offset + 1);
if (n > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
(u"UNOIDL format: too many type parameters of polymorphic"
" struct type template"_ustr));
}
offset += 5;
std::vector< OUString > params;
params.reserve(n);
for (sal_uInt32 i = 0; i != n; ++i) {
OUString param(file->readIdxName(&offset));
checkEntityName(file, param);
params.push_back(param);
}
n = file->read32(offset);
if (n > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
(u"UNOIDL format: too many members of polymorphic struct"
" type template"_ustr));
}
offset += 4;
std::vector< PolymorphicStructTypeTemplateEntity::Member > mems;
mems.reserve(n);
for (sal_uInt32 i = 0; i != n; ++i) {
v = file->read8(offset);
++offset;
OUString memName(file->readIdxName(&offset));
checkEntityName(file, memName);
OUString memType(file->readIdxName(&offset));
checkTypeName(file, memType);
if (v > 1) {
throw FileFormatException(
file->uri,
("UNOIDL format: bad flags " + OUString::number(v)
+ " for member " + memName
+ " of polymorphic struct type template"));
}
mems.emplace_back(
memName, memType, v == 1,
readAnnotations(annotated, file, offset, &offset));
}
return new PolymorphicStructTypeTemplateEntity(
published, std::move(params), std::move(mems),
readAnnotations(annotated, file, offset));
}
case 4: // exception type without base
case 4 | 0x20: // exception type with base
{
++offset;
OUString base;
if (flag) {
base = file->readIdxName(&offset);
if (base.isEmpty()) {
throw FileFormatException(
file->uri,
(u"UNOIDL format: empty base type name of exception"
" type"_ustr));
}
checkTypeName(file, base);
}
sal_uInt32 n = file->read32(offset);
if (n > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
u"UNOIDL format: too many direct members of exception type"_ustr);
}
offset += 4;
std::vector< ExceptionTypeEntity::Member > mems;
mems.reserve(n);
for (sal_uInt32 i = 0; i != n; ++i) {
OUString memName(file->readIdxName(&offset));
checkEntityName(file, memName);
OUString memType(file->readIdxName(&offset));
checkTypeName(file, memType);
mems.emplace_back(
memName, memType,
readAnnotations(annotated, file, offset, &offset));
}
return new ExceptionTypeEntity(
published, base, std::move(mems),
readAnnotations(annotated, file, offset));
}
case 5: // interface type
{
sal_uInt32 n = file->read32(offset + 1);
if (n > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
(u"UNOIDL format: too many direct mandatory bases of"
" interface type"_ustr));
}
offset += 5;
std::vector< AnnotatedReference > mandBases;
mandBases.reserve(n);
for (sal_uInt32 i = 0; i != n; ++i) {
OUString base(file->readIdxName(&offset));
checkTypeName(file, base);
mandBases.emplace_back(
base, readAnnotations(annotated, file, offset, &offset));
}
n = file->read32(offset);
if (n > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
(u"UNOIDL format: too many direct optional bases of"
" interface type"_ustr));
}
offset += 4;
std::vector< AnnotatedReference > optBases;
optBases.reserve(n);
for (sal_uInt32 i = 0; i != n; ++i) {
OUString base(file->readIdxName(&offset));
checkTypeName(file, base);
optBases.emplace_back(
base, readAnnotations(annotated, file, offset, &offset));
}
sal_uInt32 nAttrs = file->read32(offset);
if (nAttrs > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
(u"UNOIDL format: too many direct attributes of interface"
" type"_ustr));
}
offset += 4;
std::vector< InterfaceTypeEntity::Attribute > attrs;
attrs.reserve(nAttrs);
for (sal_uInt32 i = 0; i != nAttrs; ++i) {
v = file->read8(offset);
++offset;
OUString attrName(file->readIdxName(&offset));
checkEntityName(file, attrName);
OUString attrType(file->readIdxName(&offset));
checkTypeName(file, attrType);
if (v > 0x03) {
throw FileFormatException(
file->uri,
("UNOIDL format: bad flags for direct attribute "
+ attrName + " of interface type"));
}
std::vector< OUString > getExcs;
sal_uInt32 m = file->read32(offset);
if (m > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
("UNOIDL format: too many getter exceptions for direct"
" attribute " + attrName + " of interface type"));
}
offset += 4;
getExcs.reserve(m);
for (sal_uInt32 j = 0; j != m; ++j) {
OUString exc(file->readIdxName(&offset));
checkTypeName(file, exc);
getExcs.push_back(exc);
}
std::vector< OUString > setExcs;
if ((v & 0x02) == 0) {
m = file->read32(offset);
if (m > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
("UNOIDL format: too many setter exceptions for"
" direct attribute " + attrName
+ " of interface type"));
}
offset += 4;
setExcs.reserve(m);
for (sal_uInt32 j = 0; j != m; ++j) {
OUString exc(file->readIdxName(&offset));
checkTypeName(file, exc);
setExcs.push_back(exc);
}
}
attrs.emplace_back(
attrName, attrType, (v & 0x01) != 0, (v & 0x02) != 0,
std::move(getExcs), std::move(setExcs),
readAnnotations(annotated, file, offset, &offset));
}
sal_uInt32 nMeths = file->read32(offset);
if (nMeths > SAL_MAX_INT32 - nAttrs) {
throw FileFormatException(
file->uri,
(u"UNOIDL format: too many direct attributes and methods of"
" interface type"_ustr));
}
offset += 4;
std::vector< InterfaceTypeEntity::Method > meths;
meths.reserve(nMeths);
for (sal_uInt32 i = 0; i != nMeths; ++i) {
OUString methName(file->readIdxName(&offset));
checkEntityName(file, methName);
OUString methType(file->readIdxName(&offset));
checkTypeName(file, methType);
sal_uInt32 m = file->read32(offset);
if (m > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
("UNOIDL format: too many parameters for method "
+ methName + " of interface type"));
}
offset += 4;
std::vector< InterfaceTypeEntity::Method::Parameter > params;
params.reserve(m);
for (sal_uInt32 j = 0; j != m; ++j) {
v = file->read8(offset);
++offset;
OUString paramName(file->readIdxName(&offset));
checkEntityName(file, paramName);
OUString paramType(file->readIdxName(&offset));
checkTypeName(file, paramType);
InterfaceTypeEntity::Method::Parameter::Direction dir;
switch (v) {
case 0:
dir = InterfaceTypeEntity::Method::Parameter::
DIRECTION_IN;
break;
case 1:
dir = InterfaceTypeEntity::Method::Parameter::
DIRECTION_OUT;
break;
case 2:
dir = InterfaceTypeEntity::Method::Parameter::
DIRECTION_IN_OUT;
break;
default:
throw FileFormatException(
file->uri,
("UNOIDL format: bad direction "
+ OUString::number(v) + " of parameter "
+ paramName + " for method " + methName
+ " of interface type"));
}
params.emplace_back(paramName, paramType, dir);
}
std::vector< OUString > excs;
m = file->read32(offset);
if (m > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
("UNOIDL format: too many exceptions for method "
+ methName + " of interface type"));
}
offset += 4;
excs.reserve(m);
for (sal_uInt32 j = 0; j != m; ++j) {
OUString exc(file->readIdxName(&offset));
checkTypeName(file, exc);
excs.push_back(exc);
}
meths.emplace_back(
methName, methType, std::move(params), std::move(excs),
readAnnotations(annotated, file, offset, &offset));
}
return new InterfaceTypeEntity(
published, std::move(mandBases), std::move(optBases), std::move(attrs), std::move(meths),
readAnnotations(annotated, file, offset));
}
case 6: // typedef
{
++offset;
OUString base(file->readIdxName(&offset));
checkTypeName(file, base);
return new TypedefEntity(
published, base, readAnnotations(annotated, file, offset));
}
case 7: // constant group
{
sal_uInt32 n = file->read32(offset + 1);
if (n > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
u"UNOIDL format: too many constants in constant group"_ustr);
}
if (sal_uInt64(offset) + 5 + 8 * sal_uInt64(n) > file->size)
// cannot overflow
{
throw FileFormatException(
file->uri,
(u"UNOIDL format: constant group map offset + size too"
" large"_ustr));
}
MapEntry const * p = reinterpret_cast< MapEntry const * >(
static_cast< char const * >(file->address) + offset + 5);
std::vector< ConstantGroupEntity::Member > mems;
mems.reserve(n);
for (sal_uInt32 i = 0; i != n; ++i) {
sal_uInt32 off = p[i].data.getUnsigned32();
bool ann;
ConstantValue val(readConstant(file, off, &off, &ann));
mems.emplace_back(
file->readNulName(p[i].name.getUnsigned32()), val,
readAnnotations(ann, file, off));
}
return new ConstantGroupEntity(
published, std::move(mems),
readAnnotations(annotated, file, offset + 5 + 8 * n));
}
case 8: // single-interface--based service without default constructor
case 8 | 0x20: // single-interface--based service with default constructor
{
++offset;
OUString base(file->readIdxName(&offset));
checkTypeName(file, base);
std::vector< SingleInterfaceBasedServiceEntity::Constructor > ctors;
if (flag) {
ctors.push_back(
SingleInterfaceBasedServiceEntity::Constructor());
} else {
sal_uInt32 n = file->read32(offset);
if (n > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
(u"UNOIDL format: too many constructors of"
" single-interface--based service"_ustr));
}
offset += 4;
ctors.reserve(n);
for (sal_uInt32 i = 0; i != n; ++i) {
OUString ctorName(file->readIdxName(&offset));
checkEntityName(file, ctorName);
sal_uInt32 m = file->read32(offset);
if (m > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
("UNOIDL format: too many parameters for"
" constructor " + ctorName
+ " of single-interface--based service"));
}
offset += 4;
std::vector<
SingleInterfaceBasedServiceEntity::Constructor::
Parameter > params;
params.reserve(m);
for (sal_uInt32 j = 0; j != m; ++j) {
v = file->read8(offset);
++offset;
OUString paramName(file->readIdxName(&offset));
checkEntityName(file, paramName);
OUString paramType(file->readIdxName(&offset));
checkTypeName(file, paramType);
bool rest;
switch (v) {
case 0:
rest = false;
break;
case 0x04:
rest = true;
break;
default:
throw FileFormatException(
file->uri,
("UNOIDL format: bad mode "
+ OUString::number(v) + " of parameter "
+ paramName + " for constructor " + ctorName
+ " of single-interface--based service"));
}
params.emplace_back(paramName, paramType, rest);
}
std::vector< OUString > excs;
m = file->read32(offset);
if (m > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
("UNOIDL format: too many exceptions for"
" constructor " + ctorName
+ " of single-interface--based service"));
}
offset += 4;
excs.reserve(m);
for (sal_uInt32 j = 0; j != m; ++j) {
OUString exc(file->readIdxName(&offset));
checkTypeName(file, exc);
excs.push_back(exc);
}
ctors.push_back(
SingleInterfaceBasedServiceEntity::Constructor(
ctorName, std::move(params), std::move(excs),
readAnnotations(annotated, file, offset, &offset)));
}
}
return new SingleInterfaceBasedServiceEntity(
published, base, std::move(ctors),
readAnnotations(annotated, file, offset));
}
case 9: // accumulation-based service
{
sal_uInt32 n = file->read32(offset + 1);
if (n > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
(u"UNOIDL format: too many direct mandatory service bases of"
" accumulation-based service"_ustr));
}
offset += 5;
std::vector< AnnotatedReference > mandServs;
mandServs.reserve(n);
for (sal_uInt32 i = 0; i != n; ++i) {
OUString base(file->readIdxName(&offset));
checkTypeName(file, base);
mandServs.emplace_back(
base, readAnnotations(annotated, file, offset, &offset));
}
n = file->read32(offset);
if (n > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
(u"UNOIDL format: too many direct optional service bases of"
" accumulation-based service"_ustr));
}
offset += 4;
std::vector< AnnotatedReference > optServs;
optServs.reserve(n);
for (sal_uInt32 i = 0; i != n; ++i) {
OUString base(file->readIdxName(&offset));
checkTypeName(file, base);
optServs.emplace_back(
base, readAnnotations(annotated, file, offset, &offset));
}
n = file->read32(offset);
if (n > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
(u"UNOIDL format: too many direct mandatory interface bases"
" of accumulation-based service"_ustr));
}
offset += 4;
std::vector< AnnotatedReference > mandIfcs;
mandIfcs.reserve(n);
for (sal_uInt32 i = 0; i != n; ++i) {
OUString base(file->readIdxName(&offset));
checkTypeName(file, base);
mandIfcs.emplace_back(
base, readAnnotations(annotated, file, offset, &offset));
}
n = file->read32(offset);
if (n > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
(u"UNOIDL format: too many direct optional interface bases"
" of accumulation-based service"_ustr));
}
offset += 4;
std::vector< AnnotatedReference > optIfcs;
optIfcs.reserve(n);
for (sal_uInt32 i = 0; i != n; ++i) {
OUString base(file->readIdxName(&offset));
checkTypeName(file, base);
optIfcs.emplace_back(
base, readAnnotations(annotated, file, offset, &offset));
}
n = file->read32(offset);
if (n > SAL_MAX_INT32) {
throw FileFormatException(
file->uri,
(u"UNOIDL format: too many direct properties of"
" accumulation-based service"_ustr));
}
offset += 4;
std::vector< AccumulationBasedServiceEntity::Property > props;
props.reserve(n);
for (sal_uInt32 i = 0; i != n; ++i) {
sal_uInt16 attrs = file->read16(offset);
offset += 2;
OUString propName(file->readIdxName(&offset));
checkEntityName(file, propName);
OUString propType(file->readIdxName(&offset));
checkTypeName(file, propType);
if (attrs > 0x01FF) { // see css.beans.PropertyAttribute
throw FileFormatException(
file->uri,
("UNOIDL format: bad mode " + OUString::number(v)
+ " of property " + propName
+ " for accumulation-based service"));
}
props.emplace_back(
propName, propType,
static_cast<
AccumulationBasedServiceEntity::Property::Attributes >(
attrs),
readAnnotations(annotated, file, offset, &offset));
}
return new AccumulationBasedServiceEntity(
published, std::move(mandServs), std::move(optServs), std::move(mandIfcs), std::move(optIfcs), std::move(props),
readAnnotations(annotated, file, offset));
}
case 10: // interface-based singleton
{
++offset;
OUString base(file->readIdxName(&offset));
checkTypeName(file, base);
return new InterfaceBasedSingletonEntity(
published, base, readAnnotations(annotated, file, offset));
}
case 11: // service-based singleton
{
++offset;
OUString base(file->readIdxName(&offset));
checkTypeName(file, base);
return new ServiceBasedSingletonEntity(
published, base, readAnnotations(annotated, file, offset));
}
default:
throw FileFormatException(
file->uri, "UNOIDL format: bad type byte " + OUString::number(v));
}
}
}
UnoidlProvider::UnoidlProvider(OUString const & uri): file_(new MappedFile(uri))
{
if (file_->size < 8 || std::memcmp(file_->address, "UNOIDL\xFF\0", 8) != 0)
{
throw FileFormatException(
file_->uri,
u"UNOIDL format: does not begin with magic UNOIDL\\xFF and version"
" 0"_ustr);
}
sal_uInt32 off = file_->read32(8);
map_.map.size = file_->read32(12);
if (off + 8 * sal_uInt64(map_.map.size) > file_->size) { // cannot overflow
throw FileFormatException(
file_->uri, u"UNOIDL format: root map offset + size too large"_ustr);
}
map_.map.begin = reinterpret_cast< MapEntry const * >(
static_cast< char const * >(file_->address) + off);
map_.trace.insert(map_.map);
}
rtl::Reference< MapCursor > UnoidlProvider::createRootCursor() const {
return new UnoidlCursor(
file_, const_cast<UnoidlProvider *>(this),
rtl::Reference<UnoidlModuleEntity>(), map_);
}
rtl::Reference< Entity > UnoidlProvider::findEntity(OUString const & name) const
{
NestedMap map(map_);
bool cgroup = false;
for (sal_Int32 i = 0;;) {
sal_Int32 j = name.indexOf('.', i);
if (j == -1) {
j = name.getLength();
}
sal_Int32 off = findInMap(
file_, map.map.begin, map.map.size, name, i, j - i);
if (off == 0) {
return rtl::Reference< Entity >();
}
if (j == name.getLength()) {
return cgroup
? rtl::Reference< Entity >()
: readEntity(file_, off, std::set(map.trace));
}
if (cgroup) {
return rtl::Reference< Entity >();
//TODO: throw an exception instead here, where the segments of a
// constant's name are a prefix of the requested name's
// segments?
}
int v = file_->read8(off);
if (v != 0) { // module
if ((v & 0x3F) == 7) { // constant group
cgroup = true;
} else {
return rtl::Reference< Entity >();
//TODO: throw an exception instead here, where the segments
// of a non-module, non-constant-group entity's name are a
// prefix of the requested name's segments?
}
}
map.map.size = file_->read32(off + 1);
if (sal_uInt64(off) + 5 + 8 * sal_uInt64(map.map.size) > file_->size)
// cannot overflow
{
throw FileFormatException(
file_->uri, u"UNOIDL format: map offset + size too large"_ustr);
}
map.map.begin = reinterpret_cast< MapEntry const * >(
static_cast< char const * >(file_->address) + off + 5);
if (!map.trace.insert(map.map).second) {
throw FileFormatException(
file_->uri, u"UNOIDL format: recursive map"_ustr);
}
i = j + 1;
}
}
UnoidlProvider::~UnoidlProvider() noexcept {}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V1077 The 'MappedFile' constructor contains potentially uninitialized members. Inspect the following: address.