1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "tools/cddl/sema.h"
6 
7 #include <string.h>
8 #include <unistd.h>
9 
10 #include <cinttypes>
11 #include <cstdlib>
12 #include <iostream>
13 #include <map>
14 #include <memory>
15 #include <string>
16 #include <unordered_set>
17 #include <vector>
18 
19 #include "absl/algorithm/container.h"
20 #include "absl/strings/numbers.h"
21 #include "absl/strings/string_view.h"
22 #include "absl/types/optional.h"
23 #include "tools/cddl/logging.h"
24 
TypesWithId()25 std::vector<CppType*> CppSymbolTable::TypesWithId() {
26   if (!this->TypesWithId_.size()) {
27     for (const std::unique_ptr<CppType>& ptr : this->cpp_types) {
28       if (ptr->type_key == absl::nullopt) {
29         continue;
30       }
31       this->TypesWithId_.emplace_back(ptr.get());
32     }
33   }
34   return this->TypesWithId_;
35 }
36 
CddlType()37 CddlType::CddlType()
38     : map(nullptr), op(CddlType::Op::kNone), constraint_type(nullptr) {}
~CddlType()39 CddlType::~CddlType() {
40   switch (which) {
41     case CddlType::Which::kDirectChoice:
42       direct_choice.std::vector<CddlType*>::~vector();
43       break;
44     case CddlType::Which::kValue:
45       value.std::string::~basic_string();
46       break;
47     case CddlType::Which::kId:
48       id.std::string::~basic_string();
49       break;
50     case CddlType::Which::kMap:
51       break;
52     case CddlType::Which::kArray:
53       break;
54     case CddlType::Which::kGroupChoice:
55       break;
56     case CddlType::Which::kGroupnameChoice:
57       break;
58     case CddlType::Which::kTaggedType:
59       tagged_type.~TaggedType();
60       break;
61   }
62 }
63 
Entry()64 CddlGroup::Entry::Entry() : group(nullptr) {}
~Entry()65 CddlGroup::Entry::~Entry() {
66   switch (which) {
67     case CddlGroup::Entry::Which::kUninitialized:
68       break;
69     case CddlGroup::Entry::Which::kType:
70       type.~EntryType();
71       break;
72     case CddlGroup::Entry::Which::kGroup:
73       break;
74   }
75 }
76 
CppType()77 CppType::CppType() : vector_type() {}
~CppType()78 CppType::~CppType() {
79   switch (which) {
80     case CppType::Which::kUninitialized:
81       break;
82     case CppType::Which::kUint64:
83       break;
84     case CppType::Which::kString:
85       break;
86     case CppType::Which::kBytes:
87       break;
88     case CppType::Which::kVector:
89       break;
90     case CppType::Which::kEnum:
91       enum_type.~Enum();
92       break;
93     case CppType::Which::kStruct:
94       struct_type.~Struct();
95       break;
96     case CppType::Which::kOptional:
97       break;
98     case CppType::Which::kDiscriminatedUnion:
99       discriminated_union.~DiscriminatedUnion();
100       break;
101     case CppType::Which::kTaggedType:
102       break;
103   }
104 }
105 
InitVector()106 void CppType::InitVector() {
107   which = Which::kVector;
108   new (&vector_type) Vector();
109 }
110 
InitEnum()111 void CppType::InitEnum() {
112   which = Which::kEnum;
113   new (&enum_type) Enum();
114 }
115 
InitStruct()116 void CppType::InitStruct() {
117   which = Which::kStruct;
118   new (&struct_type) Struct();
119 }
120 
InitDiscriminatedUnion()121 void CppType::InitDiscriminatedUnion() {
122   which = Which::kDiscriminatedUnion;
123   new (&discriminated_union) DiscriminatedUnion();
124 }
125 
InitBytes()126 void CppType::InitBytes() {
127   which = Which::kBytes;
128 }
129 
InitString(std::string * s,absl::string_view value)130 void InitString(std::string* s, absl::string_view value) {
131   new (s) std::string(value);
132 }
133 
InitDirectChoice(std::vector<CddlType * > * direct_choice)134 void InitDirectChoice(std::vector<CddlType*>* direct_choice) {
135   new (direct_choice) std::vector<CddlType*>();
136 }
137 
InitGroupEntry(CddlGroup::Entry::EntryType * entry)138 void InitGroupEntry(CddlGroup::Entry::EntryType* entry) {
139   new (entry) CddlGroup::Entry::EntryType();
140 }
141 
AddCddlType(CddlSymbolTable * table,CddlType::Which which)142 CddlType* AddCddlType(CddlSymbolTable* table, CddlType::Which which) {
143   table->types.emplace_back(new CddlType);
144   CddlType* value = table->types.back().get();
145   value->which = which;
146   return value;
147 }
148 
149 CddlType* AnalyzeType(CddlSymbolTable* table, const AstNode& type);
150 CddlGroup* AnalyzeGroup(CddlSymbolTable* table, const AstNode& group);
151 
AnalyzeType2(CddlSymbolTable * table,const AstNode & type2)152 CddlType* AnalyzeType2(CddlSymbolTable* table, const AstNode& type2) {
153   const AstNode* node = type2.children;
154   if (node->type == AstNode::Type::kNumber ||
155       node->type == AstNode::Type::kText ||
156       node->type == AstNode::Type::kBytes) {
157     CddlType* value = AddCddlType(table, CddlType::Which::kValue);
158     InitString(&value->value, node->text);
159     return value;
160   } else if (node->type == AstNode::Type::kTypename) {
161     if (type2.text[0] == '~') {
162       dprintf(STDERR_FILENO, "We don't support the '~' operator.\n");
163       return nullptr;
164     }
165     CddlType* id = AddCddlType(table, CddlType::Which::kId);
166     InitString(&id->id, node->text);
167     return id;
168   } else if (node->type == AstNode::Type::kType) {
169     if (type2.text[0] == '#' && type2.text[1] == '6' && type2.text[2] == '.') {
170       CddlType* tagged_type = AddCddlType(table, CddlType::Which::kTaggedType);
171       tagged_type->tagged_type.tag_value =
172           atoll(type2.text.substr(3 /* #6. */).data());
173       tagged_type->tagged_type.type = AnalyzeType(table, *node);
174       return tagged_type;
175     }
176     dprintf(STDERR_FILENO, "Unknown type2 value, expected #6.[uint]\n");
177   } else if (node->type == AstNode::Type::kGroup) {
178     if (type2.text[0] == '{') {
179       CddlType* map = AddCddlType(table, CddlType::Which::kMap);
180       map->map = AnalyzeGroup(table, *node);
181       return map;
182     } else if (type2.text[0] == '[') {
183       CddlType* array = AddCddlType(table, CddlType::Which::kArray);
184       array->array = AnalyzeGroup(table, *node);
185       return array;
186     } else if (type2.text[0] == '&') {
187       // Represents a choice between options in this group (ie an enum), not a
188       // choice between groups (which is currently unsupported).
189       CddlType* group_choice =
190           AddCddlType(table, CddlType::Which::kGroupChoice);
191       group_choice->group_choice = AnalyzeGroup(table, *node);
192       return group_choice;
193     }
194   } else if (node->type == AstNode::Type::kGroupname) {
195     if (type2.text[0] == '&') {
196       CddlType* group_choice =
197           AddCddlType(table, CddlType::Which::kGroupnameChoice);
198       InitString(&group_choice->id, node->text);
199       return group_choice;
200     }
201   }
202   return nullptr;
203 }
204 
AnalyzeRangeop(const AstNode & rangeop)205 CddlType::Op AnalyzeRangeop(const AstNode& rangeop) {
206   if (rangeop.text == "..") {
207     return CddlType::Op::kInclusiveRange;
208   } else if (rangeop.text == "...") {
209     return CddlType::Op::kExclusiveRange;
210   } else {
211     dprintf(STDERR_FILENO, "Unsupported '%s' range operator.\n",
212             rangeop.text.c_str());
213     return CddlType::Op::kNone;
214   }
215 }
216 
AnalyzeCtlop(const AstNode & ctlop)217 CddlType::Op AnalyzeCtlop(const AstNode& ctlop) {
218   if (!ctlop.children) {
219     dprintf(STDERR_FILENO, "Missing id for control operator '%s'.\n",
220             ctlop.text.c_str());
221     return CddlType::Op::kNone;
222   }
223   const std::string& id = ctlop.children->text;
224   if (id == "size") {
225     return CddlType::Op::kSize;
226   } else if (id == "bits") {
227     return CddlType::Op::kBits;
228   } else if (id == "regexp") {
229     return CddlType::Op::kRegexp;
230   } else if (id == "cbor") {
231     return CddlType::Op::kCbor;
232   } else if (id == "cborseq") {
233     return CddlType::Op::kCborseq;
234   } else if (id == "within") {
235     return CddlType::Op::kWithin;
236   } else if (id == "and") {
237     return CddlType::Op::kAnd;
238   } else if (id == "lt") {
239     return CddlType::Op::kLess;
240   } else if (id == "le") {
241     return CddlType::Op::kLessOrEqual;
242   } else if (id == "gt") {
243     return CddlType::Op::kGreater;
244   } else if (id == "ge") {
245     return CddlType::Op::kGreaterOrEqual;
246   } else if (id == "eq") {
247     return CddlType::Op::kEqual;
248   } else if (id == "ne") {
249     return CddlType::Op::kNotEqual;
250   } else if (id == "default") {
251     return CddlType::Op::kDefault;
252   } else {
253     dprintf(STDERR_FILENO, "Unsupported '%s' control operator.\n",
254             ctlop.text.c_str());
255     return CddlType::Op::kNone;
256   }
257 }
258 
259 // Produces CddlType by analyzing AST parsed from type1 rule
260 // ABNF rule: type1 = type2 [S (rangeop / ctlop) S type2]
AnalyzeType1(CddlSymbolTable * table,const AstNode & type1)261 CddlType* AnalyzeType1(CddlSymbolTable* table, const AstNode& type1) {
262   if (!type1.children) {
263     dprintf(STDERR_FILENO, "Missing type2 in type1 '%s'.\n",
264             type1.text.c_str());
265     return nullptr;
266   }
267   const AstNode& target_type = *type1.children;
268   CddlType* analyzed_type = AnalyzeType2(table, target_type);
269   if (!analyzed_type) {
270     dprintf(STDERR_FILENO, "Invalid type2 '%s' in type1 '%s'.\n",
271             target_type.text.c_str(), type1.text.c_str());
272     return nullptr;
273   }
274   if (!target_type.sibling) {
275     // No optional range or control operator, return type as-is
276     return analyzed_type;
277   }
278   const AstNode& operator_type = *target_type.sibling;
279   CddlType::Op op;
280   if (operator_type.type == AstNode::Type::kRangeop) {
281     op = AnalyzeRangeop(operator_type);
282   } else if (operator_type.type == AstNode::Type::kCtlop) {
283     op = AnalyzeCtlop(operator_type);
284   } else {
285     op = CddlType::Op::kNone;
286   }
287   if (op == CddlType::Op::kNone) {
288     dprintf(STDERR_FILENO,
289             "Unsupported or missing operator '%s' in type1 '%s'.\n",
290             operator_type.text.c_str(), type1.text.c_str());
291     return nullptr;
292   }
293   if (!operator_type.sibling) {
294     dprintf(STDERR_FILENO,
295             "Missing controller type for operator '%s' in type1 '%s'.\n",
296             operator_type.text.c_str(), type1.text.c_str());
297     return nullptr;
298   }
299   const AstNode& controller_type = *operator_type.sibling;
300   CddlType* constraint_type = AnalyzeType2(table, controller_type);
301   if (!constraint_type) {
302     dprintf(STDERR_FILENO,
303             "Invalid controller type '%s' for operator '%s' in type1 '%s'.\n",
304             controller_type.text.c_str(), operator_type.text.c_str(),
305             type1.text.c_str());
306     return nullptr;
307   }
308   analyzed_type->op = op;
309   analyzed_type->constraint_type = constraint_type;
310   return analyzed_type;
311 }
312 
AnalyzeType(CddlSymbolTable * table,const AstNode & type)313 CddlType* AnalyzeType(CddlSymbolTable* table, const AstNode& type) {
314   const AstNode* type1 = type.children;
315   if (type1->sibling) {
316     // If the type we are looking at has a type choice, create a top-level
317     // choice object, with a vector containing all valid choices.
318     CddlType* type_choice = AddCddlType(table, CddlType::Which::kDirectChoice);
319     InitDirectChoice(&type_choice->direct_choice);
320     while (type1) {
321       type_choice->direct_choice.push_back(AnalyzeType1(table, *type1));
322       type1 = type1->sibling;
323     }
324     return type_choice;
325   } else {
326     // Else just return the single choice.
327     return AnalyzeType1(table, *type1);
328   }
329 }
330 
331 bool AnalyzeGroupEntry(CddlSymbolTable* table,
332                        const AstNode& group_entry,
333                        CddlGroup::Entry* entry);
334 
AnalyzeGroup(CddlSymbolTable * table,const AstNode & group)335 CddlGroup* AnalyzeGroup(CddlSymbolTable* table, const AstNode& group) {
336   // NOTE: |group.children| is a grpchoice, which we don't currently handle.
337   // Therefore, we assume it has no siblings and move on to immediately handling
338   // its grpent children.
339   const AstNode* node = group.children->children;
340   table->groups.emplace_back(new CddlGroup);
341   CddlGroup* group_def = table->groups.back().get();
342   while (node) {
343     group_def->entries.emplace_back(new CddlGroup::Entry);
344     AnalyzeGroupEntry(table, *node, group_def->entries.back().get());
345     node = node->sibling;
346   }
347   return group_def;
348 }
349 
350 // Parses a string into an optional uint64_t, with the value being that
351 // represented by the string if it is present and nullopt if it cannot
352 // be parsed.
353 // TODO(rwkeane): Add support for hex and binary options.
ParseOptionalUint(const std::string & text)354 absl::optional<uint64_t> ParseOptionalUint(const std::string& text) {
355   if (text == "0") {
356     return 0;
357   }
358 
359   uint64_t parsed = std::strtoul(text.c_str(), nullptr, 10);
360   if (!parsed) {
361     return absl::nullopt;
362   }
363   return parsed;
364 }
365 
AnalyzeGroupEntry(CddlSymbolTable * table,const AstNode & group_entry,CddlGroup::Entry * entry)366 bool AnalyzeGroupEntry(CddlSymbolTable* table,
367                        const AstNode& group_entry,
368                        CddlGroup::Entry* entry) {
369   const AstNode* node = group_entry.children;
370 
371   // If it's an occurance operator (so the entry is optional), mark it as such
372   // and proceed to the next the node.
373   if (node->type == AstNode::Type::kOccur) {
374     if (node->text == "?") {
375       entry->opt_occurrence_min = CddlGroup::Entry::kOccurrenceMinUnbounded;
376       entry->opt_occurrence_max = 1;
377     } else if (node->text == "+") {
378       entry->opt_occurrence_min = 1;
379       entry->opt_occurrence_max = CddlGroup::Entry::kOccurrenceMaxUnbounded;
380     } else {
381       auto index = node->text.find('*');
382       if (index == std::string::npos) {
383         return false;
384       }
385 
386       int lower_bound = CddlGroup::Entry::kOccurrenceMinUnbounded;
387       std::string first_half = node->text.substr(0, index);
388       if ((first_half.length() != 1 || first_half.at(0) != '0') &&
389           first_half.length() != 0) {
390         lower_bound = std::atoi(first_half.c_str());
391         if (!lower_bound) {
392           return false;
393         }
394       }
395 
396       int upper_bound = CddlGroup::Entry::kOccurrenceMaxUnbounded;
397       std::string second_half =
398           index >= node->text.length() ? "" : node->text.substr(index + 1);
399       if ((second_half.length() != 1 || second_half.at(0) != '0') &&
400           second_half.length() != 0) {
401         upper_bound = std::atoi(second_half.c_str());
402         if (!upper_bound) {
403           return false;
404         }
405       }
406 
407       entry->opt_occurrence_min = lower_bound;
408       entry->opt_occurrence_max = upper_bound;
409     }
410     entry->occurrence_specified = true;
411     node = node->sibling;
412   } else {
413     entry->opt_occurrence_min = 1;
414     entry->opt_occurrence_max = 1;
415     entry->occurrence_specified = false;
416   }
417 
418   // If it's a member key (key in a map), save it and go to next node.
419   if (node->type == AstNode::Type::kMemberKey) {
420     if (node->text[node->text.size() - 1] == '>')
421       return false;
422     entry->which = CddlGroup::Entry::Which::kType;
423     InitGroupEntry(&entry->type);
424     entry->type.opt_key = std::string(node->children->text);
425     entry->type.integer_key = ParseOptionalUint(node->integer_member_key_text);
426     node = node->sibling;
427   }
428 
429   // If it's a type, process it as such.
430   if (node->type == AstNode::Type::kType) {
431     if (entry->which == CddlGroup::Entry::Which::kUninitialized) {
432       entry->which = CddlGroup::Entry::Which::kType;
433       InitGroupEntry(&entry->type);
434     }
435     entry->type.value = AnalyzeType(table, *node);
436   } else if (node->type == AstNode::Type::kGroupname) {
437     return false;
438   } else if (node->type == AstNode::Type::kGroup) {
439     entry->which = CddlGroup::Entry::Which::kGroup;
440     entry->group = AnalyzeGroup(table, *node);
441   }
442   return true;
443 }
444 
BuildSymbolTable(const AstNode & rules)445 std::pair<bool, CddlSymbolTable> BuildSymbolTable(const AstNode& rules) {
446   std::pair<bool, CddlSymbolTable> result;
447   result.first = false;
448   auto& table = result.second;
449 
450   // Parse over all rules iteratively.
451   for (const AstNode* rule = &rules; rule; rule = rule->sibling) {
452     AstNode* node = rule->children;
453 
454     // Ensure that the node is either a type or group definition.
455     if (node->type != AstNode::Type::kTypename &&
456         node->type != AstNode::Type::kGroupname) {
457       Logger::Error("Error parsing node with text '%s'. Unexpected node type.",
458                     node->text);
459       return result;
460     }
461     bool is_type = node->type == AstNode::Type::kTypename;
462     absl::string_view name = node->text;
463 
464     // Ensure that the node is assignment.
465     node = node->sibling;
466     if (node->type != AstNode::Type::kAssign) {
467       Logger::Error("Error parsing node with text '%s'. Node type != kAssign.",
468                     node->text);
469       return result;
470     }
471 
472     // Process the definition.
473     node = node->sibling;
474     if (is_type) {
475       CddlType* type = AnalyzeType(&table, *node);
476       if (rule->type_key != absl::nullopt) {
477         auto parsed_type_key = ParseOptionalUint(rule->type_key.value());
478         if (parsed_type_key == absl::nullopt) {
479           return result;
480         }
481         type->type_key = parsed_type_key.value();
482       }
483       if (!type) {
484         Logger::Error(
485             "Error parsing node with text '%s'."
486             "Failed to analyze node type.",
487             node->text);
488       }
489       table.type_map.emplace(std::string(name), type);
490     } else {
491       table.groups.emplace_back(new CddlGroup);
492       CddlGroup* group = table.groups.back().get();
493       group->entries.emplace_back(new CddlGroup::Entry);
494       AnalyzeGroupEntry(&table, *node, group->entries.back().get());
495       table.group_map.emplace(std::string(name), group);
496     }
497   }
498 
499   DumpSymbolTable(&result.second);
500 
501   result.first = true;
502   return result;
503 }
504 
505 // Fetches a C++ Type from all known definitons, or inserts a placeholder to be
506 // updated later if the type hasn't been defined yet.
GetCppType(CppSymbolTable * table,const std::string & name)507 CppType* GetCppType(CppSymbolTable* table, const std::string& name) {
508   if (name.empty()) {
509     table->cpp_types.emplace_back(new CppType);
510     return table->cpp_types.back().get();
511   }
512   auto entry = table->cpp_type_map.find(name);
513   if (entry != table->cpp_type_map.end())
514     return entry->second;
515   table->cpp_types.emplace_back(new CppType);
516   table->cpp_type_map.emplace(name, table->cpp_types.back().get());
517   return table->cpp_types.back().get();
518 }
519 
520 bool IncludeGroupMembersInEnum(CppSymbolTable* table,
521                                const CddlSymbolTable& cddl_table,
522                                CppType* cpp_type,
523                                const CddlGroup& group);
524 
IncludeGroupMembersInSubEnum(CppSymbolTable * table,const CddlSymbolTable & cddl_table,CppType * cpp_type,const std::string & name)525 bool IncludeGroupMembersInSubEnum(CppSymbolTable* table,
526                                   const CddlSymbolTable& cddl_table,
527                                   CppType* cpp_type,
528                                   const std::string& name) {
529   auto group_entry = cddl_table.group_map.find(name);
530   if (group_entry == cddl_table.group_map.end()) {
531     return false;
532   }
533   if (group_entry->second->entries.size() != 1 ||
534       group_entry->second->entries[0]->which !=
535           CddlGroup::Entry::Which::kGroup) {
536     return false;
537   }
538   CppType* sub_enum = GetCppType(table, name);
539   if (sub_enum->which == CppType::Which::kUninitialized) {
540     sub_enum->InitEnum();
541     sub_enum->name = name;
542     if (!IncludeGroupMembersInEnum(table, cddl_table, sub_enum,
543                                    *group_entry->second->entries[0]->group)) {
544       return false;
545     }
546   }
547   cpp_type->enum_type.sub_members.push_back(sub_enum);
548   return true;
549 }
550 
IncludeGroupMembersInEnum(CppSymbolTable * table,const CddlSymbolTable & cddl_table,CppType * cpp_type,const CddlGroup & group)551 bool IncludeGroupMembersInEnum(CppSymbolTable* table,
552                                const CddlSymbolTable& cddl_table,
553                                CppType* cpp_type,
554                                const CddlGroup& group) {
555   for (const auto& x : group.entries) {
556     if (x->HasOccurrenceOperator() ||
557         x->which != CddlGroup::Entry::Which::kType) {
558       return false;
559     }
560     if (x->type.value->which == CddlType::Which::kValue &&
561         !x->type.opt_key.empty()) {
562       cpp_type->enum_type.members.emplace_back(
563           x->type.opt_key, atoi(x->type.value->value.c_str()));
564     } else if (x->type.value->which == CddlType::Which::kId) {
565       IncludeGroupMembersInSubEnum(table, cddl_table, cpp_type,
566                                    x->type.value->id);
567     } else {
568       return false;
569     }
570   }
571   return true;
572 }
573 
574 CppType* MakeCppType(CppSymbolTable* table,
575                      const CddlSymbolTable& cddl_table,
576                      const std::string& name,
577                      const CddlType& type);
578 
AddMembersToStruct(CppSymbolTable * table,const CddlSymbolTable & cddl_table,CppType * cpp_type,const std::vector<std::unique_ptr<CddlGroup::Entry>> & entries)579 bool AddMembersToStruct(
580     CppSymbolTable* table,
581     const CddlSymbolTable& cddl_table,
582     CppType* cpp_type,
583     const std::vector<std::unique_ptr<CddlGroup::Entry>>& entries) {
584   for (const auto& x : entries) {
585     if (x->which == CddlGroup::Entry::Which::kType) {
586       if (x->type.opt_key.empty()) {
587         // If the represented node has no text (ie - it's code generated) then
588         // it must have an inner type that is based on the user input. If this
589         // one looks as expected, process it recursively.
590         if (x->type.value->which != CddlType::Which::kId ||
591             x->HasOccurrenceOperator()) {
592           return false;
593         }
594         auto group_entry = cddl_table.group_map.find(x->type.value->id);
595         if (group_entry == cddl_table.group_map.end())
596           return false;
597         if (group_entry->second->entries.size() != 1 ||
598             group_entry->second->entries[0]->which !=
599                 CddlGroup::Entry::Which::kGroup) {
600           return false;
601         }
602         if (!AddMembersToStruct(
603                 table, cddl_table, cpp_type,
604                 group_entry->second->entries[0]->group->entries)) {
605           return false;
606         }
607       } else {
608         // Here it is a real type definition - so process it as such.
609         CppType* member_type =
610             MakeCppType(table, cddl_table,
611                         cpp_type->name + std::string("_") + x->type.opt_key,
612                         *x->type.value);
613         if (!member_type)
614           return false;
615         if (member_type->name.empty())
616           member_type->name = x->type.opt_key;
617         if (x->opt_occurrence_min ==
618                 CddlGroup::Entry::kOccurrenceMinUnbounded &&
619             x->opt_occurrence_max == 1) {
620           // Create an "optional" type, with sub-type being the type that is
621           // optional. This corresponds with occurrence operator '?'.
622           table->cpp_types.emplace_back(new CppType);
623           CppType* optional_type = table->cpp_types.back().get();
624           optional_type->which = CppType::Which::kOptional;
625           optional_type->optional_type = member_type;
626           cpp_type->struct_type.members.emplace_back(
627               x->type.opt_key, x->type.integer_key, optional_type);
628         } else {
629           cpp_type->struct_type.members.emplace_back(
630               x->type.opt_key, x->type.integer_key, member_type);
631         }
632       }
633     } else {
634       // If it's not a type, it's a group so add its members recursuvely.
635       if (!AddMembersToStruct(table, cddl_table, cpp_type, x->group->entries))
636         return false;
637     }
638   }
639   return true;
640 }
641 
MakeCppType(CppSymbolTable * table,const CddlSymbolTable & cddl_table,const std::string & name,const CddlType & type)642 CppType* MakeCppType(CppSymbolTable* table,
643                      const CddlSymbolTable& cddl_table,
644                      const std::string& name,
645                      const CddlType& type) {
646   CppType* cpp_type = nullptr;
647   switch (type.which) {
648     case CddlType::Which::kId: {
649       if (type.id == "uint") {
650         cpp_type = GetCppType(table, name);
651         cpp_type->which = CppType::Which::kUint64;
652       } else if (type.id == "text") {
653         cpp_type = GetCppType(table, name);
654         cpp_type->which = CppType::Which::kString;
655       } else if (type.id == "bytes") {
656         cpp_type = GetCppType(table, name);
657         cpp_type->InitBytes();
658         if (type.op == CddlType::Op::kSize) {
659           size_t size = 0;
660           if (!absl::SimpleAtoi(type.constraint_type->value, &size)) {
661             return nullptr;
662           }
663           cpp_type->bytes_type.fixed_size = size;
664         }
665       } else {
666         cpp_type = GetCppType(table, type.id);
667       }
668     } break;
669     case CddlType::Which::kMap: {
670       cpp_type = GetCppType(table, name);
671       cpp_type->InitStruct();
672       cpp_type->struct_type.key_type = CppType::Struct::KeyType::kMap;
673       cpp_type->name = name;
674       if (!AddMembersToStruct(table, cddl_table, cpp_type, type.map->entries))
675         return nullptr;
676     } break;
677     case CddlType::Which::kArray: {
678       cpp_type = GetCppType(table, name);
679       if (type.array->entries.size() == 1 &&
680           type.array->entries[0]->HasOccurrenceOperator()) {
681         cpp_type->InitVector();
682         cpp_type->vector_type.min_length =
683             type.array->entries[0]->opt_occurrence_min;
684         cpp_type->vector_type.max_length =
685             type.array->entries[0]->opt_occurrence_max;
686         cpp_type->vector_type.element_type =
687             GetCppType(table, type.array->entries[0]->type.value->id);
688       } else {
689         cpp_type->InitStruct();
690         cpp_type->struct_type.key_type = CppType::Struct::KeyType::kArray;
691         cpp_type->name = name;
692         if (!AddMembersToStruct(table, cddl_table, cpp_type,
693                                 type.map->entries)) {
694           return nullptr;
695         }
696       }
697     } break;
698     case CddlType::Which::kGroupChoice: {
699       cpp_type = GetCppType(table, name);
700       cpp_type->InitEnum();
701       cpp_type->name = name;
702       if (!IncludeGroupMembersInEnum(table, cddl_table, cpp_type,
703                                      *type.group_choice)) {
704         return nullptr;
705       }
706     } break;
707     case CddlType::Which::kGroupnameChoice: {
708       cpp_type = GetCppType(table, name);
709       cpp_type->InitEnum();
710       cpp_type->name = name;
711       if (!IncludeGroupMembersInSubEnum(table, cddl_table, cpp_type, type.id)) {
712         return nullptr;
713       }
714     } break;
715     case CddlType::Which::kDirectChoice: {
716       cpp_type = GetCppType(table, name);
717       cpp_type->InitDiscriminatedUnion();
718       for (const auto* cddl_choice : type.direct_choice) {
719         CppType* member = MakeCppType(table, cddl_table, "", *cddl_choice);
720         if (!member)
721           return nullptr;
722         cpp_type->discriminated_union.members.push_back(member);
723       }
724       return cpp_type;
725     } break;
726     case CddlType::Which::kTaggedType: {
727       cpp_type = GetCppType(table, name);
728       cpp_type->which = CppType::Which::kTaggedType;
729       cpp_type->tagged_type.tag = type.tagged_type.tag_value;
730       cpp_type->tagged_type.real_type =
731           MakeCppType(table, cddl_table, "", *type.tagged_type.type);
732     } break;
733     default:
734       return nullptr;
735   }
736 
737   cpp_type->type_key = type.type_key;
738   return cpp_type;
739 }
740 
PrePopulateCppTypes(CppSymbolTable * table)741 void PrePopulateCppTypes(CppSymbolTable* table) {
742   std::vector<std::pair<std::string, CppType::Which>> default_types;
743   default_types.emplace_back("text", CppType::Which::kString);
744   default_types.emplace_back("tstr", CppType::Which::kString);
745   default_types.emplace_back("bstr", CppType::Which::kBytes);
746   default_types.emplace_back("bytes", CppType::Which::kBytes);
747   default_types.emplace_back("uint", CppType::Which::kUint64);
748 
749   for (auto& pair : default_types) {
750     auto entry = table->cpp_type_map.find(pair.first);
751     if (entry != table->cpp_type_map.end())
752       continue;
753     table->cpp_types.emplace_back(new CppType);
754     auto* type = table->cpp_types.back().get();
755     type->name = pair.first;
756     type->which = pair.second;
757     table->cpp_type_map.emplace(pair.first, type);
758   }
759 }
760 
BuildCppTypes(const CddlSymbolTable & cddl_table)761 std::pair<bool, CppSymbolTable> BuildCppTypes(
762     const CddlSymbolTable& cddl_table) {
763   std::pair<bool, CppSymbolTable> result;
764   result.first = false;
765   PrePopulateCppTypes(&result.second);
766   auto& table = result.second;
767   for (const auto& type_entry : cddl_table.type_map) {
768     if (!MakeCppType(&table, cddl_table, type_entry.first,
769                      *type_entry.second)) {
770       return result;
771     }
772   }
773 
774   result.first = true;
775   return result;
776 }
777 
VerifyUniqueKeysInMember(std::unordered_set<std::string> * keys,const CppType::Struct::CppMember & member)778 bool VerifyUniqueKeysInMember(std::unordered_set<std::string>* keys,
779                               const CppType::Struct::CppMember& member) {
780   return keys->insert(member.name).second &&
781          (!member.integer_key.has_value() ||
782           keys->insert(std::to_string(member.integer_key.value())).second);
783 }
784 
HasUniqueKeys(const CppType & type)785 bool HasUniqueKeys(const CppType& type) {
786   std::unordered_set<std::string> keys;
787   return type.which != CppType::Which::kStruct ||
788          absl::c_all_of(type.struct_type.members,
789                         [&keys](const CppType::Struct::CppMember& member) {
790                           return VerifyUniqueKeysInMember(&keys, member);
791                         });
792 }
793 
IsUniqueEnumValue(std::vector<uint64_t> * values,uint64_t v)794 bool IsUniqueEnumValue(std::vector<uint64_t>* values, uint64_t v) {
795   auto it = std::lower_bound(values->begin(), values->end(), v);
796   if (it == values->end() || *it != v) {
797     values->insert(it, v);
798     return true;
799   }
800   return false;
801 }
802 
HasUniqueEnumValues(std::vector<uint64_t> * values,const CppType & type)803 bool HasUniqueEnumValues(std::vector<uint64_t>* values, const CppType& type) {
804   return absl::c_all_of(type.enum_type.sub_members,
805                         [values](CppType* sub_member) {
806                           return HasUniqueEnumValues(values, *sub_member);
807                         }) &&
808          absl::c_all_of(
809              type.enum_type.members,
810              [values](const std::pair<std::string, uint64_t>& member) {
811                return IsUniqueEnumValue(values, member.second);
812              });
813 }
814 
HasUniqueEnumValues(const CppType & type)815 bool HasUniqueEnumValues(const CppType& type) {
816   std::vector<uint64_t> values;
817   return type.which != CppType::Which::kEnum ||
818          HasUniqueEnumValues(&values, type);
819 }
820 
ValidateCppTypes(const CppSymbolTable & cpp_symbols)821 bool ValidateCppTypes(const CppSymbolTable& cpp_symbols) {
822   return absl::c_all_of(
823       cpp_symbols.cpp_types, [](const std::unique_ptr<CppType>& ptr) {
824         return HasUniqueKeys(*ptr) && HasUniqueEnumValues(*ptr);
825       });
826 }
827 
DumpTypeKey(absl::optional<uint64_t> key)828 std::string DumpTypeKey(absl::optional<uint64_t> key) {
829   if (key != absl::nullopt) {
830     return " (type key=\"" + std::to_string(key.value()) + "\")";
831   }
832   return "";
833 }
834 
DumpType(CddlType * type,int indent_level)835 void DumpType(CddlType* type, int indent_level) {
836   std::string output = "";
837   for (int i = 0; i <= indent_level; ++i)
838     output += "--";
839   switch (type->which) {
840     case CddlType::Which::kDirectChoice:
841       output = "kDirectChoice" + DumpTypeKey(type->type_key) + ": ";
842       Logger::Log(output);
843       for (auto& option : type->direct_choice)
844         DumpType(option, indent_level + 1);
845       break;
846     case CddlType::Which::kValue:
847       output += "kValue" + DumpTypeKey(type->type_key) + ": " + type->value;
848       Logger::Log(output);
849       break;
850     case CddlType::Which::kId:
851       output += "kId" + DumpTypeKey(type->type_key) + ": " + type->id;
852       Logger::Log(output);
853       break;
854     case CddlType::Which::kMap:
855       output += "kMap" + DumpTypeKey(type->type_key) + ": ";
856       Logger::Log(output);
857       DumpGroup(type->map, indent_level + 1);
858       break;
859     case CddlType::Which::kArray:
860       output += "kArray" + DumpTypeKey(type->type_key) + ": ";
861       Logger::Log(output);
862       DumpGroup(type->array, indent_level + 1);
863       break;
864     case CddlType::Which::kGroupChoice:
865       output += "kGroupChoice" + DumpTypeKey(type->type_key) + ": ";
866       Logger::Log(output);
867       DumpGroup(type->group_choice, indent_level + 1);
868       break;
869     case CddlType::Which::kGroupnameChoice:
870       output += "kGroupnameChoice" + DumpTypeKey(type->type_key) + ": ";
871       Logger::Log(output);
872       break;
873     case CddlType::Which::kTaggedType:
874       output += "kTaggedType" + DumpTypeKey(type->type_key) + ": " +
875                 std::to_string(type->tagged_type.tag_value);
876       Logger::Log(output);
877       DumpType(type->tagged_type.type, indent_level + 1);
878       break;
879   }
880 }
881 
DumpGroup(CddlGroup * group,int indent_level)882 void DumpGroup(CddlGroup* group, int indent_level) {
883   for (auto& entry : group->entries) {
884     std::string output = "";
885     for (int i = 0; i <= indent_level; ++i)
886       output += "--";
887     switch (entry->which) {
888       case CddlGroup::Entry::Which::kUninitialized:
889         break;
890       case CddlGroup::Entry::Which::kType:
891         output += "kType:";
892         if (entry->HasOccurrenceOperator()) {
893           output +=
894               "minOccurance: " + std::to_string(entry->opt_occurrence_min) +
895               " maxOccurance: " + std::to_string(entry->opt_occurrence_max);
896         }
897         if (!entry->type.opt_key.empty()) {
898           output += " " + entry->type.opt_key + "=>";
899         }
900         Logger::Log(output);
901         DumpType(entry->type.value, indent_level + 1);
902         break;
903       case CddlGroup::Entry::Which::kGroup:
904         if (entry->HasOccurrenceOperator())
905           output +=
906               "minOccurance: " + std::to_string(entry->opt_occurrence_min) +
907               " maxOccurance: " + std::to_string(entry->opt_occurrence_max);
908         Logger::Log(output);
909         DumpGroup(entry->group, indent_level + 1);
910         break;
911     }
912   }
913 }
914 
DumpSymbolTable(CddlSymbolTable * table)915 void DumpSymbolTable(CddlSymbolTable* table) {
916   for (auto& entry : table->type_map) {
917     Logger::Log(entry.first);
918     DumpType(entry.second);
919   }
920   for (auto& entry : table->group_map) {
921     Logger::Log(entry.first);
922     DumpGroup(entry.second);
923   }
924 }
925