mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-03-16 18:20:51 +00:00
Merge pull request #679 from afdw/master
Add support for unions in arguments and for optional arguments
This commit is contained in:
commit
3f7c8d1b6f
@ -1,4 +1,3 @@
|
||||
use std::collections::BTreeMap;
|
||||
use proc_macro2::{Ident, Span};
|
||||
use shared;
|
||||
use syn;
|
||||
@ -8,7 +7,7 @@ use Diagnostic;
|
||||
/// An abstract syntax tree representing a rust program. Contains
|
||||
/// extra information for joining up this rust code with javascript.
|
||||
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq))]
|
||||
#[derive(Default)]
|
||||
#[derive(Default, Clone)]
|
||||
pub struct Program {
|
||||
/// rust -> js interfaces
|
||||
pub exports: Vec<Export>,
|
||||
@ -21,12 +20,13 @@ pub struct Program {
|
||||
/// rust consts
|
||||
pub consts: Vec<Const>,
|
||||
/// rust submodules
|
||||
pub modules: BTreeMap<Ident, Module>,
|
||||
pub modules: Vec<Module>,
|
||||
}
|
||||
|
||||
/// A rust to js interface. Allows interaction with rust objects/functions
|
||||
/// from javascript.
|
||||
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
|
||||
#[derive(Clone)]
|
||||
pub struct Export {
|
||||
/// The javascript class name.
|
||||
pub class: Option<Ident>,
|
||||
@ -47,6 +47,7 @@ pub struct Export {
|
||||
|
||||
/// The 3 types variations of `self`.
|
||||
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
|
||||
#[derive(Clone)]
|
||||
pub enum MethodSelf {
|
||||
/// `self`
|
||||
ByValue,
|
||||
@ -57,6 +58,7 @@ pub enum MethodSelf {
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
|
||||
#[derive(Clone)]
|
||||
pub struct Import {
|
||||
pub module: Option<String>,
|
||||
pub js_namespace: Option<Ident>,
|
||||
@ -64,6 +66,7 @@ pub struct Import {
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
|
||||
#[derive(Clone)]
|
||||
pub enum ImportKind {
|
||||
Function(ImportFunction),
|
||||
Static(ImportStatic),
|
||||
@ -72,6 +75,7 @@ pub enum ImportKind {
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
|
||||
#[derive(Clone)]
|
||||
pub struct ImportFunction {
|
||||
pub function: Function,
|
||||
pub rust_name: Ident,
|
||||
@ -84,6 +88,7 @@ pub struct ImportFunction {
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
|
||||
#[derive(Clone)]
|
||||
pub enum ImportFunctionKind {
|
||||
Method {
|
||||
class: String,
|
||||
@ -94,18 +99,21 @@ pub enum ImportFunctionKind {
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
|
||||
#[derive(Clone)]
|
||||
pub enum MethodKind {
|
||||
Constructor,
|
||||
Operation(Operation),
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
|
||||
#[derive(Clone)]
|
||||
pub struct Operation {
|
||||
pub is_static: bool,
|
||||
pub kind: OperationKind,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
|
||||
#[derive(Clone)]
|
||||
pub enum OperationKind {
|
||||
Regular,
|
||||
Getter(Option<Ident>),
|
||||
@ -116,6 +124,7 @@ pub enum OperationKind {
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
|
||||
#[derive(Clone)]
|
||||
pub struct ImportStatic {
|
||||
pub vis: syn::Visibility,
|
||||
pub ty: syn::Type,
|
||||
@ -125,6 +134,7 @@ pub struct ImportStatic {
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
|
||||
#[derive(Clone)]
|
||||
pub struct ImportType {
|
||||
pub vis: syn::Visibility,
|
||||
pub rust_name: Ident,
|
||||
@ -136,6 +146,7 @@ pub struct ImportType {
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
|
||||
#[derive(Clone)]
|
||||
pub struct ImportEnum {
|
||||
/// The Rust enum's visibility
|
||||
pub vis: syn::Visibility,
|
||||
@ -150,6 +161,7 @@ pub struct ImportEnum {
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
|
||||
#[derive(Clone)]
|
||||
pub struct Function {
|
||||
pub name: String,
|
||||
pub arguments: Vec<syn::ArgCaptured>,
|
||||
@ -159,6 +171,7 @@ pub struct Function {
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
|
||||
#[derive(Clone)]
|
||||
pub struct Struct {
|
||||
pub name: Ident,
|
||||
pub fields: Vec<StructField>,
|
||||
@ -166,6 +179,7 @@ pub struct Struct {
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
|
||||
#[derive(Clone)]
|
||||
pub struct StructField {
|
||||
pub name: Ident,
|
||||
pub struct_name: Ident,
|
||||
@ -177,6 +191,7 @@ pub struct StructField {
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
|
||||
#[derive(Clone)]
|
||||
pub struct Enum {
|
||||
pub name: Ident,
|
||||
pub variants: Vec<Variant>,
|
||||
@ -184,6 +199,7 @@ pub struct Enum {
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
|
||||
#[derive(Clone)]
|
||||
pub struct Variant {
|
||||
pub name: Ident,
|
||||
pub value: u32,
|
||||
@ -205,6 +221,7 @@ pub enum TypeLocation {
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq))]
|
||||
#[derive(Clone)]
|
||||
pub struct Const {
|
||||
pub vis: syn::Visibility,
|
||||
pub name: Ident,
|
||||
@ -214,6 +231,7 @@ pub struct Const {
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq))]
|
||||
#[derive(Clone)]
|
||||
/// same as webidl::ast::ConstValue
|
||||
pub enum ConstValue {
|
||||
BooleanLiteral(bool),
|
||||
@ -227,8 +245,10 @@ pub enum ConstValue {
|
||||
///
|
||||
/// This exists to give the ability to namespace js imports.
|
||||
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
|
||||
#[derive(Clone)]
|
||||
pub struct Module {
|
||||
pub vis: syn::Visibility,
|
||||
pub name: Ident,
|
||||
/// js -> rust interfaces
|
||||
pub imports: Vec<Import>,
|
||||
}
|
||||
@ -241,7 +261,7 @@ impl Program {
|
||||
enums: self.enums.iter().map(|a| a.shared()).collect(),
|
||||
imports: self.imports.iter()
|
||||
// add in imports from inside modules
|
||||
.chain(self.modules.values().flat_map(|m| m.imports.iter()))
|
||||
.chain(self.modules.iter().flat_map(|m| m.imports.iter()))
|
||||
.map(|a| a.shared())
|
||||
.collect::<Result<_, Diagnostic>>()?,
|
||||
version: shared::version(),
|
||||
|
@ -63,17 +63,17 @@ impl TryToTokens for ast::Program {
|
||||
errors.push(e);
|
||||
}
|
||||
}
|
||||
for m in self.modules.iter() {
|
||||
if let Err(e) = ModuleInIter::from(m).try_to_tokens(tokens) {
|
||||
errors.push(e);
|
||||
}
|
||||
}
|
||||
for e in self.enums.iter() {
|
||||
e.to_tokens(tokens);
|
||||
}
|
||||
for c in self.consts.iter() {
|
||||
c.to_tokens(tokens);
|
||||
}
|
||||
for m in self.modules.iter() {
|
||||
if let Err(e) = m.try_to_tokens(tokens) {
|
||||
errors.push(e);
|
||||
}
|
||||
}
|
||||
|
||||
Diagnostic::from_vec(errors)?;
|
||||
|
||||
@ -1111,30 +1111,17 @@ impl ToTokens for ast::Const {
|
||||
}
|
||||
}
|
||||
|
||||
/// Struct to help implementing TryToTokens over the key/value pairs from the hashmap.
|
||||
struct ModuleInIter<'a> {
|
||||
name: &'a Ident,
|
||||
module: &'a ast::Module
|
||||
}
|
||||
|
||||
impl<'a> From<(&'a Ident, &'a ast::Module)> for ModuleInIter<'a> {
|
||||
fn from((name, module): (&'a Ident, &'a ast::Module)) -> ModuleInIter<'a> {
|
||||
ModuleInIter { name, module }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryToTokens for ModuleInIter<'a> {
|
||||
impl<'a> TryToTokens for ast::Module {
|
||||
fn try_to_tokens(&self, tokens: &mut TokenStream) -> Result<(), Diagnostic> {
|
||||
let name = &self.name;
|
||||
let imports = &self.module.imports;
|
||||
let mut errors = Vec::new();
|
||||
for i in imports.iter() {
|
||||
DescribeImport(&i.kind).to_tokens(tokens);
|
||||
for import in &self.imports {
|
||||
DescribeImport(&import.kind).to_tokens(tokens);
|
||||
}
|
||||
let vis = &self.module.vis;
|
||||
let vis = &self.vis;
|
||||
let name = &self.name;
|
||||
let mut errors = Vec::new();
|
||||
let mut body = TokenStream::new();
|
||||
for i in imports.iter() {
|
||||
if let Err(e) = i.kind.try_to_tokens(&mut body) {
|
||||
for import in &self.imports {
|
||||
if let Err(e) = import.kind.try_to_tokens(&mut body) {
|
||||
errors.push(e);
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,8 @@
|
||||
use wasm_bindgen_test::*;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use web_sys::console;
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_console() {
|
||||
console::time("test label");
|
||||
console::time_end("test label");
|
||||
console::time_using_label("test label");
|
||||
console::time_end_using_label("test label");
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ use web_sys::{DomPoint, DomPointReadOnly};
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn dom_point() {
|
||||
let x = DomPoint::new(1.0, 2.0, 3.0, 4.0).unwrap();
|
||||
let x = DomPoint::new_using_x_and_y_and_z_and_w(1.0, 2.0, 3.0, 4.0).unwrap();
|
||||
assert_eq!(x.x(), 1.0);
|
||||
x.set_x(1.5);
|
||||
assert_eq!(x.x(), 1.5);
|
||||
@ -24,7 +24,7 @@ fn dom_point() {
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn dom_point_readonly() {
|
||||
let x = DomPoint::new(1.0, 2.0, 3.0, 4.0).unwrap();
|
||||
let x = DomPoint::new_using_x_and_y_and_z_and_w(1.0, 2.0, 3.0, 4.0).unwrap();
|
||||
let x = DomPointReadOnly::from(JsValue::from(x));
|
||||
assert_eq!(x.x(), 1.0);
|
||||
assert_eq!(x.y(), 2.0);
|
||||
|
@ -3,7 +3,7 @@ use web_sys::HtmlOptionElement;
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_option_element() {
|
||||
let option = HtmlOptionElement::new(
|
||||
let option = HtmlOptionElement::new_using_text_and_value_and_default_selected_and_selected(
|
||||
"option_text",
|
||||
"option_value",
|
||||
false,
|
||||
|
@ -99,7 +99,7 @@ fn test_table_element() {
|
||||
);
|
||||
|
||||
table
|
||||
.insert_row(0)
|
||||
.insert_row_using_index(0)
|
||||
.expect("Failed to insert row at index 0");
|
||||
assert!(
|
||||
table.rows().length() == 1,
|
||||
|
@ -26,13 +26,13 @@ typedef (Uint32Array or sequence<GLuint>) Uint32List;
|
||||
|
||||
// WebGL2 spec has this as an empty interface that pulls in everything
|
||||
// via WebGL2RenderingContextBase.
|
||||
[Pref="webgl.enable-webgl2"]
|
||||
[Exposed=(Window,Worker),
|
||||
Pref="webgl.enable-webgl2"]
|
||||
interface WebGL2RenderingContext
|
||||
{
|
||||
};
|
||||
|
||||
[NoInterfaceObject]
|
||||
interface WebGL2RenderingContextBase
|
||||
interface mixin WebGL2RenderingContextBase
|
||||
{
|
||||
const GLenum READ_BUFFER = 0x0C02;
|
||||
const GLenum UNPACK_ROW_LENGTH = 0x0CF2;
|
||||
@ -694,8 +694,8 @@ interface WebGL2RenderingContextBase
|
||||
void bindVertexArray(WebGLVertexArrayObject? array);
|
||||
};
|
||||
|
||||
WebGL2RenderingContextBase implements WebGLRenderingContextBase;
|
||||
WebGL2RenderingContext implements WebGL2RenderingContextBase;
|
||||
WebGL2RenderingContextBase includes WebGLRenderingContextBase;
|
||||
WebGL2RenderingContext includes WebGL2RenderingContextBase;
|
||||
|
||||
[NoInterfaceObject]
|
||||
interface EXT_color_buffer_float {
|
||||
|
@ -111,8 +111,7 @@ typedef (Int32Array or sequence<GLint>) Int32List;
|
||||
// WebGL2RenderingContext have in common. This doesn't have all the things they
|
||||
// have in common, because we don't support splitting multiple overloads of the
|
||||
// same method across separate interfaces and pulling them in with "implements".
|
||||
[Exposed=(Window, Worker), NoInterfaceObject]
|
||||
interface WebGLRenderingContextBase {
|
||||
interface mixin WebGLRenderingContextBase {
|
||||
/* ClearBufferMask */
|
||||
const GLenum DEPTH_BUFFER_BIT = 0x00000100;
|
||||
const GLenum STENCIL_BUFFER_BIT = 0x00000400;
|
||||
@ -802,7 +801,7 @@ interface WebGLRenderingContext {
|
||||
void uniformMatrix4fv(WebGLUniformLocation? location, GLboolean transpose, Float32List data);
|
||||
};
|
||||
|
||||
WebGLRenderingContext implements WebGLRenderingContextBase;
|
||||
WebGLRenderingContext includes WebGLRenderingContextBase;
|
||||
|
||||
// For OffscreenCanvas
|
||||
// Reference: https://wiki.whatwg.org/wiki/OffscreenCanvas
|
||||
|
@ -8,6 +8,6 @@ pub mod array;
|
||||
pub mod array_buffer;
|
||||
pub mod consts;
|
||||
pub mod enums;
|
||||
pub mod namespace;
|
||||
pub mod simple;
|
||||
pub mod throws;
|
||||
pub mod namespace;
|
||||
|
@ -1,11 +1,9 @@
|
||||
const strictEqual = require('assert').strictEqual;
|
||||
global.math_test = {
|
||||
pow(base, exp) {
|
||||
return Math.pow(base, exp);
|
||||
},
|
||||
|
||||
global.mathtest = {};
|
||||
|
||||
global.mathtest.powf = function powf(base, exp) {
|
||||
return Math.pow(base, exp);
|
||||
}
|
||||
|
||||
global.mathtest.add_one = function add_one(val) {
|
||||
return val + 1;
|
||||
}
|
||||
add_one(val) {
|
||||
return val + 1;
|
||||
},
|
||||
};
|
||||
|
@ -4,7 +4,7 @@ include!(concat!(env!("OUT_DIR"), "/namespace.rs"));
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn simple_namespace_test() {
|
||||
assert_eq!(mathtest::add_one(1), 2);
|
||||
assert_eq!(mathtest::powf(1.0, 100.0), 1.0);
|
||||
assert_eq!(mathtest::powf(10.0, 2.0), 100.0);
|
||||
assert_eq!(math_test::add_one(1), 2);
|
||||
assert_eq!(math_test::pow(1.0, 100.0), 1.0);
|
||||
assert_eq!(math_test::pow(10.0, 2.0), 100.0);
|
||||
}
|
||||
|
6
crates/webidl-tests/namespace.webidl
vendored
6
crates/webidl-tests/namespace.webidl
vendored
@ -1,4 +1,4 @@
|
||||
namespace mathtest {
|
||||
long add_one(long val);
|
||||
double powf(double base, double exponent);
|
||||
namespace math_test {
|
||||
long add_one(long val);
|
||||
double pow(double base, double exponent);
|
||||
};
|
||||
|
@ -67,7 +67,7 @@ global.UndefinedMethod = class UndefinedMethod {
|
||||
}
|
||||
};
|
||||
|
||||
global.OptionalMethod = class OptionalMethod {
|
||||
global.NullableMethod = class NullableMethod {
|
||||
constructor() {}
|
||||
opt(a) {
|
||||
if (a == undefined) {
|
||||
@ -107,6 +107,13 @@ global.Indexing = function () {
|
||||
});
|
||||
};
|
||||
|
||||
global.OptionalAndUnionArguments = class OptionalAndUnionArguments {
|
||||
constructor() {}
|
||||
m(a, b = true, c = 123, d = 456) {
|
||||
return [typeof a, a, typeof b, b, typeof c, c, typeof d, d].join(', ');
|
||||
}
|
||||
};
|
||||
|
||||
global.PartialInterface = class PartialInterface {
|
||||
get un() {
|
||||
return 1;
|
||||
|
@ -56,8 +56,8 @@ fn one_method_using_an_undefined_import_doesnt_break_all_other_methods() {
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn optional_method() {
|
||||
let f = OptionalMethod::new().unwrap();
|
||||
fn nullable_method() {
|
||||
let f = NullableMethod::new().unwrap();
|
||||
assert!(f.opt(Some(15)) == Some(16));
|
||||
assert!(f.opt(None) == None);
|
||||
}
|
||||
@ -78,6 +78,19 @@ fn indexing() {
|
||||
assert_eq!(f.get(123), -1);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn optional_and_union_arguments() {
|
||||
let f = OptionalAndUnionArguments::new().unwrap();
|
||||
assert_eq!(f.m_using_a("abc"), "string, abc, boolean, true, number, 123, number, 456");
|
||||
assert_eq!(f.m_using_a_and_b("abc", false), "string, abc, boolean, false, number, 123, number, 456");
|
||||
assert_eq!(f.m_using_dom_str_and_bool_and_i16("abc", false, 5), "string, abc, boolean, false, number, 5, number, 456");
|
||||
assert_eq!(f.m_using_dom_str_and_bool_and_dom_str("abc", false, "5"), "string, abc, boolean, false, string, 5, number, 456");
|
||||
assert_eq!(f.m_using_dom_str_and_bool_and_i16_and_opt_i64("abc", false, 5, Some(10)), "string, abc, boolean, false, number, 5, bigint, 10");
|
||||
assert_eq!(f.m_using_dom_str_and_bool_and_i16_and_opt_bool("abc", false, 5, Some(true)), "string, abc, boolean, false, number, 5, boolean, true");
|
||||
assert_eq!(f.m_using_dom_str_and_bool_and_dom_str_and_opt_i64("abc", false, "5", Some(10)), "string, abc, boolean, false, string, 5, bigint, 10");
|
||||
assert_eq!(f.m_using_dom_str_and_bool_and_dom_str_and_opt_bool("abc", false, "5", Some(true)), "string, abc, boolean, false, string, 5, boolean, true");
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn unforgeable_is_structural() {
|
||||
let f = Unforgeable::new().unwrap();
|
||||
|
12
crates/webidl-tests/simple.webidl
vendored
12
crates/webidl-tests/simple.webidl
vendored
@ -31,7 +31,7 @@ interface UndefinedMethod {
|
||||
};
|
||||
|
||||
[Constructor()]
|
||||
interface OptionalMethod {
|
||||
interface NullableMethod {
|
||||
octet? opt(short? a);
|
||||
};
|
||||
|
||||
@ -47,6 +47,16 @@ interface Indexing {
|
||||
deleter void (unsigned long index);
|
||||
};
|
||||
|
||||
[Constructor()]
|
||||
interface OptionalAndUnionArguments {
|
||||
DOMString m(
|
||||
DOMString a,
|
||||
optional boolean b = true,
|
||||
optional (short or DOMString) c = 123,
|
||||
optional (long long or boolean)? d = 456
|
||||
);
|
||||
};
|
||||
|
||||
[Constructor()]
|
||||
interface Unforgeable {
|
||||
[Unforgeable] readonly attribute short uno;
|
||||
|
@ -12,7 +12,8 @@ use std::collections::{BTreeMap, BTreeSet};
|
||||
use weedle::argument::Argument;
|
||||
use weedle::attribute::ExtendedAttribute;
|
||||
use weedle::interface::StringifierOrStatic;
|
||||
use weedle::mixin::MixinMembers;
|
||||
use weedle::mixin::MixinMember;
|
||||
use weedle::namespace::NamespaceMember;
|
||||
use weedle;
|
||||
|
||||
use super::Result;
|
||||
@ -26,9 +27,10 @@ pub(crate) struct FirstPassRecord<'src> {
|
||||
pub(crate) dictionaries: BTreeSet<&'src str>,
|
||||
pub(crate) enums: BTreeSet<&'src str>,
|
||||
/// The mixins, mapping their name to the webidl ast node for the mixin.
|
||||
pub(crate) mixins: BTreeMap<&'src str, Vec<&'src MixinMembers<'src>>>,
|
||||
pub(crate) mixins: BTreeMap<&'src str, MixinData<'src>>,
|
||||
pub(crate) typedefs: BTreeMap<&'src str, &'src weedle::types::Type<'src>>,
|
||||
pub(crate) namespaces: BTreeMap<&'src str, NamespaceData<'src>>,
|
||||
pub(crate) includes: BTreeMap<&'src str, BTreeSet<&'src str>>,
|
||||
}
|
||||
|
||||
/// We need to collect interface data during the first pass, to be used later.
|
||||
@ -41,31 +43,25 @@ pub(crate) struct InterfaceData<'src> {
|
||||
pub(crate) superclass: Option<&'src str>,
|
||||
}
|
||||
|
||||
/// We need to collect mixin data during the first pass, to be used later.
|
||||
#[derive(Default)]
|
||||
pub(crate) struct MixinData<'src> {
|
||||
/// Whether only partial mixins were encountered
|
||||
pub(crate) partial: bool,
|
||||
pub(crate) members: Vec<&'src MixinMember<'src>>,
|
||||
pub(crate) operations: BTreeMap<OperationId<'src>, OperationData<'src>>,
|
||||
}
|
||||
|
||||
/// We need to collect namespace data during the first pass, to be used later.
|
||||
#[derive(Default)]
|
||||
pub(crate) struct NamespaceData<'src> {
|
||||
/// Whether only partial namespaces were encountered
|
||||
pub(crate) partial: bool,
|
||||
pub(crate) operations: BTreeMap<Option<&'src str>, OperationData<'src>>,
|
||||
pub(crate) members: Vec<&'src NamespaceMember<'src>>,
|
||||
pub(crate) operations: BTreeMap<OperationId<'src>, OperationData<'src>>,
|
||||
}
|
||||
|
||||
impl<'src> NamespaceData<'src> {
|
||||
/// Creates an empty node for a non-partial namespace.
|
||||
pub(crate) fn empty_non_partial() -> Self {
|
||||
Self {
|
||||
partial: false,
|
||||
operations: Default::default(),
|
||||
}
|
||||
}
|
||||
/// Creates an empty node for a partial namespace.
|
||||
pub(crate) fn empty_partial() -> Self {
|
||||
Self {
|
||||
partial: true,
|
||||
operations: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
|
||||
pub(crate) enum OperationId<'src> {
|
||||
Constructor,
|
||||
Operation(Option<&'src str>),
|
||||
@ -104,6 +100,7 @@ impl<'src> FirstPass<'src, ()> for weedle::Definition<'src> {
|
||||
match self {
|
||||
Dictionary(dictionary) => dictionary.first_pass(record, ()),
|
||||
Enum(enum_) => enum_.first_pass(record, ()),
|
||||
IncludesStatement(includes) => includes.first_pass(record, ()),
|
||||
Interface(interface) => interface.first_pass(record, ()),
|
||||
PartialInterface(interface) => interface.first_pass(record, ()),
|
||||
InterfaceMixin(mixin) => mixin.first_pass(record, ()),
|
||||
@ -121,15 +118,24 @@ impl<'src> FirstPass<'src, ()> for weedle::Definition<'src> {
|
||||
|
||||
impl<'src> FirstPass<'src, ()> for weedle::DictionaryDefinition<'src> {
|
||||
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> {
|
||||
if util::is_chrome_only(&self.attributes) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if !record.dictionaries.insert(self.identifier.0) {
|
||||
warn!("encountered multiple dictionary declarations of {}", self.identifier.0);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'src> FirstPass<'src, ()> for weedle::EnumDefinition<'src> {
|
||||
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> {
|
||||
if util::is_chrome_only(&self.attributes) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if !record.enums.insert(self.identifier.0) {
|
||||
warn!("Encountered multiple enum declarations of {}", self.identifier.0);
|
||||
}
|
||||
@ -138,25 +144,66 @@ impl<'src> FirstPass<'src, ()> for weedle::EnumDefinition<'src> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper function to add an operation to an interface.
|
||||
fn first_pass_interface_operation<'src>(
|
||||
impl<'src> FirstPass<'src, ()> for weedle::IncludesStatementDefinition<'src> {
|
||||
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> {
|
||||
if util::is_chrome_only(&self.attributes) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
record
|
||||
.includes
|
||||
.entry(self.lhs_identifier.0)
|
||||
.or_insert_with(Default::default)
|
||||
.insert(self.rhs_identifier.0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum FirstPassOperationType {
|
||||
Interface,
|
||||
Mixin,
|
||||
Namespace,
|
||||
}
|
||||
|
||||
fn first_pass_operation<'src>(
|
||||
record: &mut FirstPassRecord<'src>,
|
||||
first_pass_operation_type: FirstPassOperationType,
|
||||
self_name: &'src str,
|
||||
id: OperationId<'src>,
|
||||
arguments: &[Argument<'src>],
|
||||
) -> Result<()> {
|
||||
) -> Result<()> {
|
||||
let mut names = Vec::with_capacity(arguments.len());
|
||||
for argument in arguments {
|
||||
match argument {
|
||||
Argument::Single(arg) => names.push(arg.identifier.0),
|
||||
Argument::Variadic(_) => return Ok(()),
|
||||
Argument::Single(single) => names.push(single.identifier.0),
|
||||
Argument::Variadic(variadic) => names.push(variadic.identifier.0),
|
||||
}
|
||||
}
|
||||
record
|
||||
.interfaces
|
||||
.get_mut(self_name)
|
||||
.unwrap()
|
||||
.operations
|
||||
match first_pass_operation_type{
|
||||
FirstPassOperationType::Interface => {
|
||||
&mut record
|
||||
.interfaces
|
||||
.get_mut(self_name)
|
||||
.expect(&format!("not found {} interface", self_name))
|
||||
.operations
|
||||
},
|
||||
FirstPassOperationType::Mixin => {
|
||||
&mut record
|
||||
.mixins
|
||||
.get_mut(self_name)
|
||||
.expect(&format!("not found {} mixin", self_name))
|
||||
.operations
|
||||
},
|
||||
FirstPassOperationType::Namespace => {
|
||||
&mut record
|
||||
.namespaces
|
||||
.get_mut(self_name)
|
||||
.expect(&format!("not found {} namesace", self_name))
|
||||
.operations
|
||||
},
|
||||
}
|
||||
.entry(id)
|
||||
.and_modify(|operation_data| operation_data.overloaded = true)
|
||||
.or_default()
|
||||
@ -170,17 +217,17 @@ fn first_pass_interface_operation<'src>(
|
||||
|
||||
impl<'src> FirstPass<'src, ()> for weedle::InterfaceDefinition<'src> {
|
||||
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> {
|
||||
if util::is_chrome_only(&self.attributes) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
{
|
||||
let interface = record
|
||||
let interface_data = record
|
||||
.interfaces
|
||||
.entry(self.identifier.0)
|
||||
.or_default();
|
||||
interface.partial = false;
|
||||
interface.superclass = self.inheritance.map(|s| s.identifier.0);
|
||||
}
|
||||
|
||||
if util::is_chrome_only(&self.attributes) {
|
||||
return Ok(())
|
||||
interface_data.partial = false;
|
||||
interface_data.superclass = self.inheritance.map(|s| s.identifier.0);
|
||||
}
|
||||
|
||||
if let Some(attrs) = &self.attributes {
|
||||
@ -199,22 +246,22 @@ impl<'src> FirstPass<'src, ()> for weedle::InterfaceDefinition<'src> {
|
||||
|
||||
impl<'src> FirstPass<'src, ()> for weedle::PartialInterfaceDefinition<'src> {
|
||||
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> {
|
||||
if util::is_chrome_only(&self.attributes) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
record
|
||||
.interfaces
|
||||
.entry(self.identifier.0)
|
||||
.or_insert_with(||
|
||||
InterfaceData {
|
||||
partial: true,
|
||||
operations: Default::default(),
|
||||
global: false,
|
||||
operations: Default::default(),
|
||||
superclass: None,
|
||||
},
|
||||
);
|
||||
|
||||
if util::is_chrome_only(&self.attributes) {
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
for member in &self.members.body {
|
||||
member.first_pass(record, self.identifier.0)?;
|
||||
}
|
||||
@ -227,16 +274,18 @@ impl<'src> FirstPass<'src, &'src str> for ExtendedAttribute<'src> {
|
||||
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str) -> Result<()> {
|
||||
match self {
|
||||
ExtendedAttribute::ArgList(list) if list.identifier.0 == "Constructor" => {
|
||||
first_pass_interface_operation(
|
||||
first_pass_operation(
|
||||
record,
|
||||
FirstPassOperationType::Interface,
|
||||
self_name,
|
||||
OperationId::Constructor,
|
||||
&list.args.body.list,
|
||||
)
|
||||
}
|
||||
ExtendedAttribute::NoArgs(name) if (name.0).0 == "Constructor" => {
|
||||
first_pass_interface_operation(
|
||||
first_pass_operation(
|
||||
record,
|
||||
FirstPassOperationType::Interface,
|
||||
self_name,
|
||||
OperationId::Constructor,
|
||||
&[],
|
||||
@ -245,8 +294,9 @@ impl<'src> FirstPass<'src, &'src str> for ExtendedAttribute<'src> {
|
||||
ExtendedAttribute::NamedArgList(list)
|
||||
if list.lhs_identifier.0 == "NamedConstructor" =>
|
||||
{
|
||||
first_pass_interface_operation(
|
||||
first_pass_operation(
|
||||
record,
|
||||
FirstPassOperationType::Interface,
|
||||
self_name,
|
||||
OperationId::Constructor,
|
||||
&list.args.body.list,
|
||||
@ -278,6 +328,10 @@ impl<'src> FirstPass<'src, &'src str> for weedle::interface::InterfaceMember<'sr
|
||||
|
||||
impl<'src> FirstPass<'src, &'src str> for weedle::interface::OperationInterfaceMember<'src> {
|
||||
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str) -> Result<()> {
|
||||
if util::is_chrome_only(&self.attributes) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if !self.specials.is_empty() && self.specials.len() != 1 {
|
||||
warn!("Unsupported webidl operation {:?}", self);
|
||||
return Ok(())
|
||||
@ -286,20 +340,17 @@ impl<'src> FirstPass<'src, &'src str> for weedle::interface::OperationInterfaceM
|
||||
warn!("Unsupported webidl operation {:?}", self);
|
||||
return Ok(())
|
||||
}
|
||||
first_pass_interface_operation(
|
||||
first_pass_operation(
|
||||
record,
|
||||
FirstPassOperationType::Interface,
|
||||
self_name,
|
||||
match self.identifier.map(|s| s.0) {
|
||||
None => match self.specials.get(0) {
|
||||
None => OperationId::Operation(None),
|
||||
Some(weedle::interface::Special::Getter(weedle::term::Getter))
|
||||
=> OperationId::IndexingGetter,
|
||||
Some(weedle::interface::Special::Setter(weedle::term::Setter))
|
||||
=> OperationId::IndexingSetter,
|
||||
Some(weedle::interface::Special::Deleter(weedle::term::Deleter))
|
||||
=> OperationId::IndexingDeleter,
|
||||
Some(weedle::interface::Special::LegacyCaller(weedle::term::LegacyCaller))
|
||||
=> return Ok(()),
|
||||
Some(weedle::interface::Special::Getter(_)) => OperationId::IndexingGetter,
|
||||
Some(weedle::interface::Special::Setter(_)) => OperationId::IndexingSetter,
|
||||
Some(weedle::interface::Special::Deleter(_)) => OperationId::IndexingDeleter,
|
||||
Some(weedle::interface::Special::LegacyCaller(_)) => return Ok(()),
|
||||
},
|
||||
Some(ref name) => OperationId::Operation(Some(name.clone())),
|
||||
},
|
||||
@ -310,26 +361,85 @@ impl<'src> FirstPass<'src, &'src str> for weedle::interface::OperationInterfaceM
|
||||
|
||||
impl<'src> FirstPass<'src, ()> for weedle::InterfaceMixinDefinition<'src>{
|
||||
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> {
|
||||
record
|
||||
.mixins
|
||||
.entry(self.identifier.0)
|
||||
.or_insert_with(Default::default)
|
||||
.push(&self.members.body);
|
||||
if util::is_chrome_only(&self.attributes) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
{
|
||||
let mixin_data = record
|
||||
.mixins
|
||||
.entry(self.identifier.0)
|
||||
.or_insert_with(Default::default);
|
||||
mixin_data.partial = false;
|
||||
mixin_data.members.extend(&self.members.body);
|
||||
}
|
||||
|
||||
for member in &self.members.body {
|
||||
member.first_pass(record, self.identifier.0)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'src> FirstPass<'src, ()> for weedle::PartialInterfaceMixinDefinition<'src> {
|
||||
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> {
|
||||
if util::is_chrome_only(&self.attributes) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
record
|
||||
.mixins
|
||||
.entry(self.identifier.0)
|
||||
.or_insert_with(Default::default)
|
||||
.push(&self.members.body);
|
||||
.or_insert_with(||
|
||||
MixinData {
|
||||
partial: true,
|
||||
members: Default::default(),
|
||||
operations: Default::default(),
|
||||
},
|
||||
)
|
||||
.members
|
||||
.extend(&self.members.body);
|
||||
|
||||
for member in &self.members.body {
|
||||
member.first_pass(record, self.identifier.0)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'src> FirstPass<'src, &'src str> for weedle::mixin::MixinMember<'src> {
|
||||
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str) -> Result<()> {
|
||||
match self {
|
||||
weedle::mixin::MixinMember::Operation(op) => {
|
||||
op.first_pass(record, self_name)
|
||||
}
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'src> FirstPass<'src, &'src str> for weedle::mixin::OperationMixinMember<'src> {
|
||||
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str) -> Result<()> {
|
||||
if util::is_chrome_only(&self.attributes) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if self.stringifier.is_some() {
|
||||
warn!("Unsupported webidl operation {:?}", self);
|
||||
return Ok(())
|
||||
}
|
||||
first_pass_operation(
|
||||
record,
|
||||
FirstPassOperationType::Mixin,
|
||||
self_name,
|
||||
OperationId::Operation(self.identifier.map(|s| s.0.clone())),
|
||||
&self.args.body.list,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'src> FirstPass<'src, ()> for weedle::TypedefDefinition<'src> {
|
||||
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> {
|
||||
if util::is_chrome_only(&self.attributes) {
|
||||
@ -346,17 +456,23 @@ impl<'src> FirstPass<'src, ()> for weedle::TypedefDefinition<'src> {
|
||||
|
||||
impl<'src> FirstPass<'src, ()> for weedle::NamespaceDefinition<'src> {
|
||||
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> {
|
||||
record
|
||||
.namespaces
|
||||
.entry(self.identifier.0)
|
||||
.and_modify(|entry| entry.partial = false)
|
||||
.or_insert_with(NamespaceData::empty_non_partial);
|
||||
|
||||
if util::is_chrome_only(&self.attributes) {
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
// We ignore all attributes.
|
||||
record
|
||||
.namespaces
|
||||
.entry(self.identifier.0)
|
||||
.and_modify(|namespace_data| namespace_data.partial = false)
|
||||
.or_insert_with(||
|
||||
NamespaceData {
|
||||
partial: true,
|
||||
members: Default::default(),
|
||||
operations: Default::default(),
|
||||
},
|
||||
)
|
||||
.members
|
||||
.extend(&self.members.body);
|
||||
|
||||
for member in &self.members.body {
|
||||
member.first_pass(record, self.identifier.0)?;
|
||||
@ -368,15 +484,17 @@ impl<'src> FirstPass<'src, ()> for weedle::NamespaceDefinition<'src> {
|
||||
|
||||
impl<'src> FirstPass<'src, ()> for weedle::PartialNamespaceDefinition<'src> {
|
||||
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> {
|
||||
record
|
||||
.namespaces
|
||||
.entry(self.identifier.0)
|
||||
.or_insert_with(NamespaceData::empty_partial);
|
||||
|
||||
if util::is_chrome_only(&self.attributes) {
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
record
|
||||
.namespaces
|
||||
.entry(self.identifier.0)
|
||||
.or_default()
|
||||
.members
|
||||
.extend(&self.members.body);
|
||||
|
||||
for member in &self.members.body {
|
||||
member.first_pass(record, self.identifier.0)?;
|
||||
}
|
||||
@ -386,13 +504,10 @@ impl<'src> FirstPass<'src, ()> for weedle::PartialNamespaceDefinition<'src> {
|
||||
}
|
||||
|
||||
impl<'src> FirstPass<'src, &'src str> for weedle::namespace::NamespaceMember<'src> {
|
||||
fn first_pass(&'src self,
|
||||
record: &mut FirstPassRecord<'src>,
|
||||
namespace_name: &'src str) -> Result<()>
|
||||
{
|
||||
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str) -> Result<()> {
|
||||
match self {
|
||||
weedle::namespace::NamespaceMember::Operation(op) => {
|
||||
op.first_pass(record, namespace_name)
|
||||
op.first_pass(record, self_name)
|
||||
}
|
||||
_ => Ok(()),
|
||||
}
|
||||
@ -400,32 +515,18 @@ impl<'src> FirstPass<'src, &'src str> for weedle::namespace::NamespaceMember<'sr
|
||||
}
|
||||
|
||||
impl<'src> FirstPass<'src, &'src str> for weedle::namespace::OperationNamespaceMember<'src> {
|
||||
fn first_pass(&'src self,
|
||||
record: &mut FirstPassRecord<'src>,
|
||||
namespace_name: &'src str) -> Result<()>
|
||||
{
|
||||
let identifier = self.identifier.map(|s| s.0);
|
||||
let arguments = &self.args.body.list;
|
||||
let mut names = Vec::with_capacity(arguments.len());
|
||||
for argument in arguments {
|
||||
match argument {
|
||||
Argument::Single(arg) => names.push(arg.identifier.0),
|
||||
Argument::Variadic(_) => return Ok(()),
|
||||
}
|
||||
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str) -> Result<()> {
|
||||
if util::is_chrome_only(&self.attributes) {
|
||||
return Ok(())
|
||||
}
|
||||
record
|
||||
.namespaces
|
||||
.get_mut(namespace_name)
|
||||
.unwrap() // call this after creating the namespace
|
||||
.operations
|
||||
.entry(identifier)
|
||||
.and_modify(|operation_data| operation_data.overloaded = true)
|
||||
.or_insert_with(Default::default)
|
||||
.argument_names_same
|
||||
.entry(names)
|
||||
.and_modify(|same_argument_names| *same_argument_names = true)
|
||||
.or_insert(false);
|
||||
Ok(())
|
||||
|
||||
first_pass_operation(
|
||||
record,
|
||||
FirstPassOperationType::Namespace,
|
||||
self_name,
|
||||
OperationId::Operation(self.identifier.map(|s| s.0.clone())),
|
||||
&self.args.body.list,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
675
crates/webidl/src/idl_type.rs
Normal file
675
crates/webidl/src/idl_type.rs
Normal file
@ -0,0 +1,675 @@
|
||||
use backend::util::{ident_ty, leading_colon_path_ty, raw_ident, rust_ident};
|
||||
use heck::SnakeCase;
|
||||
use syn;
|
||||
use weedle::common::Identifier;
|
||||
use weedle::term;
|
||||
use weedle::types::*;
|
||||
|
||||
use first_pass::FirstPassRecord;
|
||||
use util::{TypePosition, camel_case_ident, shared_ref, option_ty, array};
|
||||
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)]
|
||||
pub(crate) enum IdlType<'a> {
|
||||
Boolean,
|
||||
Byte,
|
||||
Octet,
|
||||
Short,
|
||||
UnsignedShort,
|
||||
Long,
|
||||
UnsignedLong,
|
||||
LongLong,
|
||||
UnsignedLongLong,
|
||||
Float,
|
||||
UnrestrictedFloat,
|
||||
Double,
|
||||
UnrestrictedDouble,
|
||||
DomString,
|
||||
ByteString,
|
||||
UsvString,
|
||||
Object,
|
||||
Symbol,
|
||||
Error,
|
||||
|
||||
ArrayBuffer,
|
||||
DataView,
|
||||
Int8Array,
|
||||
Uint8Array,
|
||||
Uint8ClampedArray,
|
||||
Int16Array,
|
||||
Uint16Array,
|
||||
Int32Array,
|
||||
Uint32Array,
|
||||
Float32Array,
|
||||
Float64Array,
|
||||
|
||||
Interface(&'a str),
|
||||
Dictionary(&'a str),
|
||||
Enum(&'a str),
|
||||
|
||||
Nullable(Box<IdlType<'a>>),
|
||||
FrozenArray(Box<IdlType<'a>>),
|
||||
Sequence(Box<IdlType<'a>>),
|
||||
Promise(Box<IdlType<'a>>),
|
||||
Record(Box<IdlType<'a>>, Box<IdlType<'a>>),
|
||||
Union(Vec<IdlType<'a>>),
|
||||
|
||||
Any,
|
||||
Void,
|
||||
}
|
||||
|
||||
pub(crate) trait ToIdlType<'a> {
|
||||
fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option<IdlType<'a>>;
|
||||
}
|
||||
|
||||
impl<'a> ToIdlType<'a> for UnionType<'a> {
|
||||
fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option<IdlType<'a>> {
|
||||
let mut idl_types = Vec::with_capacity(self.body.list.len());
|
||||
for t in &self.body.list {
|
||||
idl_types.push(t.to_idl_type(record)?);
|
||||
}
|
||||
Some(IdlType::Union(idl_types))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToIdlType<'a> for Type<'a> {
|
||||
fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option<IdlType<'a>> {
|
||||
match self {
|
||||
Type::Single(t) => t.to_idl_type(record),
|
||||
Type::Union(t) => t.to_idl_type(record),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToIdlType<'a> for SingleType<'a> {
|
||||
fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option<IdlType<'a>> {
|
||||
match self {
|
||||
SingleType::Any(t) => t.to_idl_type(record),
|
||||
SingleType::NonAny(t) => t.to_idl_type(record),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToIdlType<'a> for NonAnyType<'a> {
|
||||
fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option<IdlType<'a>> {
|
||||
match self {
|
||||
NonAnyType::Promise(t) => t.to_idl_type(record),
|
||||
NonAnyType::Integer(t) => t.to_idl_type(record),
|
||||
NonAnyType::FloatingPoint(t) => t.to_idl_type(record),
|
||||
NonAnyType::Boolean(t) => t.to_idl_type(record),
|
||||
NonAnyType::Byte(t) => t.to_idl_type(record),
|
||||
NonAnyType::Octet(t) => t.to_idl_type(record),
|
||||
NonAnyType::ByteString(t) => t.to_idl_type(record),
|
||||
NonAnyType::DOMString(t) => t.to_idl_type(record),
|
||||
NonAnyType::USVString(t) => t.to_idl_type(record),
|
||||
NonAnyType::Sequence(t) => t.to_idl_type(record),
|
||||
NonAnyType::Object(t) => t.to_idl_type(record),
|
||||
NonAnyType::Symbol(t) => t.to_idl_type(record),
|
||||
NonAnyType::Error(t) => t.to_idl_type(record),
|
||||
NonAnyType::ArrayBuffer(t) => t.to_idl_type(record),
|
||||
NonAnyType::DataView(t) => t.to_idl_type(record),
|
||||
NonAnyType::Int8Array(t) => t.to_idl_type(record),
|
||||
NonAnyType::Int16Array(t) => t.to_idl_type(record),
|
||||
NonAnyType::Int32Array(t) => t.to_idl_type(record),
|
||||
NonAnyType::Uint8Array(t) => t.to_idl_type(record),
|
||||
NonAnyType::Uint16Array(t) => t.to_idl_type(record),
|
||||
NonAnyType::Uint32Array(t) => t.to_idl_type(record),
|
||||
NonAnyType::Uint8ClampedArray(t) => t.to_idl_type(record),
|
||||
NonAnyType::Float32Array(t) => t.to_idl_type(record),
|
||||
NonAnyType::Float64Array(t) => t.to_idl_type(record),
|
||||
NonAnyType::FrozenArrayType(t) => t.to_idl_type(record),
|
||||
NonAnyType::RecordType(t) => t.to_idl_type(record),
|
||||
NonAnyType::Identifier(t) => t.to_idl_type(record),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToIdlType<'a> for SequenceType<'a> {
|
||||
fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option<IdlType<'a>> {
|
||||
Some(IdlType::Sequence(Box::new(self.generics.body.to_idl_type(record)?)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToIdlType<'a> for FrozenArrayType<'a> {
|
||||
fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option<IdlType<'a>> {
|
||||
Some(IdlType::FrozenArray(Box::new(self.generics.body.to_idl_type(record)?)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ToIdlType<'a>> ToIdlType<'a> for MayBeNull<T> {
|
||||
fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option<IdlType<'a>> {
|
||||
let inner_idl_type = self.type_.to_idl_type(record)?;
|
||||
if self.q_mark.is_some() {
|
||||
Some(IdlType::Nullable(Box::new(inner_idl_type)))
|
||||
} else {
|
||||
Some(inner_idl_type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToIdlType<'a> for PromiseType<'a> {
|
||||
fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option<IdlType<'a>> {
|
||||
Some(IdlType::Promise(Box::new(self.generics.body.to_idl_type(record)?)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToIdlType<'a> for IntegerType {
|
||||
fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option<IdlType<'a>> {
|
||||
match self {
|
||||
IntegerType::LongLong(t) => t.to_idl_type(record),
|
||||
IntegerType::Long(t) => t.to_idl_type(record),
|
||||
IntegerType::Short(t) => t.to_idl_type(record),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToIdlType<'a> for LongLongType {
|
||||
fn to_idl_type(&self, _record: &FirstPassRecord<'a>) -> Option<IdlType<'a>> {
|
||||
if self.unsigned.is_some() {
|
||||
Some(IdlType::UnsignedLongLong)
|
||||
} else {
|
||||
Some(IdlType::LongLong)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToIdlType<'a> for LongType {
|
||||
fn to_idl_type(&self, _record: &FirstPassRecord<'a>) -> Option<IdlType<'a>> {
|
||||
if self.unsigned.is_some() {
|
||||
Some(IdlType::UnsignedLong)
|
||||
} else {
|
||||
Some(IdlType::Long)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToIdlType<'a> for ShortType {
|
||||
fn to_idl_type(&self, _record: &FirstPassRecord<'a>) -> Option<IdlType<'a>> {
|
||||
if self.unsigned.is_some() {
|
||||
Some(IdlType::UnsignedShort)
|
||||
} else {
|
||||
Some(IdlType::Short)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToIdlType<'a> for FloatingPointType {
|
||||
fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option<IdlType<'a>> {
|
||||
match self {
|
||||
FloatingPointType::Float(t) => t.to_idl_type(record),
|
||||
FloatingPointType::Double(t) => t.to_idl_type(record),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToIdlType<'a> for FloatType {
|
||||
fn to_idl_type(&self, _record: &FirstPassRecord<'a>) -> Option<IdlType<'a>> {
|
||||
if self.unrestricted.is_some() {
|
||||
Some(IdlType::UnrestrictedFloat)
|
||||
} else {
|
||||
Some(IdlType::Float)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToIdlType<'a> for DoubleType {
|
||||
fn to_idl_type(&self, _record: &FirstPassRecord<'a>) -> Option<IdlType<'a>> {
|
||||
if self.unrestricted.is_some() {
|
||||
Some(IdlType::UnrestrictedDouble)
|
||||
} else {
|
||||
Some(IdlType::Double)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToIdlType<'a> for RecordType<'a> {
|
||||
fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option<IdlType<'a>> {
|
||||
Some(
|
||||
IdlType::Record(
|
||||
Box::new(self.generics.body.0.to_idl_type(record)?),
|
||||
Box::new(self.generics.body.2.to_idl_type(record)?)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToIdlType<'a> for StringType {
|
||||
fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option<IdlType<'a>> {
|
||||
match self {
|
||||
StringType::Byte(t) => t.to_idl_type(record),
|
||||
StringType::DOM(t) => t.to_idl_type(record),
|
||||
StringType::USV(t) => t.to_idl_type(record),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToIdlType<'a> for UnionMemberType<'a> {
|
||||
fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option<IdlType<'a>> {
|
||||
match self {
|
||||
UnionMemberType::Single(t) => t.to_idl_type(record),
|
||||
UnionMemberType::Union(t) => t.to_idl_type(record),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToIdlType<'a> for ConstType<'a> {
|
||||
fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option<IdlType<'a>> {
|
||||
match self {
|
||||
ConstType::Integer(t) => t.to_idl_type(record),
|
||||
ConstType::FloatingPoint(t) => t.to_idl_type(record),
|
||||
ConstType::Boolean(t) => t.to_idl_type(record),
|
||||
ConstType::Byte(t) => t.to_idl_type(record),
|
||||
ConstType::Octet(t) => t.to_idl_type(record),
|
||||
ConstType::Identifier(t) => t.to_idl_type(record),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToIdlType<'a> for ReturnType<'a> {
|
||||
fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option<IdlType<'a>> {
|
||||
match self {
|
||||
ReturnType::Void(t) => t.to_idl_type(record),
|
||||
ReturnType::Type(t) => t.to_idl_type(record),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToIdlType<'a> for AttributedType<'a> {
|
||||
fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option<IdlType<'a>> {
|
||||
self.type_.to_idl_type(record)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToIdlType<'a> for Identifier<'a> {
|
||||
fn to_idl_type(&self, record: &FirstPassRecord<'a>) -> Option<IdlType<'a>> {
|
||||
if let Some(idl_type) = record.typedefs.get(&self.0) {
|
||||
idl_type.to_idl_type(record)
|
||||
} else if record.interfaces.contains_key(self.0) {
|
||||
Some(IdlType::Interface(self.0))
|
||||
} else if record.dictionaries.contains(self.0) {
|
||||
Some(IdlType::Dictionary(self.0))
|
||||
} else if record.enums.contains(self.0) {
|
||||
Some(IdlType::Enum(self.0))
|
||||
} else {
|
||||
warn!("unrecognized type {}", self.0);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! terms_to_idl_type {
|
||||
($($t:tt => $r:tt)*) => ($(
|
||||
impl<'a> ToIdlType<'a> for term::$t {
|
||||
fn to_idl_type(&self, _record: &FirstPassRecord<'a>) -> Option<IdlType<'a>> {
|
||||
Some(IdlType::$r)
|
||||
}
|
||||
}
|
||||
)*)
|
||||
}
|
||||
|
||||
terms_to_idl_type! {
|
||||
Symbol => Symbol
|
||||
ByteString => ByteString
|
||||
DOMString => DomString
|
||||
USVString => UsvString
|
||||
Any => Any
|
||||
Boolean => Boolean
|
||||
Byte => Byte
|
||||
Double => Double
|
||||
Float => Float
|
||||
Long => Long
|
||||
Object => Object
|
||||
Octet => Octet
|
||||
Short => Short
|
||||
Void => Void
|
||||
ArrayBuffer => ArrayBuffer
|
||||
DataView => DataView
|
||||
Int8Array => Int8Array
|
||||
Int16Array => Int16Array
|
||||
Int32Array => Int32Array
|
||||
Uint8Array => Uint8Array
|
||||
Uint16Array => Uint16Array
|
||||
Uint32Array => Uint32Array
|
||||
Uint8ClampedArray => Uint8ClampedArray
|
||||
Float32Array => Float32Array
|
||||
Float64Array => Float64Array
|
||||
Error => Error
|
||||
}
|
||||
|
||||
impl<'a> IdlType<'a> {
|
||||
/// Generates a snake case type name.
|
||||
pub(crate) fn push_type_name(&self, dst: &mut String) {
|
||||
match self {
|
||||
IdlType::Boolean => dst.push_str("bool"),
|
||||
IdlType::Byte => dst.push_str("i8"),
|
||||
IdlType::Octet => dst.push_str("u8"),
|
||||
IdlType::Short => dst.push_str("i16"),
|
||||
IdlType::UnsignedShort => dst.push_str("u16"),
|
||||
IdlType::Long => dst.push_str("i32"),
|
||||
IdlType::UnsignedLong => dst.push_str("u32"),
|
||||
IdlType::LongLong => dst.push_str("i64"),
|
||||
IdlType::UnsignedLongLong => dst.push_str("u64"),
|
||||
IdlType::Float => dst.push_str("f32"),
|
||||
IdlType::UnrestrictedFloat => dst.push_str("unrestricted_f32"),
|
||||
IdlType::Double => dst.push_str("f64"),
|
||||
IdlType::UnrestrictedDouble => dst.push_str("unrestricted_f64"),
|
||||
IdlType::DomString => dst.push_str("dom_str"),
|
||||
IdlType::ByteString => dst.push_str("byte_str"),
|
||||
IdlType::UsvString => dst.push_str("usv_str"),
|
||||
IdlType::Object => dst.push_str("object"),
|
||||
IdlType::Symbol => dst.push_str("symbol"),
|
||||
IdlType::Error => dst.push_str("error"),
|
||||
|
||||
IdlType::ArrayBuffer => dst.push_str("array_buffer"),
|
||||
IdlType::DataView => dst.push_str("data_view"),
|
||||
IdlType::Int8Array => dst.push_str("i8_array"),
|
||||
IdlType::Uint8Array => dst.push_str("u8_array"),
|
||||
IdlType::Uint8ClampedArray => dst.push_str("u8_clamped_array"),
|
||||
IdlType::Int16Array => dst.push_str("i16_array"),
|
||||
IdlType::Uint16Array => dst.push_str("u16_array"),
|
||||
IdlType::Int32Array => dst.push_str("i32_array"),
|
||||
IdlType::Uint32Array => dst.push_str("u32_array"),
|
||||
IdlType::Float32Array => dst.push_str("f32_array"),
|
||||
IdlType::Float64Array => dst.push_str("f64_array"),
|
||||
|
||||
IdlType::Interface(name) => dst.push_str(&name.to_snake_case()),
|
||||
IdlType::Dictionary(name) => dst.push_str(&name.to_snake_case()),
|
||||
IdlType::Enum(name) => dst.push_str(&name.to_snake_case()),
|
||||
|
||||
IdlType::Nullable(idl_type) => {
|
||||
dst.push_str("opt_");
|
||||
idl_type.push_type_name(dst);
|
||||
},
|
||||
IdlType::FrozenArray(idl_type) => {
|
||||
idl_type.push_type_name(dst);
|
||||
dst.push_str("_frozen_array");
|
||||
},
|
||||
IdlType::Sequence(idl_type) => {
|
||||
idl_type.push_type_name(dst);
|
||||
dst.push_str("_sequence");
|
||||
},
|
||||
IdlType::Promise(idl_type) => {
|
||||
idl_type.push_type_name(dst);
|
||||
dst.push_str("_promise");
|
||||
},
|
||||
IdlType::Record(idl_type_from, idl_type_to) => {
|
||||
dst.push_str("record_from_");
|
||||
idl_type_from.push_type_name(dst);
|
||||
dst.push_str("_to_");
|
||||
idl_type_to.push_type_name(dst);
|
||||
},
|
||||
IdlType::Union(idl_types) => {
|
||||
dst.push_str("union_of_");
|
||||
let mut first = true;
|
||||
for idl_type in idl_types {
|
||||
if first {
|
||||
first = false;
|
||||
} else {
|
||||
dst.push_str("_and_");
|
||||
}
|
||||
idl_type.push_type_name(dst);
|
||||
}
|
||||
},
|
||||
|
||||
IdlType::Any => dst.push_str("any"),
|
||||
IdlType::Void => dst.push_str("void"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates a snake case type name.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn get_type_name(&self) -> String {
|
||||
let mut string = String::new();
|
||||
self.push_type_name(&mut string);
|
||||
return string;
|
||||
}
|
||||
|
||||
/// Converts to syn type if possible.
|
||||
pub(crate) fn to_syn_type(&self, pos: TypePosition) -> Option<syn::Type> {
|
||||
match self {
|
||||
IdlType::Boolean => Some(ident_ty(raw_ident("bool"))),
|
||||
IdlType::Byte => Some(ident_ty(raw_ident("i8"))),
|
||||
IdlType::Octet => Some(ident_ty(raw_ident("u8"))),
|
||||
IdlType::Short => Some(ident_ty(raw_ident("i16"))),
|
||||
IdlType::UnsignedShort => Some(ident_ty(raw_ident("u16"))),
|
||||
IdlType::Long => Some(ident_ty(raw_ident("i32"))),
|
||||
IdlType::UnsignedLong => Some(ident_ty(raw_ident("u32"))),
|
||||
IdlType::LongLong => Some(ident_ty(raw_ident("i64"))),
|
||||
IdlType::UnsignedLongLong => Some(ident_ty(raw_ident("u64"))),
|
||||
IdlType::Float => Some(ident_ty(raw_ident("f32"))),
|
||||
IdlType::UnrestrictedFloat => Some(ident_ty(raw_ident("f32"))),
|
||||
IdlType::Double => Some(ident_ty(raw_ident("f64"))),
|
||||
IdlType::UnrestrictedDouble => Some(ident_ty(raw_ident("f64"))),
|
||||
| IdlType::DomString
|
||||
| IdlType::ByteString
|
||||
| IdlType::UsvString => match pos {
|
||||
TypePosition::Argument => Some(shared_ref(ident_ty(raw_ident("str")))),
|
||||
TypePosition::Return => Some(ident_ty(raw_ident("String"))),
|
||||
},
|
||||
IdlType::Object => {
|
||||
let path = vec![rust_ident("js_sys"), rust_ident("Object")];
|
||||
Some(leading_colon_path_ty(path))
|
||||
},
|
||||
IdlType::Symbol => None,
|
||||
IdlType::Error => None,
|
||||
|
||||
IdlType::ArrayBuffer => {
|
||||
let path = vec![rust_ident("js_sys"), rust_ident("ArrayBuffer")];
|
||||
Some(leading_colon_path_ty(path))
|
||||
},
|
||||
IdlType::DataView => None,
|
||||
IdlType::Int8Array => Some(array("i8", pos)),
|
||||
IdlType::Uint8Array => Some(array("u8", pos)),
|
||||
IdlType::Uint8ClampedArray => Some(array("u8", pos)),
|
||||
IdlType::Int16Array => Some(array("i16", pos)),
|
||||
IdlType::Uint16Array => Some(array("u16", pos)),
|
||||
IdlType::Int32Array => Some(array("i32", pos)),
|
||||
IdlType::Uint32Array => Some(array("u32", pos)),
|
||||
IdlType::Float32Array => Some(array("f32", pos)),
|
||||
IdlType::Float64Array => Some(array("f64", pos)),
|
||||
|
||||
IdlType::Interface(name) => {
|
||||
let ty = ident_ty(rust_ident(camel_case_ident(name).as_str()));
|
||||
if pos == TypePosition::Argument {
|
||||
Some(shared_ref(ty))
|
||||
} else {
|
||||
Some(ty)
|
||||
}
|
||||
},
|
||||
IdlType::Dictionary(name) => Some(ident_ty(rust_ident(camel_case_ident(name).as_str()))),
|
||||
IdlType::Enum(name) => Some(ident_ty(rust_ident(camel_case_ident(name).as_str()))),
|
||||
|
||||
IdlType::Nullable(idl_type) => Some(option_ty(idl_type.to_syn_type(pos)?)),
|
||||
IdlType::FrozenArray(_idl_type) => None,
|
||||
IdlType::Sequence(_idl_type) => None,
|
||||
IdlType::Promise(_idl_type) => None,
|
||||
IdlType::Record(_idl_type_from, _idl_type_to) => None,
|
||||
IdlType::Union(_idl_types) => None,
|
||||
|
||||
IdlType::Any => {
|
||||
let path = vec![rust_ident("wasm_bindgen"), rust_ident("JsValue")];
|
||||
Some(leading_colon_path_ty(path))
|
||||
},
|
||||
IdlType::Void => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Flattens unions recursively.
|
||||
///
|
||||
/// Works similarly to [flattened union member types],
|
||||
/// but also flattens unions inside generics of other types.
|
||||
///
|
||||
/// [flattened union member types]: https://heycam.github.io/webidl/#dfn-flattened-union-member-types
|
||||
pub(crate) fn flatten(&self) -> Vec<Self> {
|
||||
match self {
|
||||
IdlType::Nullable(idl_type) => idl_type
|
||||
.flatten()
|
||||
.into_iter()
|
||||
.map(Box::new)
|
||||
.map(IdlType::Nullable)
|
||||
.collect(),
|
||||
IdlType::FrozenArray(idl_type) => idl_type
|
||||
.flatten()
|
||||
.into_iter()
|
||||
.map(Box::new)
|
||||
.map(IdlType::FrozenArray)
|
||||
.collect(),
|
||||
IdlType::Sequence(idl_type) => idl_type
|
||||
.flatten()
|
||||
.into_iter()
|
||||
.map(Box::new)
|
||||
.map(IdlType::Sequence)
|
||||
.collect(),
|
||||
IdlType::Promise(idl_type) => idl_type
|
||||
.flatten()
|
||||
.into_iter()
|
||||
.map(Box::new)
|
||||
.map(IdlType::Promise)
|
||||
.collect(),
|
||||
IdlType::Record(idl_type_from, idl_type_to) => {
|
||||
let mut idl_types = Vec::new();
|
||||
for idl_type_from in idl_type_from.flatten() {
|
||||
for idl_type_to in idl_type_to.flatten() {
|
||||
idl_types.push(
|
||||
IdlType::Record(
|
||||
Box::new(idl_type_from.clone()),
|
||||
Box::new(idl_type_to.clone())
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
idl_types
|
||||
},
|
||||
IdlType::Union(idl_types) => idl_types
|
||||
.iter()
|
||||
.flat_map(|idl_type| idl_type.flatten())
|
||||
.collect(),
|
||||
|
||||
idl_type @ _ => vec![idl_type.clone()]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn idl_type_flatten_test() {
|
||||
use self::IdlType::*;
|
||||
|
||||
assert_eq!(
|
||||
Union(vec![
|
||||
Interface("Node"),
|
||||
Union(vec![
|
||||
Sequence(
|
||||
Box::new(Long),
|
||||
),
|
||||
Interface("Event"),
|
||||
]),
|
||||
Nullable(
|
||||
Box::new(Union(vec![
|
||||
Interface("XMLHttpRequest"),
|
||||
DomString,
|
||||
])),
|
||||
),
|
||||
Sequence(
|
||||
Box::new(Union(vec![
|
||||
Sequence(
|
||||
Box::new(Double),
|
||||
),
|
||||
Interface("NodeList"),
|
||||
])),
|
||||
),
|
||||
]).flatten(),
|
||||
vec![
|
||||
Interface("Node"),
|
||||
Sequence(Box::new(Long)),
|
||||
Interface("Event"),
|
||||
Nullable(Box::new(Interface("XMLHttpRequest"))),
|
||||
Nullable(Box::new(DomString)),
|
||||
Sequence(Box::new(Sequence(Box::new(Double)))),
|
||||
Sequence(Box::new(Interface("NodeList"))),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// Converts arguments into possibilities.
|
||||
///
|
||||
/// Each argument represented with a tuple of its idl type and whether it is optional.
|
||||
/// Each possibility is a vector of idl types.
|
||||
///
|
||||
/// The goal is to find equivalent possibilities of argument types each of which is not optional and
|
||||
/// does not contains union types.
|
||||
pub(crate) fn flatten<'a>(arguments: &'a [(IdlType, bool)]) -> Vec<Vec<IdlType<'a>>> {
|
||||
if arguments.is_empty() {
|
||||
return vec![Vec::new()];
|
||||
}
|
||||
let mut optional_possibilities = if arguments[0].1 { vec![Vec::new()] } else { Vec::new() };
|
||||
let mut possibilities = Vec::new();
|
||||
for idl_type in arguments[0].0.flatten() {
|
||||
possibilities.push(vec![idl_type])
|
||||
}
|
||||
for argument in arguments[1..].iter() {
|
||||
let mut new_possibilities = Vec::new();
|
||||
for old_idl_types in possibilities {
|
||||
if argument.1 {
|
||||
optional_possibilities.push(old_idl_types.clone());
|
||||
}
|
||||
for idl_type in argument.0.flatten() {
|
||||
let mut new_idl_types = old_idl_types.clone();
|
||||
new_idl_types.push(idl_type);
|
||||
new_possibilities.push(new_idl_types)
|
||||
}
|
||||
}
|
||||
possibilities = new_possibilities;
|
||||
}
|
||||
optional_possibilities.extend(possibilities.into_iter());
|
||||
optional_possibilities
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn arguments_flatten_test() {
|
||||
use self::IdlType::*;
|
||||
|
||||
assert_eq!(
|
||||
flatten(
|
||||
&vec![
|
||||
(
|
||||
Union(vec![
|
||||
Short,
|
||||
Long,
|
||||
]),
|
||||
false,
|
||||
),
|
||||
(
|
||||
Union(vec![
|
||||
Sequence(Box::new(
|
||||
Union(vec![
|
||||
Byte,
|
||||
Octet,
|
||||
]),
|
||||
)),
|
||||
LongLong,
|
||||
]),
|
||||
true,
|
||||
),
|
||||
(
|
||||
DomString,
|
||||
true,
|
||||
)
|
||||
]
|
||||
),
|
||||
vec![
|
||||
vec![Short],
|
||||
vec![Long],
|
||||
vec![Short, Sequence(Box::new(Byte))],
|
||||
vec![Short, Sequence(Box::new(Octet))],
|
||||
vec![Short, LongLong],
|
||||
vec![Long, Sequence(Box::new(Byte))],
|
||||
vec![Long, Sequence(Box::new(Octet))],
|
||||
vec![Long, LongLong],
|
||||
vec![Short, Sequence(Box::new(Byte)), DomString],
|
||||
vec![Short, Sequence(Box::new(Octet)), DomString],
|
||||
vec![Short, LongLong, DomString],
|
||||
vec![Long, Sequence(Box::new(Byte)), DomString],
|
||||
vec![Long, Sequence(Box::new(Octet)), DomString],
|
||||
vec![Long, LongLong, DomString]
|
||||
],
|
||||
);
|
||||
}
|
@ -25,6 +25,7 @@ extern crate wasm_bindgen_backend as backend;
|
||||
extern crate weedle;
|
||||
|
||||
mod first_pass;
|
||||
mod idl_type;
|
||||
mod util;
|
||||
mod error;
|
||||
|
||||
@ -45,7 +46,7 @@ use weedle::attribute::{ExtendedAttribute, ExtendedAttributeList};
|
||||
|
||||
use first_pass::{FirstPass, FirstPassRecord};
|
||||
use util::{public, webidl_const_v_to_backend_const_v, TypePosition, camel_case_ident, mdn_doc};
|
||||
use util::ToSynType;
|
||||
use idl_type::{IdlType, ToIdlType};
|
||||
|
||||
pub use error::{Error, ErrorKind, Result};
|
||||
|
||||
@ -168,18 +169,17 @@ impl<'src> WebidlParse<'src, ()> for weedle::Definition<'src> {
|
||||
weedle::Definition::Enum(enumeration) => {
|
||||
enumeration.webidl_parse(program, first_pass, ())?
|
||||
}
|
||||
weedle::Definition::IncludesStatement(includes) => {
|
||||
includes.webidl_parse(program, first_pass, ())?
|
||||
}
|
||||
weedle::Definition::Interface(interface) => {
|
||||
interface.webidl_parse(program, first_pass, ())?
|
||||
}
|
||||
weedle::Definition::PartialInterface(interface) => {
|
||||
interface.webidl_parse(program, first_pass, ())?
|
||||
}
|
||||
weedle::Definition::Typedef(_) |
|
||||
weedle::Definition::InterfaceMixin(_) |
|
||||
weedle::Definition::PartialInterfaceMixin(_) => {
|
||||
| weedle::Definition::Typedef(_)
|
||||
| weedle::Definition::InterfaceMixin(_)
|
||||
| weedle::Definition::PartialInterfaceMixin(_)
|
||||
| weedle::Definition::IncludesStatement(..)
|
||||
| weedle::Definition::PartialNamespace(..)=> {
|
||||
// handled in the first pass
|
||||
}
|
||||
weedle::Definition::Implements(..) => {
|
||||
@ -188,11 +188,8 @@ impl<'src> WebidlParse<'src, ()> for weedle::Definition<'src> {
|
||||
weedle::Definition::Namespace(namespace) => {
|
||||
namespace.webidl_parse(program, first_pass, ())?
|
||||
}
|
||||
weedle::Definition::PartialNamespace(namespace) => {
|
||||
namespace.webidl_parse(program, first_pass, ())?
|
||||
}
|
||||
// TODO
|
||||
weedle::Definition::Callback(..)
|
||||
| weedle::Definition::Callback(..)
|
||||
| weedle::Definition::CallbackInterface(..)
|
||||
| weedle::Definition::Dictionary(..)
|
||||
| weedle::Definition::PartialDictionary(..) => {
|
||||
@ -203,25 +200,6 @@ impl<'src> WebidlParse<'src, ()> for weedle::Definition<'src> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'src> WebidlParse<'src, ()> for weedle::IncludesStatementDefinition<'src> {
|
||||
fn webidl_parse(
|
||||
&'src self,
|
||||
program: &mut backend::ast::Program,
|
||||
first_pass: &FirstPassRecord<'src>,
|
||||
(): (),
|
||||
) -> Result<()> {
|
||||
match first_pass.mixins.get(self.rhs_identifier.0) {
|
||||
Some(member_lists) => {
|
||||
for member in member_lists.iter().flat_map(|list| list.iter()) {
|
||||
member.webidl_parse(program, first_pass, self.lhs_identifier.0)?;
|
||||
}
|
||||
}
|
||||
None => warn!("Tried to include missing mixin {}", self.rhs_identifier.0),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'src> WebidlParse<'src, ()> for weedle::InterfaceDefinition<'src> {
|
||||
fn webidl_parse(
|
||||
&'src self,
|
||||
@ -265,10 +243,31 @@ impl<'src> WebidlParse<'src, ()> for weedle::InterfaceDefinition<'src> {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse<'src>(
|
||||
program: &mut backend::ast::Program,
|
||||
first_pass: &FirstPassRecord<'src>,
|
||||
self_name: &str,
|
||||
mixin_name: &str,
|
||||
) -> Result<()> {
|
||||
if let Some(mixin_data) = first_pass.mixins.get(mixin_name) {
|
||||
for member in &mixin_data.members {
|
||||
member.webidl_parse(program, first_pass, self_name)?;
|
||||
}
|
||||
}
|
||||
if let Some(mixin_names) = first_pass.includes.get(mixin_name) {
|
||||
for mixin_name in mixin_names {
|
||||
parse(program, first_pass, self_name, mixin_name)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
for member in &self.members.body {
|
||||
member.webidl_parse(program, first_pass, self.identifier.0)?;
|
||||
}
|
||||
|
||||
parse(program, first_pass, self.identifier.0, self.identifier.0)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -284,7 +283,11 @@ impl<'src> WebidlParse<'src, ()> for weedle::PartialInterfaceDefinition<'src> {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if !first_pass.interfaces.contains_key(self.identifier.0) {
|
||||
if first_pass
|
||||
.interfaces
|
||||
.get(self.identifier.0)
|
||||
.map(|interface_data| !interface_data.partial)
|
||||
.unwrap_or(true) {
|
||||
warn!(
|
||||
"Partial interface {} missing non-partial interface",
|
||||
self.identifier.0
|
||||
@ -307,10 +310,11 @@ impl<'src> WebidlParse<'src, &'src weedle::InterfaceDefinition<'src>> for Extend
|
||||
interface: &'src weedle::InterfaceDefinition<'src>,
|
||||
) -> Result<()> {
|
||||
let mut add_constructor = |arguments: &[Argument], class: &str| {
|
||||
let (overloaded, same_argument_names) = first_pass.get_method_overloading(
|
||||
let (overloaded, same_argument_names) = first_pass.get_operation_overloading(
|
||||
arguments,
|
||||
&::first_pass::OperationId::Constructor,
|
||||
interface.identifier.0,
|
||||
false,
|
||||
);
|
||||
|
||||
let self_ty = ident_ty(rust_ident(camel_case_ident(interface.identifier.0).as_str()));
|
||||
@ -337,20 +341,22 @@ impl<'src> WebidlParse<'src, &'src weedle::InterfaceDefinition<'src>> for Extend
|
||||
// > exception**.
|
||||
let throws = true;
|
||||
|
||||
first_pass
|
||||
.create_function(
|
||||
"new",
|
||||
overloaded,
|
||||
same_argument_names,
|
||||
arguments,
|
||||
Some(self_ty),
|
||||
kind,
|
||||
structural,
|
||||
throws,
|
||||
None,
|
||||
)
|
||||
.map(wrap_import_function)
|
||||
.map(|import| program.imports.push(import));
|
||||
for import_function in first_pass.create_function(
|
||||
"new",
|
||||
overloaded,
|
||||
same_argument_names,
|
||||
&match first_pass.convert_arguments(arguments) {
|
||||
None => return,
|
||||
Some(arguments) => arguments
|
||||
},
|
||||
IdlType::Interface(interface.identifier.0),
|
||||
kind,
|
||||
structural,
|
||||
throws,
|
||||
None,
|
||||
) {
|
||||
program.imports.push(wrap_import_function(import_function));
|
||||
}
|
||||
};
|
||||
|
||||
match self {
|
||||
@ -440,8 +446,8 @@ impl<'src> WebidlParse<'src, &'src str> for weedle::interface::InterfaceMember<'
|
||||
Operation(op) => {
|
||||
op.webidl_parse(program, first_pass, self_name)
|
||||
}
|
||||
Const(cnst) => {
|
||||
cnst.webidl_parse(program, first_pass, self_name)
|
||||
Const(const_) => {
|
||||
const_.webidl_parse(program, first_pass, self_name)
|
||||
}
|
||||
Iterable(iterable) => {
|
||||
iterable.webidl_parse(program, first_pass, self_name)
|
||||
@ -541,7 +547,7 @@ fn member_attribute<'src>(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let statik = match modifier {
|
||||
let is_static = match modifier {
|
||||
Some(Stringifier(_)) => {
|
||||
warn!("Unsupported stringifier on type {:?}", (self_name, identifier));
|
||||
return Ok(())
|
||||
@ -559,30 +565,28 @@ fn member_attribute<'src>(
|
||||
let is_structural = util::is_structural(attrs);
|
||||
let throws = util::throws(attrs);
|
||||
|
||||
first_pass
|
||||
.create_getter(
|
||||
identifier,
|
||||
&type_.type_,
|
||||
self_name,
|
||||
statik,
|
||||
is_structural,
|
||||
throws,
|
||||
)
|
||||
.map(wrap_import_function)
|
||||
.map(|import| program.imports.push(import));
|
||||
for import_function in first_pass.create_getter(
|
||||
identifier,
|
||||
&type_.type_,
|
||||
self_name,
|
||||
is_static,
|
||||
is_structural,
|
||||
throws,
|
||||
) {
|
||||
program.imports.push(wrap_import_function(import_function));
|
||||
}
|
||||
|
||||
if !readonly {
|
||||
first_pass
|
||||
.create_setter(
|
||||
identifier,
|
||||
type_.type_.clone(),
|
||||
self_name,
|
||||
statik,
|
||||
is_structural,
|
||||
throws,
|
||||
)
|
||||
.map(wrap_import_function)
|
||||
.map(|import| program.imports.push(import));
|
||||
for import_function in first_pass.create_setter(
|
||||
identifier,
|
||||
type_.type_.clone(),
|
||||
self_name,
|
||||
is_static,
|
||||
is_structural,
|
||||
throws,
|
||||
) {
|
||||
program.imports.push(wrap_import_function(import_function));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -646,7 +650,8 @@ fn member_operation<'src>(
|
||||
if util::is_chrome_only(attrs) {
|
||||
return Ok(());
|
||||
}
|
||||
let statik = match modifier {
|
||||
|
||||
let is_static = match modifier {
|
||||
Some(Stringifier(_)) => {
|
||||
warn!("Unsupported stringifier on type {:?}", (self_name, identifier));
|
||||
return Ok(())
|
||||
@ -655,40 +660,34 @@ fn member_operation<'src>(
|
||||
None => false,
|
||||
};
|
||||
|
||||
first_pass
|
||||
.create_basic_method(
|
||||
args,
|
||||
match identifier.map(|s| s.0) {
|
||||
None if specials.is_empty() => ::first_pass::OperationId::Operation(None),
|
||||
None if specials.len() == 1 => match specials[0] {
|
||||
weedle::interface::Special::Getter(weedle::term::Getter) =>
|
||||
::first_pass::OperationId::IndexingGetter,
|
||||
weedle::interface::Special::Setter(weedle::term::Setter) =>
|
||||
::first_pass::OperationId::IndexingSetter,
|
||||
weedle::interface::Special::Deleter(weedle::term::Deleter) =>
|
||||
::first_pass::OperationId::IndexingDeleter,
|
||||
weedle::interface::Special::LegacyCaller(weedle::term::LegacyCaller) =>
|
||||
return Ok(()),
|
||||
},
|
||||
Some(ref name) if specials.is_empty() =>
|
||||
::first_pass::OperationId::Operation(Some(name.clone())),
|
||||
_ => {
|
||||
warn!("Unsupported specials on type {:?}", (self_name, identifier));
|
||||
return Ok(())
|
||||
}
|
||||
for import_function in first_pass.create_basic_method(
|
||||
args,
|
||||
match identifier.map(|s| s.0) {
|
||||
None if specials.is_empty() => ::first_pass::OperationId::Operation(None),
|
||||
None if specials.len() == 1 => match specials[0] {
|
||||
weedle::interface::Special::Getter(_) => ::first_pass::OperationId::IndexingGetter,
|
||||
weedle::interface::Special::Setter(_) => ::first_pass::OperationId::IndexingSetter,
|
||||
weedle::interface::Special::Deleter(_) => ::first_pass::OperationId::IndexingDeleter,
|
||||
weedle::interface::Special::LegacyCaller(_) => return Ok(()),
|
||||
},
|
||||
return_type,
|
||||
self_name,
|
||||
statik,
|
||||
specials.len() == 1 || first_pass
|
||||
.interfaces
|
||||
.get(self_name)
|
||||
.map(|interface_data| interface_data.global)
|
||||
.unwrap_or(false),
|
||||
util::throws(attrs),
|
||||
)
|
||||
.map(wrap_import_function)
|
||||
.map(|import| program.imports.push(import));
|
||||
Some(ref name) if specials.is_empty() => ::first_pass::OperationId::Operation(Some(name.clone())),
|
||||
_ => {
|
||||
warn!("Unsupported specials on type {:?}", (self_name, identifier));
|
||||
return Ok(())
|
||||
}
|
||||
},
|
||||
return_type,
|
||||
self_name,
|
||||
is_static,
|
||||
specials.len() == 1 || first_pass
|
||||
.interfaces
|
||||
.get(self_name)
|
||||
.map(|interface_data| interface_data.global)
|
||||
.unwrap_or(false),
|
||||
util::throws(attrs),
|
||||
) {
|
||||
program.imports.push(wrap_import_function(import_function));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -743,6 +742,10 @@ impl<'src> WebidlParse<'src, ()> for weedle::EnumDefinition<'src> {
|
||||
_: &FirstPassRecord<'src>,
|
||||
(): (),
|
||||
) -> Result<()> {
|
||||
if util::is_chrome_only(&self.attributes) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let variants = &self.values.body.list;
|
||||
program.imports.push(backend::ast::Import {
|
||||
module: None,
|
||||
@ -776,9 +779,21 @@ impl<'src> WebidlParse<'src, &'src str> for weedle::interface::ConstMember<'src>
|
||||
first_pass: &FirstPassRecord<'src>,
|
||||
self_name: &'src str,
|
||||
) -> Result<()> {
|
||||
let ty = match self.const_type.to_syn_type(first_pass, TypePosition::Return) {
|
||||
Some(s) => s,
|
||||
if util::is_chrome_only(&self.attributes) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let idl_type = match self.const_type.to_idl_type(first_pass) {
|
||||
None => return Ok(()),
|
||||
Some(idl_type) => idl_type,
|
||||
};
|
||||
|
||||
let ty = match idl_type.to_syn_type(TypePosition::Return) {
|
||||
None => {
|
||||
warn!("Can not convert const type to syn type: {:?}", idl_type);
|
||||
return Ok(());
|
||||
},
|
||||
Some(ty) => ty,
|
||||
};
|
||||
|
||||
program.consts.push(backend::ast::Const {
|
||||
@ -804,60 +819,25 @@ impl<'src> WebidlParse<'src, ()> for weedle::NamespaceDefinition<'src> {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let rust_name = rust_ident(self.identifier.0.to_snake_case().as_str());
|
||||
|
||||
program.modules.entry(rust_name.clone())
|
||||
.and_modify(|_| warn!("Namespace with rust name {:?} added more than once", rust_name))
|
||||
.or_insert_with(|| backend::ast::Module {
|
||||
vis: public(),
|
||||
imports: Default::default()
|
||||
});
|
||||
|
||||
if let Some(attrs) = &self.attributes {
|
||||
for attr in &attrs.body.list {
|
||||
attr.webidl_parse(program, first_pass, self)?;
|
||||
}
|
||||
}
|
||||
|
||||
let namespace_names = NamespaceNames {
|
||||
rust_name: &rust_name,
|
||||
js_name: &self.identifier.0,
|
||||
let mut module = backend::ast::Module {
|
||||
vis: public(),
|
||||
name: rust_ident(self.identifier.0.to_snake_case().as_str()),
|
||||
imports: Default::default(),
|
||||
};
|
||||
for member in &self.members.body {
|
||||
member.webidl_parse(program, first_pass, namespace_names)?;
|
||||
|
||||
if let Some(namespace_data) = first_pass.namespaces.get(&self.identifier.0) {
|
||||
for member in &namespace_data.members {
|
||||
member.webidl_parse(program, first_pass, (&self.identifier.0, &mut module))?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'src> WebidlParse<'src, ()> for weedle::PartialNamespaceDefinition<'src> {
|
||||
fn webidl_parse(
|
||||
&'src self,
|
||||
program: &mut backend::ast::Program,
|
||||
first_pass: &FirstPassRecord<'src>,
|
||||
(): (),
|
||||
) -> Result<()> {
|
||||
if util::is_chrome_only(&self.attributes) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let rust_name = rust_ident(self.identifier.0.to_snake_case().as_str());
|
||||
|
||||
if !first_pass.namespaces.contains_key(self.identifier.0) {
|
||||
warn!(
|
||||
"Partial namespace {} missing non-partial namespace",
|
||||
self.identifier.0
|
||||
);
|
||||
}
|
||||
|
||||
let namespace_names = NamespaceNames {
|
||||
rust_name: &rust_name,
|
||||
js_name: &self.identifier.0,
|
||||
};
|
||||
for member in &self.members.body {
|
||||
member.webidl_parse(program, first_pass, namespace_names)?;
|
||||
}
|
||||
program.modules.push(module);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -874,22 +854,17 @@ impl<'src> WebidlParse<'src, &'src weedle::NamespaceDefinition<'src>> for Extend
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
#[derive(Copy, Clone)]
|
||||
struct NamespaceNames<'a> {
|
||||
rust_name: &'a Ident,
|
||||
js_name: &'a str,
|
||||
}
|
||||
|
||||
impl<'src> WebidlParse<'src, NamespaceNames<'src>> for weedle::namespace::NamespaceMember<'src> {
|
||||
impl<'src> WebidlParse<'src, (&'src str, &'src mut backend::ast::Module)> for weedle::namespace::NamespaceMember<'src> {
|
||||
fn webidl_parse(
|
||||
&'src self,
|
||||
program: &mut backend::ast::Program,
|
||||
first_pass: &FirstPassRecord<'src>,
|
||||
ns_names: NamespaceNames<'src>
|
||||
(self_name, module): (&'src str, &mut backend::ast::Module),
|
||||
) -> Result<()> {
|
||||
match self {
|
||||
weedle::namespace::NamespaceMember::Operation(op) => {
|
||||
op.webidl_parse(program, first_pass, ns_names)?;
|
||||
op.webidl_parse(program, first_pass, (self_name, module))?;
|
||||
}
|
||||
weedle::namespace::NamespaceMember::Attribute(_) => {
|
||||
warn!("Attribute namespace members are not supported")
|
||||
@ -899,42 +874,33 @@ impl<'src> WebidlParse<'src, NamespaceNames<'src>> for weedle::namespace::Namesp
|
||||
}
|
||||
}
|
||||
|
||||
impl<'src> WebidlParse<'src, NamespaceNames<'src>> for weedle::namespace::OperationNamespaceMember<'src> {
|
||||
impl<'src> WebidlParse<'src, (&'src str, &'src mut backend::ast::Module)> for weedle::namespace::OperationNamespaceMember<'src> {
|
||||
fn webidl_parse(
|
||||
&'src self,
|
||||
program: &mut backend::ast::Program,
|
||||
_program: &mut backend::ast::Program,
|
||||
first_pass: &FirstPassRecord<'src>,
|
||||
ns_names: NamespaceNames<'src>
|
||||
(self_name, module): (&'src str, &mut backend::ast::Module),
|
||||
) -> Result<()> {
|
||||
if util::is_chrome_only(&self.attributes) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let imported_fn = match first_pass.create_namespace_operation(
|
||||
for import_function in first_pass.create_namespace_operation(
|
||||
&self.args.body.list,
|
||||
self.identifier.as_ref().map(|id| id.0),
|
||||
&self.return_type,
|
||||
ns_names.js_name,
|
||||
self_name,
|
||||
util::throws(&self.attributes)
|
||||
) {
|
||||
Some(f) => f,
|
||||
None => { return Ok(()) }
|
||||
module.imports.push(
|
||||
backend::ast::Import {
|
||||
module: None,
|
||||
js_namespace: Some(raw_ident(self_name)),
|
||||
kind: backend::ast::ImportKind::Function(import_function),
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
let import = backend::ast::Import {
|
||||
module: None,
|
||||
js_namespace: Some(raw_ident(ns_names.js_name)),
|
||||
kind: backend::ast::ImportKind::Function(imported_fn),
|
||||
};
|
||||
|
||||
program
|
||||
.modules
|
||||
.get_mut(ns_names.rust_name)
|
||||
.unwrap()
|
||||
.imports
|
||||
.push(import);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user