/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 // // ::= | // ::= * ("_" )* // ::= + // ::= | "a"--"z" | "0"--"9" // ::= "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__) 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 && trace); class UnoidlModuleEntity; class UnoidlCursor: public MapCursor { public: UnoidlCursor( rtl::Reference< MappedFile > file, rtl::Reference reference1, rtl::Reference 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 reference1_; // HACK to keep alive whatever rtl::Reference 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 && trace): file_(file) { assert(file.is()); map_.map.begin = reinterpret_cast( static_cast(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(), const_cast(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 && 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(this), rtl::Reference(), 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: */