Skip to content

Commit 45ebd3b

Browse files
committed
fix: Exclude circular dependant types when some subtype is not supported
* Reset cached metadata when storing it in the meta container in order to revalidate it * Store the complete exception instead of only the message (`isError` was being lost) * Throw cached exceptions polymorphically (the throw statement uses the static type of the exception) * fix unresolved bridge interface exception's description to not use the null pointer `Tcl_HashTable` from the Catalyst SDK had this issue: ``` typedef struct Tcl_HashTable Tcl_HashTable; ... struct Tcl_HashEntry { ... Tcl_HashTable *tablePtr; /* Pointer to table containing entry. */ ... } ... struct Tcl_HashTable { ... Tcl_HashEntry **buckets; ... } ``` While defining `Tcl_HashEntry` we need to define `Tcl_HashTable`. While defining it we reach back to `Tcl_HashEntry` and use it's partially defined cached entry (which is currently considered valid as we still haven't reached the unsupported `union` field). This results in caching and emitting the `Tcl_HashTable`'s metadata. However, this is wrong since `Tcl_HashEntry` ultimately turns out to be invalid and is missing from the metadata.
1 parent 4c7ee01 commit 45ebd3b

File tree

8 files changed

+129
-82
lines changed

8 files changed

+129
-82
lines changed

src/Binary/binaryTypeEncodingSerializer.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ unique_ptr<binary::TypeEncoding> binary::BinaryTypeEncodingSerializer::visitBrid
169169
return this->visitId(::Meta::IdType());
170170
}
171171
if (type.bridgedInterface == nullptr) {
172-
throw logic_error(std::string("Unresolved bridged interface for BridgedInterfaceType with name '") + type.bridgedInterface->name + "'.");
172+
throw logic_error(std::string("Unresolved bridged interface for BridgedInterfaceType with name '") + type.name + "'.");
173173
}
174174
auto s = new binary::InterfaceDeclarationReferenceEncoding();
175175
s->_name = this->_heapWriter.push_string(type.bridgedInterface->jsName);

src/Meta/CreationException.h

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,19 @@
77
#include <clang/AST/Type.h>
88
#include <string>
99

10+
11+
#define DEFINE_POLYMORPHIC_THROW \
12+
virtual void polymorhicThrow() override { \
13+
throw *this; \
14+
}
15+
16+
17+
#define POLYMORPHIC_THROW(ex) do \
18+
{ \
19+
ex->polymorhicThrow(); \
20+
throw std::logic_error("polymorphicThrow should never return"); \
21+
} while(false)
22+
1023
namespace Meta {
1124
class CreationException {
1225
public:
@@ -20,7 +33,11 @@ class CreationException {
2033
, _isError(isError)
2134
{
2235
}
36+
37+
virtual ~CreationException() { }
2338

39+
virtual void polymorhicThrow() = 0;
40+
2441
std::string getMessage() const
2542
{
2643
return _message;
@@ -49,7 +66,7 @@ class MetaCreationException : public CreationException {
4966
{
5067
}
5168

52-
std::string getDetailedMessage() const
69+
std::string getDetailedMessage() const override
5370
{
5471
return _meta->identificationString() + " : " + this->getMessage();
5572
}
@@ -59,6 +76,8 @@ class MetaCreationException : public CreationException {
5976
return _meta;
6077
}
6178

79+
DEFINE_POLYMORPHIC_THROW;
80+
6281
private:
6382
const Meta* _meta;
6483
};
@@ -71,7 +90,7 @@ class TypeCreationException : public CreationException {
7190
{
7291
}
7392

74-
std::string getDetailedMessage() const
93+
std::string getDetailedMessage() const override
7594
{
7695
return std::string("[Type ") + (_type == nullptr ? "" : _type->getTypeClassName()) + "] : " + this->getMessage();
7796
}
@@ -81,7 +100,9 @@ class TypeCreationException : public CreationException {
81100
return _type;
82101
}
83102

103+
DEFINE_POLYMORPHIC_THROW;
104+
84105
private:
85106
const clang::Type* _type;
86107
};
87-
}
108+
}

src/Meta/DeclarationConverterVisitor.h

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,35 @@ class DeclarationConverterVisitor : public clang::RecursiveASTVisitor<Declaratio
5252
bool Visit(T* decl)
5353
{
5454
try {
55-
Meta* meta = this->_metaFactory.create(*decl);
55+
// Remove from cache if present to have a chance to process any errors
56+
// in dependant types which have been pending when it was cached the 1st time.
57+
// If we have the following (inspired from Tcl_HashTable):
58+
// struct HashTable;
59+
//
60+
// struct HashEntry {
61+
// HashTable*table;
62+
// union {
63+
// }
64+
// }
65+
//
66+
// struct HashTable {
67+
// HashEntry **entries;
68+
// }
69+
// We do not support unions, so HashEntry is not included in the metadata.
70+
// But before the cache purge we were leaving HashTable and it caused crashes
71+
// if accessed at runtime.
72+
73+
Meta* meta = this->_metaFactory.create(*decl, /*resetCached*/ true);
5674
_metaContainer.push_back(meta);
5775
log(std::stringstream() << "verbose: Included " << meta->jsName << " from " << meta->module->getFullModuleName());
5876
} catch (MetaCreationException& e) {
59-
if(e.isError()) {
77+
if (e.isError()) {
6078
log(std::stringstream() << "verbose: Exception " << e.getDetailedMessage());
79+
} else {
80+
// Uncomment for maximum verbosity when debugging metadata generation issues
81+
// auto namedDecl = clang::dyn_cast<clang::NamedDecl>(decl);
82+
// auto name = namedDecl ? namedDecl->getNameAsString() : "<unknown>";
83+
// log(std::stringstream() << "verbose: Skipping " << name << ": " << e.getMessage());
6184
}
6285
}
6386
return true;

src/Meta/MetaEntities.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,6 @@ enum MetaType {
9999
};
100100

101101
class Meta {
102-
MAKE_NONCOPYABLE(Meta);
103-
104102
public:
105103
MetaType type = MetaType::Undefined;
106104
MetaFlags flags = MetaFlags::None;

src/Meta/MetaFactory.cpp

Lines changed: 51 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ static bool metasComparerByJsName(Meta* meta1, Meta* meta2)
2424
return compareJsNames(meta1->jsName, meta2->jsName);
2525
}
2626

27-
2827
void MetaFactory::validate(Type* type)
2928
{
3029
ValidateMetaTypeVisitor validator(*this);
@@ -38,12 +37,12 @@ void MetaFactory::validate(Meta* meta)
3837
if (declIt == this->_metaToDecl.end()) {
3938
throw MetaCreationException(meta, "Metadata not created", true);
4039
}
41-
40+
4241
auto metaIt = this->_cache.find(declIt->second);
4342
assert(metaIt != this->_cache.end());
44-
if (!metaIt->second.second.empty()) {
43+
if (metaIt->second.second.get() != nullptr) {
4544
// printf("**** Validation failed for %s: %s ***\n\n", meta->name.c_str(), metaIt->second.second.c_str());
46-
throw MetaCreationException(meta, metaIt->second.second, false);
45+
POLYMORPHIC_THROW(metaIt->second.second);
4746
}
4847
}
4948

@@ -68,85 +67,89 @@ string MetaFactory::getTypedefOrOwnName(const clang::TagDecl* tagDecl)
6867
return tagDecl->getNameAsString();
6968
}
7069

71-
Meta* MetaFactory::create(const clang::Decl& decl)
70+
template<class T>
71+
void resetMetaAndAddToMap(std::unique_ptr<Meta>& metaPtrRef, MetaToDeclMap& metaToDecl, const clang::Decl& decl) {
72+
if (metaPtrRef.get()) {
73+
// The pointer has been previously allocated. Reset it's value and assert that it's already present in the map
74+
static_cast<T&>(*metaPtrRef) = T();
75+
assert(metaToDecl[metaPtrRef.get()] == &decl);
76+
} else {
77+
// Allocate memory and add to map
78+
metaPtrRef.reset(new T());
79+
metaToDecl[metaPtrRef.get()] = &decl;
80+
}
81+
}
82+
83+
Meta* MetaFactory::create(const clang::Decl& decl, bool resetCached /* = false*/)
7284
{
73-
// check for cached Meta
74-
Cache::const_iterator cachedMetaIt = _cache.find(&decl);
75-
if (cachedMetaIt != _cache.end()) {
85+
// Check for cached Meta
86+
Cache::iterator cachedMetaIt = _cache.find(&decl);
87+
if (!resetCached && cachedMetaIt != _cache.end()) {
7688
Meta* meta = cachedMetaIt->second.first.get();
77-
std::string errorMessage = cachedMetaIt->second.second;
78-
if (errorMessage.empty()) {
79-
/* TODO: The meta object is not guaranteed to be fully initialized. If the meta object is in the creation stack
80-
* it will appear in cache, but will not be fully initialized. This may cause some inconsistent results.
81-
* */
82-
return meta;
89+
if (auto creationException = cachedMetaIt->second.second.get()) {
90+
POLYMORPHIC_THROW(creationException);
8391
}
84-
throw MetaCreationException(meta, errorMessage, false);
85-
}
8692

87-
std::pair<Cache::iterator, bool> insertionResult = _cache.insert(std::make_pair<Cache::key_type, Cache::mapped_type>(&decl, std::make_pair<std::unique_ptr<Meta>, std::string>(nullptr, std::string())));
93+
/* TODO: The meta object is not guaranteed to be fully initialized. If the meta object is in the creation stack
94+
* it will appear in cache, but will not be fully initialized. This may cause some inconsistent results.
95+
* */
96+
97+
return meta;
98+
}
8899

89-
assert(insertionResult.second);
90-
Cache::iterator insertionIt = insertionResult.first;
91-
std::unique_ptr<Meta>& insertedMetaPtrRef = insertionIt->second.first;
92-
std::string& insertedErrorMessageRef = insertionIt->second.second;
100+
if (cachedMetaIt == _cache.end()) {
101+
std::pair<Cache::iterator, bool> insertionResult = _cache.insert(std::make_pair(&decl, std::make_pair(nullptr, nullptr)));
102+
assert(insertionResult.second);
103+
cachedMetaIt = insertionResult.first;
104+
}
105+
std::unique_ptr<Meta>& insertedMetaPtrRef = cachedMetaIt->second.first;
106+
std::unique_ptr<CreationException>& insertedException = cachedMetaIt->second.second;
93107

94108
try {
95109
if (const clang::FunctionDecl* function = clang::dyn_cast<clang::FunctionDecl>(&decl)) {
96-
insertedMetaPtrRef.reset(new FunctionMeta());
97-
_metaToDecl[insertedMetaPtrRef.get()] = &decl;
110+
resetMetaAndAddToMap<FunctionMeta>(insertedMetaPtrRef, this->_metaToDecl, decl);
98111
populateIdentificationFields(*function, *insertedMetaPtrRef.get());
99112
createFromFunction(*function, insertedMetaPtrRef.get()->as<FunctionMeta>());
100113
} else if (const clang::RecordDecl* record = clang::dyn_cast<clang::RecordDecl>(&decl)) {
101114
if (record->isStruct()) {
102-
insertedMetaPtrRef.reset(new StructMeta());
103-
_metaToDecl[insertedMetaPtrRef.get()] = &decl;
115+
resetMetaAndAddToMap<StructMeta>(insertedMetaPtrRef, this->_metaToDecl, decl);
104116
populateIdentificationFields(*record, *insertedMetaPtrRef.get());
105117
createFromStruct(*record, insertedMetaPtrRef.get()->as<StructMeta>());
106118
} else {
107-
insertedMetaPtrRef.reset(new UnionMeta());
108-
_metaToDecl[insertedMetaPtrRef.get()] = &decl;
119+
resetMetaAndAddToMap<UnionMeta>(insertedMetaPtrRef, this->_metaToDecl, decl);
109120
populateIdentificationFields(*record, *insertedMetaPtrRef.get());
110121
throw MetaCreationException(insertedMetaPtrRef.get(), "The record is union.", false);
111122
}
112123
} else if (const clang::VarDecl* var = clang::dyn_cast<clang::VarDecl>(&decl)) {
113-
insertedMetaPtrRef.reset(new VarMeta());
114-
_metaToDecl[insertedMetaPtrRef.get()] = &decl;
124+
resetMetaAndAddToMap<VarMeta>(insertedMetaPtrRef, this->_metaToDecl, decl);
115125
populateIdentificationFields(*var, *insertedMetaPtrRef.get());
116126
createFromVar(*var, insertedMetaPtrRef.get()->as<VarMeta>());
117127
} else if (const clang::EnumDecl* enumDecl = clang::dyn_cast<clang::EnumDecl>(&decl)) {
118-
insertedMetaPtrRef.reset(new EnumMeta());
119-
_metaToDecl[insertedMetaPtrRef.get()] = &decl;
128+
resetMetaAndAddToMap<EnumMeta>(insertedMetaPtrRef, this->_metaToDecl, decl);
120129
populateIdentificationFields(*enumDecl, *insertedMetaPtrRef.get());
121130
createFromEnum(*enumDecl, insertedMetaPtrRef.get()->as<EnumMeta>());
122131
} else if (const clang::EnumConstantDecl* enumConstantDecl = clang::dyn_cast<clang::EnumConstantDecl>(&decl)) {
123-
insertedMetaPtrRef.reset(new EnumConstantMeta());
124-
_metaToDecl[insertedMetaPtrRef.get()] = &decl;
132+
resetMetaAndAddToMap<EnumConstantMeta>(insertedMetaPtrRef, this->_metaToDecl, decl);
125133
populateIdentificationFields(*enumConstantDecl, *insertedMetaPtrRef.get());
126134
createFromEnumConstant(*enumConstantDecl, insertedMetaPtrRef.get()->as<EnumConstantMeta>());
127135
} else if (const clang::ObjCInterfaceDecl* interface = clang::dyn_cast<clang::ObjCInterfaceDecl>(&decl)) {
128-
insertedMetaPtrRef.reset(new InterfaceMeta());
129-
_metaToDecl[insertedMetaPtrRef.get()] = &decl;
136+
resetMetaAndAddToMap<InterfaceMeta>(insertedMetaPtrRef, this->_metaToDecl, decl);
130137
populateIdentificationFields(*interface, *insertedMetaPtrRef.get());
131138
createFromInterface(*interface, insertedMetaPtrRef.get()->as<InterfaceMeta>());
132139
} else if (const clang::ObjCProtocolDecl* protocol = clang::dyn_cast<clang::ObjCProtocolDecl>(&decl)) {
133-
insertedMetaPtrRef.reset(new ProtocolMeta());
134-
_metaToDecl[insertedMetaPtrRef.get()] = &decl;
140+
resetMetaAndAddToMap<ProtocolMeta>(insertedMetaPtrRef, this->_metaToDecl, decl);
135141
populateIdentificationFields(*protocol, *insertedMetaPtrRef.get());
136142
createFromProtocol(*protocol, insertedMetaPtrRef.get()->as<ProtocolMeta>());
137143
} else if (const clang::ObjCCategoryDecl* category = clang::dyn_cast<clang::ObjCCategoryDecl>(&decl)) {
138-
insertedMetaPtrRef.reset(new CategoryMeta());
139-
_metaToDecl[insertedMetaPtrRef.get()] = &decl;
144+
resetMetaAndAddToMap<CategoryMeta>(insertedMetaPtrRef, this->_metaToDecl, decl);
140145
populateIdentificationFields(*category, *insertedMetaPtrRef.get());
141146
createFromCategory(*category, insertedMetaPtrRef.get()->as<CategoryMeta>());
142147
} else if (const clang::ObjCMethodDecl* method = clang::dyn_cast<clang::ObjCMethodDecl>(&decl)) {
143-
insertedMetaPtrRef.reset(new MethodMeta());
144-
_metaToDecl[insertedMetaPtrRef.get()] = &decl;
148+
resetMetaAndAddToMap<MethodMeta>(insertedMetaPtrRef, this->_metaToDecl, decl);
145149
populateIdentificationFields(*method, *insertedMetaPtrRef.get());
146150
createFromMethod(*method, insertedMetaPtrRef.get()->as<MethodMeta>());
147151
} else if (const clang::ObjCPropertyDecl* property = clang::dyn_cast<clang::ObjCPropertyDecl>(&decl)) {
148-
insertedMetaPtrRef.reset(new PropertyMeta());
149-
_metaToDecl[insertedMetaPtrRef.get()] = &decl;
152+
resetMetaAndAddToMap<PropertyMeta>(insertedMetaPtrRef, this->_metaToDecl, decl);
150153
populateIdentificationFields(*property, *insertedMetaPtrRef.get());
151154
createFromProperty(*property, insertedMetaPtrRef.get()->as<PropertyMeta>());
152155
} else {
@@ -156,16 +159,16 @@ Meta* MetaFactory::create(const clang::Decl& decl)
156159
return insertedMetaPtrRef.get();
157160
} catch (MetaCreationException& e) {
158161
if (e.getMeta() == insertedMetaPtrRef.get()) {
159-
insertedErrorMessageRef = e.getMessage();
162+
insertedException = llvm::make_unique<MetaCreationException>(e);
160163
throw;
161164
}
162165
std::string message = CreationException::constructMessage("Can't create meta dependency.", e.getDetailedMessage());
163-
insertedErrorMessageRef = message;
164-
throw MetaCreationException(insertedMetaPtrRef.get(), message, e.isError());
166+
insertedException = llvm::make_unique<MetaCreationException>(insertedMetaPtrRef.get(), message, e.isError());
167+
POLYMORPHIC_THROW(insertedException);
165168
} catch (TypeCreationException& e) {
166169
std::string message = CreationException::constructMessage("Can't create type dependency.", e.getDetailedMessage());
167-
insertedErrorMessageRef = message;
168-
throw MetaCreationException(insertedMetaPtrRef.get(), message, e.isError());
170+
insertedException = llvm::make_unique<MetaCreationException>(insertedMetaPtrRef.get(), message, e.isError());
171+
POLYMORPHIC_THROW(insertedException);
169172
}
170173
}
171174

src/Meta/MetaFactory.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#pragma once
22

3+
#include "CreationException.h"
34
#include "MetaEntities.h"
45
#include "TypeFactory.h"
56
#include "Utils/Noncopyable.h"
@@ -10,7 +11,7 @@
1011

1112
namespace Meta {
1213

13-
typedef std::unordered_map<const clang::Decl*, std::pair<std::unique_ptr<Meta>, std::string> > Cache;
14+
typedef std::unordered_map<const clang::Decl*, std::pair<std::unique_ptr<Meta>, std::unique_ptr<CreationException>> > Cache;
1415
typedef std::unordered_map<const Meta*, const clang::Decl*> MetaToDeclMap;
1516

1617
class MetaFactory {
@@ -22,7 +23,7 @@ class MetaFactory {
2223
{
2324
}
2425

25-
Meta* create(const clang::Decl& decl);
26+
Meta* create(const clang::Decl& decl, bool resetCached = false);
2627

2728
bool tryCreate(const clang::Decl& decl, Meta** meta);
2829

0 commit comments

Comments
 (0)