// Copyright 2015 Brendan Zabarauskas and the gl-rs developers // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use std::collections::btree_map::{self, Entry}; use std::collections::{BTreeMap, BTreeSet}; use std::io; use webidl::ast; use utils::{multimap_insert, parse_defs}; use webgl_generators::Generator; use super::named::{ Argument, Attribute, Callback, Const, Dictionary, Enum, Field, Interface, Member, Mixin, NamedType, Operation, }; use super::types::{Primitive, Type, TypeKind}; use super::{Api, Exts, HIDDEN_NAMES, RENDERING_CONTEXTS}; #[derive(Debug, Default)] pub struct Registry { pub types: BTreeMap, pub extensions: BTreeSet, } /// Helper for iterating over specific kinds of named type pub struct TypeIter<'a, T: 'a, F: FnMut(&'a NamedType) -> Option<&'a T>> { inner: btree_map::Iter<'a, String, NamedType>, f: F, } impl<'a, T: 'a, F: FnMut(&'a NamedType) -> Option<&'a T>> Iterator for TypeIter<'a, T, F> { type Item = (&'a String, &'a T); fn next(&mut self) -> Option { // Implement a variation of `flat_map` while let Some((k, v)) = self.inner.next() { if let Some(t) = (self.f)(v) { return Some((k, t)); } } None } } impl Registry { /// Construct a new registry given a maximum API version /// and a set of extensions to support. pub fn new(api: Api, exts: Exts) -> Registry { let mut result = Registry::default(); // First load definitions from all API versions // up to the one requested. for idl_const in api.idl_consts() { for def in parse_defs(idl_const) { result.load_definition(def); } } // Next find all requested extensions for ext in exts.enumerate() { if api < ext.min_api { continue; } // Make a note that we included this extension result.extensions.insert(ext.name.clone()); // Load the definitions for def in parse_defs(ext.idl.as_bytes()) { result.load_definition(def); } // Attach overview doc comment let ext_iface = result .types .get_mut(&ext.name) .and_then(NamedType::as_interface_mut) .expect(&ext.name); ext_iface.doc_comment = ext.overview; // Attach individual function doc comments for (name, new_fun) in ext.new_funs { let members = ext_iface.members.get_mut(&name).unwrap(); for member in members { if let Member::Operation(ref mut op) = *member { op.doc_comment = new_fun.clone(); } } } } // Find the latest version of the rendering context, and generate a // version-independent rendering context helper type called "GLContext". for name in RENDERING_CONTEXTS.into_iter().rev() { if let Some(NamedType::Interface(mut iface)) = result.types.get(name.1).cloned() { iface.rendering_context = None; result .types .insert("GLContext".into(), NamedType::Interface(iface)); break; } } // Hide types that are listed in our "hidden names" array for &hidden_name in HIDDEN_NAMES { if let Some(interface) = result .types .get_mut(hidden_name) .and_then(NamedType::as_interface_mut) { interface.is_hidden = true; } if let Some(dictionary) = result .types .get_mut(hidden_name) .and_then(NamedType::as_dictionary_mut) { dictionary.is_hidden = true; } } // Done! result } /// Iterator over types matched by a filtering function pub fn iter_types<'a, T, F: FnMut(&'a NamedType) -> Option<&'a T>>( &'a self, f: F, ) -> TypeIter<'a, T, F> { TypeIter { inner: self.types.iter(), f, } } /// Resolves a named type pub fn resolve_type(&self, name: &str) -> &NamedType { self.types.get(name).expect(name) } /// Use the specified generator to generate bindings from this registry /// and write them to a stream. pub fn write_bindings(&self, generator: G, output: &mut W) -> io::Result<()> where G: Generator, W: io::Write, { generator.write(&self, output) } fn load_const(&mut self, const_: ast::Const) -> Option<(String, Member)> { use self::ast::{ConstType, ConstValue}; let type_ = ast::Type { extended_attributes: Vec::new(), kind: match const_.type_ { ConstType::Boolean => ast::TypeKind::Boolean, ConstType::Byte => ast::TypeKind::Byte, ConstType::Octet => ast::TypeKind::Octet, ConstType::RestrictedDouble => ast::TypeKind::RestrictedDouble, ConstType::UnrestrictedDouble => ast::TypeKind::UnrestrictedDouble, ConstType::RestrictedFloat => ast::TypeKind::RestrictedFloat, ConstType::UnrestrictedFloat => ast::TypeKind::UnrestrictedFloat, ConstType::SignedLong => ast::TypeKind::SignedLong, ConstType::UnsignedLong => ast::TypeKind::UnsignedLong, ConstType::SignedLongLong => ast::TypeKind::SignedLongLong, ConstType::UnsignedLongLong => ast::TypeKind::UnsignedLongLong, ConstType::SignedShort => ast::TypeKind::SignedShort, ConstType::UnsignedShort => ast::TypeKind::UnsignedShort, ConstType::Identifier(s) => ast::TypeKind::Identifier(s), }, nullable: const_.nullable, }; Some(( const_.name, Member::Const(Const { type_: self.load_type(type_), value: match const_.value { ConstValue::BooleanLiteral(b) => format!("{:?}", b), ConstValue::FloatLiteral(v) => format!("{:?}", v), ConstValue::SignedIntegerLiteral(v) => format!("{:?}", v), ConstValue::UnsignedIntegerLiteral(v) => format!("{:?}", v), ConstValue::Null => "None".into(), }, }), )) } fn load_attribute(&mut self, attribute: ast::Attribute) -> Option<(String, Member)> { use self::ast::Attribute::*; match attribute { Regular(a) => { let type_ = self.load_type(*a.type_); Some(( a.name, Member::Attribute(Attribute { type_, setter: !a.read_only, getter: !a.inherits, }), )) }, _ => None, } } fn load_argument(&mut self, argument: ast::Argument) -> Argument { let type_ = self.load_type(*argument.type_); Argument { name: argument.name, optional: argument.optional, type_, variadic: argument.variadic, } } fn load_operation(&mut self, operation: ast::Operation) -> Option<(String, Member)> { use self::ast::Operation::*; use self::ast::ReturnType; match operation { Regular(o) => { if let Some(name) = o.name { Some(( name, Member::Operation(Operation { args: o .arguments .into_iter() .map(|a| self.load_argument(a)) .collect(), return_type: match o.return_type { ReturnType::NonVoid(t) => Some(self.load_type(*t)), ReturnType::Void => None, }, doc_comment: String::new(), }), )) } else { None } }, _ => None, } } fn load_mixin_member(&mut self, member: ast::MixinMember) -> Option<(String, Member)> { use self::ast::MixinMember::*; match member { Const(c) => self.load_const(c), Attribute(a) => self.load_attribute(a), Operation(o) => self.load_operation(o), } } fn load_mixin(&mut self, mixin: ast::NonPartialMixin) { let mut members = BTreeMap::new(); for (name, member) in mixin .members .into_iter() .flat_map(|m| self.load_mixin_member(m)) { multimap_insert(&mut members, name, member); } self.load_named_type(&mixin.name, NamedType::Mixin(Mixin { members })); } fn load_interface_member(&mut self, member: ast::InterfaceMember) -> Option<(String, Member)> { use self::ast::InterfaceMember::*; match member { Const(c) => self.load_const(c), Attribute(a) => self.load_attribute(a), Operation(o) => self.load_operation(o), _ => None, } } fn load_interface(&mut self, interface: ast::NonPartialInterface) { fn has_attr(attrs: &Vec>, name: &str) -> bool { use self::ast::ExtendedAttribute::*; for attr in attrs { if let NoArguments(ref other) = **attr { if let &ast::Other::Identifier(ref n) = other { return n == name; } } } false } let mut members = BTreeMap::new(); for (name, member) in interface .members .into_iter() .flat_map(|m| self.load_interface_member(m)) { multimap_insert(&mut members, name, member); } let mut result = Interface { inherits: interface.inherits, mixins: BTreeSet::new(), members, is_hidden: false, has_class: !has_attr(&interface.extended_attributes, "NoInterfaceObject"), rendering_context: None, doc_comment: String::new(), }; for &(context_id, context_interface) in RENDERING_CONTEXTS { if context_interface == interface.name { result.rendering_context = Some(context_id); break; } } match self.types.entry(interface.name) { Entry::Vacant(v) => { v.insert(NamedType::Interface(result)); }, Entry::Occupied(o) => { assert!( result.members.is_empty(), "Duplicate interface: {}", o.key() ); }, } } fn load_includes(&mut self, includes: ast::Includes) { if let Some(interface) = self .types .get_mut(&includes.includer) .and_then(NamedType::as_interface_mut) { interface.mixins.insert(includes.includee); } } // Load a type kind into the registry under a given name, and return // a reference to it. fn load_named_type(&mut self, name: &str, named_type: NamedType) -> Type { if !self.types.contains_key(name) { self.types.insert(name.into(), named_type); } name.into() } // Convert an AST type kind into a type kind the generator can understand fn load_type_kind(&mut self, kind: ast::TypeKind) -> TypeKind { use self::ast::TypeKind::*; match kind { // Primitives Boolean => TypeKind::Primitive(Primitive::Bool), Byte => TypeKind::Primitive(Primitive::I8), Octet => TypeKind::Primitive(Primitive::U8), SignedShort => TypeKind::Primitive(Primitive::I16), UnsignedShort => TypeKind::Primitive(Primitive::U16), SignedLong => TypeKind::Primitive(Primitive::I32), UnsignedLong => TypeKind::Primitive(Primitive::U32), SignedLongLong => TypeKind::Primitive(Primitive::I64), UnsignedLongLong => TypeKind::Primitive(Primitive::U64), RestrictedFloat | UnrestrictedFloat => TypeKind::Primitive(Primitive::F32), RestrictedDouble | UnrestrictedDouble => TypeKind::Primitive(Primitive::F64), // Strings DOMString | USVString => TypeKind::String, ByteString => unimplemented!(), // TypedArrays Int8Array => TypeKind::TypedArray(Primitive::I8), Uint8Array => TypeKind::TypedArray(Primitive::U8), Int16Array => TypeKind::TypedArray(Primitive::I16), Uint16Array => TypeKind::TypedArray(Primitive::U16), Int32Array => TypeKind::TypedArray(Primitive::I32), Uint32Array => TypeKind::TypedArray(Primitive::U32), Float32Array => TypeKind::TypedArray(Primitive::F32), Float64Array => TypeKind::TypedArray(Primitive::F64), // Sequence Sequence(inner) => TypeKind::Sequence(Box::new(self.load_type(*inner))), // Identifier Identifier(s) => match &*s { "BufferSource" => TypeKind::BufferSource, "HTMLCanvasElement" => TypeKind::CanvasElement, "ArrayBufferView" => TypeKind::ArrayBufferView, other => TypeKind::Named(other.into()), }, // Composite Union(inners) => { if inners.len() <= 2 { TypeKind::Union( inners .into_iter() .map(|inner| self.load_type(*inner)) .collect(), ) } else { TypeKind::Any } }, // Misc ArrayBuffer => TypeKind::ArrayBuffer, Object => TypeKind::Object, _ => TypeKind::Any, } } // Convert an AST type into a type the generator can understand fn load_type(&mut self, t: ast::Type) -> Type { Type { kind: self.load_type_kind(t.kind), optional: t.nullable, } } fn load_typedef(&mut self, typedef: ast::Typedef) { let type_ = self.load_type(*typedef.type_); self.load_named_type(&typedef.name, NamedType::Typedef(type_)); } fn load_field(&mut self, field: ast::DictionaryMember) -> (String, Field) { let type_ = self.load_type(*field.type_); (field.name, Field { type_ }) } fn load_dictionary(&mut self, dictionary: ast::NonPartialDictionary) { let fields = dictionary .members .into_iter() .map(|m| self.load_field(m)) .collect(); match self.types.entry(dictionary.name) { Entry::Vacant(v) => { v.insert(NamedType::Dictionary(Dictionary { inherits: dictionary.inherits, fields, is_hidden: false, })); }, Entry::Occupied(mut o) => { let key = o.key().clone(); let d = o.get_mut().as_dictionary_mut().unwrap(); // Dictionary is being extended, so make these fields all optional for (k, mut field) in fields { field.type_.optional = true; if d.fields.insert(k.clone(), field).is_some() { panic!("Duplicate field: {}.{}", key, k); } } }, } } fn load_enum(&mut self, enum_: ast::Enum) { let variants = enum_.variants.into_iter().collect(); self.load_named_type(&enum_.name, NamedType::Enum(Enum { variants })); } fn load_callback(&mut self, callback: ast::Callback) { use self::ast::ReturnType; let args = callback .arguments .into_iter() .map(|a| self.load_argument(a)) .collect(); let return_type = match callback.return_type { ReturnType::NonVoid(t) => Some(self.load_type(*t)), ReturnType::Void => None, }; self.load_named_type( &callback.name, NamedType::Callback(Callback { args, return_type }), ); } fn load_definition(&mut self, def: ast::Definition) { use self::ast::Definition::*; match def { Mixin(ast::Mixin::NonPartial(m)) => self.load_mixin(m), Interface(ast::Interface::NonPartial(i)) => self.load_interface(i), Includes(i) => self.load_includes(i), Typedef(t) => self.load_typedef(t), Dictionary(ast::Dictionary::NonPartial(d)) => self.load_dictionary(d), Enum(e) => self.load_enum(e), Callback(c) => self.load_callback(c), _ => {}, } } }