diff options
Diffstat (limited to 'unoidl/source/unoidlprovider.cxx')
-rw-r--r-- | unoidl/source/unoidlprovider.cxx | 1431 |
1 files changed, 1431 insertions, 0 deletions
diff --git a/unoidl/source/unoidlprovider.cxx b/unoidl/source/unoidlprovider.cxx new file mode 100644 index 000000000..d6f4d4ddd --- /dev/null +++ b/unoidl/source/unoidlprovider.cxx @@ -0,0 +1,1431 @@ +/* -*- 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, "UNOIDL format: offset for 8-bit value too large"); + } + return get8(offset); +} + +sal_uInt16 MappedFile::read16(sal_uInt32 offset) const { + assert(size >= 8); + if (offset > size - 2) { + throw FileFormatException( + uri, "UNOIDL format: offset for 16-bit value too large"); + } + return get16(offset); +} + +sal_uInt32 MappedFile::read32(sal_uInt32 offset) const { + assert(size >= 8); + if (offset > size - 4) { + throw FileFormatException( + uri, "UNOIDL format: offset for 32-bit value too large"); + } + return get32(offset); +} + +sal_uInt64 MappedFile::read64(sal_uInt32 offset) const { + assert(size >= 8); + if (offset > size - 8) { + throw FileFormatException( + uri, "UNOIDL format: offset for 64-bit value too large"); + } + return get64(offset); +} + +float MappedFile::readIso60599Binary32(sal_uInt32 offset) const { + assert(size >= 8); + if (offset > size - 4) { + throw FileFormatException( + uri, "UNOIDL format: offset for 32-bit value too large"); + } + return getIso60599Binary32(offset); +} + +double MappedFile::readIso60599Binary64(sal_uInt32 offset) const { + assert(size >= 8); + if (offset > size - 8) { + throw FileFormatException( + uri, "UNOIDL format: offset for 64-bit value too large"); + } + return getIso60599Binary64(offset); +} + +OUString MappedFile::readNulName(sal_uInt32 offset) { + if (offset > size) { + throw FileFormatException( + uri, "UNOIDL format: offset for string too large"); + } + sal_uInt64 end = offset; + for (;; ++end) { + if (end == size) { + throw FileFormatException( + uri, "UNOIDL format: string misses trailing NUL"); + } + if (static_cast< char const * >(address)[end] == 0) { + break; + } + } + if (end - offset > SAL_MAX_INT32) { + throw FileFormatException(uri, "UNOIDL format: string too long"); + } + 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, "UNOIDL format: name is not ASCII"); + } + 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, "UNOIDL format: string length high bit set"); + } + } + if (len > SAL_MAX_INT32 || len > size - off - 4) { + throw FileFormatException( + uri, "UNOIDL format: size of string is too large"); + } + 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, "UNOIDL format: string bytes do not match encoding"); + } + 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, "UNOIDL format: string offset too large"); + } + 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, "UNOIDL format: string misses trailing NUL"); + } + 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, "UNOIDL format: map entry data offset is null"); + } + return off; +} + +#if defined(__COVERITY__) +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_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, "UNOIDL format: recursive map"); + } + } + +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, "UNOIDL format: too many items in module"); + } + if (sal_uInt64(offset) + 5 + 8 * sal_uInt64(n) > file->size) + // cannot overflow + { + throw FileFormatException( + file->uri, + "UNOIDL format: module map offset + size too large"); + } + 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, "UNOIDL format: enum type with no members"); + } + if (n > SAL_MAX_INT32) { + throw FileFormatException( + file->uri, "UNOIDL format: too many members of enum type"); + } + 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, + ("UNOIDL format: empty base type name of plain struct" + " type")); + } + checkTypeName(file, base); + } + sal_uInt32 n = file->read32(offset); + if (n > SAL_MAX_INT32) { + throw FileFormatException( + file->uri, + ("UNOIDL format: too many direct members of plain struct" + " type")); + } + 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, + ("UNOIDL format: too many type parameters of polymorphic" + " struct type template")); + } + 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, + ("UNOIDL format: too many members of polymorphic struct" + " type template")); + } + 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, + ("UNOIDL format: empty base type name of exception" + " type")); + } + checkTypeName(file, base); + } + sal_uInt32 n = file->read32(offset); + if (n > SAL_MAX_INT32) { + throw FileFormatException( + file->uri, + "UNOIDL format: too many direct members of exception type"); + } + 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, + ("UNOIDL format: too many direct mandatory bases of" + " interface type")); + } + 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, + ("UNOIDL format: too many direct optional bases of" + " interface type")); + } + 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, + ("UNOIDL format: too many direct attributes of interface" + " type")); + } + 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, + ("UNOIDL format: too many direct attributes and methods of" + " interface type")); + } + 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, + "UNOIDL format: too many constants in constant group"); + } + if (sal_uInt64(offset) + 5 + 8 * sal_uInt64(n) > file->size) + // cannot overflow + { + throw FileFormatException( + file->uri, + ("UNOIDL format: constant group map offset + size too" + " large")); + } + 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, + ("UNOIDL format: too many constructors of" + " single-interface--based service")); + } + 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, + ("UNOIDL format: too many direct mandatory service bases of" + " accumulation-based service")); + } + 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, + ("UNOIDL format: too many direct optional service bases of" + " accumulation-based service")); + } + 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, + ("UNOIDL format: too many direct mandatory interface bases" + " of accumulation-based service")); + } + 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, + ("UNOIDL format: too many direct optional interface bases" + " of accumulation-based service")); + } + 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, + ("UNOIDL format: too many direct properties of" + " accumulation-based service")); + } + 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, + "UNOIDL format: does not begin with magic UNOIDL\\xFF and version" + " 0"); + } + 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, "UNOIDL format: root map offset + size too large"); + } + 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, "UNOIDL format: map offset + size too large"); + } + 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, "UNOIDL format: recursive map"); + } + i = j + 1; + } +} + +UnoidlProvider::~UnoidlProvider() noexcept {} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |