1use spec::*;
2use util::*;
3
4use std::collections::{HashMap, HashSet};
5
6use itertools::Itertools;
7
8pub struct TypeDeanonymizer {
63 builder: SpecBuilder,
64
65 supersums_of: HashMap<NodeName, HashSet<NodeName>>,
72}
73impl TypeDeanonymizer {
74 pub fn new(spec: &Spec) -> Self {
76 let mut result = TypeDeanonymizer {
77 builder: SpecBuilder::new(),
78 supersums_of: HashMap::new(),
79 };
80 let mut skip_name_map: HashMap<&FieldName, FieldName> = HashMap::new();
81
82 for (_, name) in spec.field_names() {
84 result.builder.import_field_name(name)
85 }
86
87 for (_, interface) in spec.interfaces_by_name() {
88 for field in interface.contents().fields() {
89 if field.is_lazy() {
90 let skip_name = result
91 .builder
92 .field_name(format!("{}_skip", field.name().to_str()).to_str());
93 skip_name_map.insert(field.name(), skip_name);
94 }
95 }
96 }
97
98 for (name, interface) in spec.interfaces_by_name() {
100 result.builder.import_node_name(name);
101 let mut fields = vec![];
105
106 for field in interface.contents().fields() {
108 result.import_type(spec, field.type_(), None);
109 fields.push(field.clone());
110 }
111
112 let mut declaration = result.builder.add_interface(name).unwrap();
114 for field in fields.drain(..) {
115 if field.is_lazy() {
118 declaration.with_field(
119 skip_name_map.get(field.name()).unwrap(),
120 Type::offset().required(),
121 );
122 }
123 declaration.with_field_laziness(
124 field.name(),
125 field.type_().clone(),
126 field.laziness(),
127 );
128 }
129
130 if let Some(ref field_name) = interface.scoped_dictionary() {
131 declaration.with_scoped_dictionary(field_name);
132 }
133 }
134 for (name, definition) in spec.typedefs_by_name() {
136 result.builder.import_node_name(name);
137 if result.builder.get_typedef(name).is_some() {
138 continue;
140 }
141 result.import_type(spec, &definition, Some(name.clone()));
142 }
143 for (name, definition) in spec.string_enums_by_name() {
145 result.builder.import_node_name(name);
146 let mut strings: Vec<_> = definition.strings().iter().collect();
147 let mut declaration = result.builder.add_string_enum(name).unwrap();
148 for string in strings.drain(..) {
149 declaration.with_string(&string);
150 }
151 }
152 debug!(target: "export_utils", "Names: {:?}", result.builder.names().keys().format(", "));
153
154 result
155 }
156
157 pub fn supersums(&self) -> &HashMap<NodeName, HashSet<NodeName>> {
158 &self.supersums_of
159 }
160
161 pub fn into_spec(self, options: SpecOptions) -> Spec {
163 self.builder.into_spec(options)
164 }
165
166 pub fn get_node_name(&self, name: &str) -> Option<NodeName> {
168 self.builder.get_node_name(name)
169 }
170
171 fn import_type(
173 &mut self,
174 spec: &Spec,
175 type_: &Type,
176 public_name: Option<NodeName>,
177 ) -> (Option<HashSet<NodeName>>, NodeName) {
178 debug!(target: "export_utils", "import_type {:?} => {:?}", public_name, type_);
179 if type_.is_optional() {
180 let (_, spec_name) = self.import_typespec(spec, &type_.spec, None);
181 let my_name = match public_name {
182 None => self.builder.node_name(&format!("Optional{}", spec_name)),
183 Some(ref name) => name.clone(),
184 };
185 let deanonymized = Type::named(&spec_name).optional().unwrap(); if let Some(ref mut typedef) = self.builder.add_typedef(&my_name) {
187 debug!(target: "export_utils", "import_type introduced {:?}", my_name);
188 typedef.with_type(deanonymized.clone());
189 } else {
190 debug!(target: "export_utils", "import_type: Attempting to redefine typedef {name}", name = my_name.to_str());
191 }
192 (None, my_name)
193 } else {
194 self.import_typespec(spec, &type_.spec, public_name)
195 }
196 }
197 fn import_typespec(
198 &mut self,
199 spec: &Spec,
200 type_spec: &TypeSpec,
201 public_name: Option<NodeName>,
202 ) -> (Option<HashSet<NodeName>>, NodeName) {
203 debug!(target: "export_utils", "import_typespec {:?} => {:?}", public_name, type_spec);
204 match *type_spec {
205 TypeSpec::Boolean
206 | TypeSpec::Number
207 | TypeSpec::UnsignedLong
208 | TypeSpec::PropertyKey
209 | TypeSpec::IdentifierName
210 | TypeSpec::String
211 | TypeSpec::Offset
212 | TypeSpec::Void => {
213 if let Some(ref my_name) = public_name {
214 if let Some(ref mut typedef) = self.builder.add_typedef(&my_name) {
215 debug!(target: "export_utils", "import_typespec: Defining {name} (primitive)", name = my_name.to_str());
216 typedef.with_type(type_spec.clone().required());
217 } else {
218 debug!(target: "export_utils", "import_typespec: Attempting to redefine typedef {name}", name = my_name.to_str());
219 }
220 }
221 let name = match *type_spec {
224 TypeSpec::PropertyKey => self.builder.node_name("PropertyKey"),
225 TypeSpec::IdentifierName => self.builder.node_name("IdentifierName"),
226 _ => self.builder.node_name(&format!("@@{:?}", type_spec)),
227 };
228 (None, name)
229 }
230 TypeSpec::NamedType(ref link) => {
231 let resolved = spec.get_type_by_name(link)
232 .unwrap_or_else(|| panic!("While deanonymizing, could not find the definition of {} in the original spec.", link.to_str()));
233 let (sum, rewrite, primitive) = match resolved {
234 NamedType::StringEnum(_) => {
235 (None, None, None)
238 }
239 NamedType::Typedef(ref type_) => {
240 let (sum, name) = self.import_type(spec, type_, Some(link.clone()));
243 (sum, Some(name), type_.get_primitive(spec))
244 }
245 NamedType::Interface(_) => {
246 let sum = [link.clone()].iter().cloned().collect();
249 (Some(sum), None, None)
250 }
251 };
252 debug!(target: "export_utils", "import_typespec dealing with named type {}, public name {:?} => {:?}",
253 link, public_name, rewrite);
254 if let Some(ref my_name) = public_name {
255 if let Some(content) = rewrite {
257 let deanonymized = match primitive {
258 None
259 | Some(IsNullable {
260 is_nullable: true, ..
261 })
262 | Some(IsNullable {
263 content: Primitive::Interface(_),
264 ..
265 }) => Type::named(&content).required(),
266 Some(IsNullable {
267 content: Primitive::String,
268 ..
269 }) => Type::string().required(),
270 Some(IsNullable {
271 content: Primitive::IdentifierName,
272 ..
273 }) => Type::identifier_name().required(),
274 Some(IsNullable {
275 content: Primitive::PropertyKey,
276 ..
277 }) => Type::property_key().required(),
278 Some(IsNullable {
279 content: Primitive::Number,
280 ..
281 }) => Type::number().required(),
282 Some(IsNullable {
283 content: Primitive::UnsignedLong,
284 ..
285 }) => Type::unsigned_long().required(),
286 Some(IsNullable {
287 content: Primitive::Boolean,
288 ..
289 }) => Type::bool().required(),
290 Some(IsNullable {
291 content: Primitive::Offset,
292 ..
293 }) => Type::offset().required(),
294 Some(IsNullable {
295 content: Primitive::Void,
296 ..
297 }) => Type::void().required(),
298 };
299 debug!(target: "export_utils", "import_typespec aliasing {:?} => {:?}",
300 my_name, deanonymized);
301 if let Some(ref mut typedef) = self.builder.add_typedef(&my_name) {
302 debug!(target: "export_utils", "import_typespec: Defining {name} (name to content)", name = my_name.to_str());
303 typedef.with_type(deanonymized.clone());
304 } else {
305 debug!(target: "export_utils", "import_typespec: Attempting to redefine typedef {name}", name = my_name.to_str());
306 }
307 }
308 let deanonymized = Type::named(link).required();
310 if let Some(ref mut typedef) = self.builder.add_typedef(&my_name) {
311 debug!(target: "export_utils", "import_typespec: Defining {name} (name to link)", name = my_name.to_str());
312 typedef.with_type(deanonymized.clone());
313 } else {
314 debug!(target: "export_utils", "import_typespec: Attempting to redefine typedef {name}", name = my_name.to_str());
315 }
316 }
317 (sum, link.clone())
318 }
319 TypeSpec::Array {
320 ref contents,
321 ref supports_empty,
322 } => {
323 let (_, contents_name) = self.import_type(spec, contents, None);
324 let my_name = match public_name {
325 None => self.builder.node_name(&format!(
326 "{non_empty}ListOf{content}",
327 non_empty = if *supports_empty { "" } else { "NonEmpty" },
328 content = contents_name.to_str()
329 )),
330 Some(ref name) => name.clone(),
331 };
332 let deanonymized = if *supports_empty {
333 Type::named(&contents_name).array()
334 } else {
335 Type::named(&contents_name).non_empty_array()
336 };
337 if let Some(ref mut typedef) = self.builder.add_typedef(&my_name) {
338 debug!(target: "export_utils", "import_typespec: Defining {name} (name to list)",
339 name = my_name.to_str());
340 typedef.with_type(deanonymized.clone());
341 } else {
342 debug!(target: "export_utils", "import_typespec: Attempting to redefine typedef {name}", name = my_name.to_str());
343 }
344 (None, my_name)
345 }
346 TypeSpec::TypeSum(ref sum) => {
347 let mut full_sum = HashSet::new();
348 let mut names = vec![];
349 let mut subsums = vec![];
350 for sub_type in sum.types() {
351 let (sub_sum, name) = self.import_typespec(spec, sub_type, None);
352 let mut sub_sum = sub_sum.unwrap_or_else(
353 || panic!("While treating {:?}, attempting to create a sum containing {}, which isn't an interface or a sum of interfaces", type_spec, name)
354 );
355 if sub_sum.len() > 1 {
356 subsums.push(name.clone())
358 }
359 names.push(name);
360 for item in sub_sum.drain() {
361 full_sum.insert(item);
362 }
363 }
364 let my_name = match public_name {
365 None => self.builder.node_name(&format!(
366 "{}",
367 names.into_iter().sorted().into_iter().format("Or")
368 )),
369 Some(ref name) => name.clone(),
370 };
371 for subsum_name in subsums {
372 let supersum_entry = self
374 .supersums_of
375 .entry(subsum_name.clone())
376 .or_insert_with(|| HashSet::new());
377 supersum_entry.insert(my_name.clone());
378 }
379 let sum: Vec<_> = full_sum.iter().map(Type::named).collect();
380 let deanonymized = Type::sum(&sum).required();
381 if let Some(ref mut typedef) = self.builder.add_typedef(&my_name) {
382 debug!(target: "export_utils", "import_typespec: Defining {name} (name to sum)", name = my_name.to_str());
383 typedef.with_type(deanonymized.clone());
384 } else {
385 debug!(target: "export_utils", "import_type: Attempting to redefine typedef {name}", name = my_name.to_str());
386 }
387 (Some(full_sum), my_name)
388 }
389 }
390 }
391}
392
393pub struct TypeName;
395impl TypeName {
396 pub fn type_(type_: &Type) -> String {
397 let spec_name = Self::type_spec(type_.spec());
398 if type_.is_optional() {
399 format!("Optional{}", spec_name)
400 } else {
401 spec_name
402 }
403 }
404
405 pub fn type_spec(spec: &TypeSpec) -> String {
406 match *spec {
407 TypeSpec::Array {
408 ref contents,
409 supports_empty: false,
410 } => format!("NonEmptyListOf{}", Self::type_(contents)),
411 TypeSpec::Array {
412 ref contents,
413 supports_empty: true,
414 } => format!("ListOf{}", Self::type_(contents)),
415 TypeSpec::NamedType(ref name) => name.to_string().clone(),
416 TypeSpec::Offset => "_Offset".to_string(),
417 TypeSpec::Boolean => "_Bool".to_string(),
418 TypeSpec::Number => "_Number".to_string(),
419 TypeSpec::UnsignedLong => "_UnsignedLong".to_string(),
420 TypeSpec::String => "_String".to_string(),
421 TypeSpec::Void => "_Void".to_string(),
422 TypeSpec::IdentifierName => "IdentifierName".to_string(),
423 TypeSpec::PropertyKey => "PropertyKey".to_string(),
424 TypeSpec::TypeSum(ref sum) => format!(
425 "{}",
426 sum.types()
427 .iter()
428 .map(Self::type_spec)
429 .sorted()
430 .into_iter()
431 .format("Or")
432 ),
433 }
434 }
435}
436
437pub struct ToWebidl;
441impl ToWebidl {
442 pub fn spec(spec: &TypeSpec, prefix: &str, indent: &str) -> Option<String> {
444 let result = match *spec {
445 TypeSpec::Offset => {
446 return None;
447 }
448 TypeSpec::Array {
449 ref contents,
450 ref supports_empty,
451 } => match Self::type_(&*contents, prefix, indent) {
452 None => {
453 return None;
454 }
455 Some(description) => format!(
456 "{emptiness}FrozenArray<{}>",
457 description,
458 emptiness = if *supports_empty { "" } else { "[NonEmpty] " }
459 ),
460 },
461 TypeSpec::Boolean => "bool".to_string(),
462 TypeSpec::String => "string".to_string(),
463 TypeSpec::PropertyKey => "[PropertyKey] string".to_string(),
464 TypeSpec::IdentifierName => "[IdentifierName] string".to_string(),
465 TypeSpec::Number => "number".to_string(),
466 TypeSpec::UnsignedLong => "unsigned long".to_string(),
467 TypeSpec::NamedType(ref name) => name.to_str().to_string(),
468 TypeSpec::TypeSum(ref sum) => format!(
469 "({})",
470 sum.types()
471 .iter()
472 .filter_map(|x| Self::spec(x, "", indent))
473 .format(" or ")
474 ),
475 TypeSpec::Void => "void".to_string(),
476 };
477 Some(result)
478 }
479
480 pub fn type_(type_: &Type, prefix: &str, indent: &str) -> Option<String> {
482 let pretty_type = Self::spec(type_.spec(), prefix, indent);
483 match pretty_type {
484 None => None,
485 Some(pretty_type) => Some(format!(
486 "{}{}",
487 pretty_type,
488 if type_.is_optional() { "?" } else { "" }
489 )),
490 }
491 }
492
493 pub fn interface(interface: &Interface, prefix: &str, indent: &str) -> String {
495 let mut result = format!(
496 "{prefix} interface {name} : Node {{\n",
497 prefix = prefix,
498 name = interface.name().to_str()
499 );
500 {
501 let prefix = format!("{prefix}{indent}", prefix = prefix, indent = indent);
502 for field in interface.contents().fields() {
503 match Self::type_(field.type_(), &prefix, indent) {
504 None =>
505 {}
507 Some(description) => {
508 if let Some(ref doc) = field.doc() {
509 result.push_str(&format!(
510 "{prefix}// {doc}\n",
511 prefix = prefix,
512 doc = doc
513 ));
514 }
515 result.push_str(&format!(
516 "{prefix}{description} {name};\n",
517 prefix = prefix,
518 name = field.name().to_str(),
519 description = description
520 ));
521 if field.doc().is_some() {
522 result.push_str("\n");
523 }
524 }
525 }
526 }
527 }
528 result.push_str(&format!("{prefix} }}\n", prefix = prefix));
529 result
530 }
531}