[WIP] Add support for unstable WebIDL (#1997)

* Re-enable WebGPU WebIDL as experimental

* Add `web_sys_unstable_apis` attribute

* Add test for unstable WebIDL

* Include unstable WebIDL in docs.rs builds

* Add docs and doc comment for unstable APIs

* Add unstable API checks to CI
This commit is contained in:
Josh Groves
2020-02-26 19:00:11 -03:30
committed by GitHub
parent d26068dc6c
commit 99c59a771e
24 changed files with 1387 additions and 792 deletions

View File

@ -20,15 +20,14 @@ use weedle::CallbackInterfaceDefinition;
use weedle::{DictionaryDefinition, PartialDictionaryDefinition};
use super::Result;
use crate::util;
use crate::util::camel_case_ident;
use crate::{util::{self, camel_case_ident}, ApiStability};
/// Collection of constructs that may use partial.
#[derive(Default)]
pub(crate) struct FirstPassRecord<'src> {
pub(crate) builtin_idents: BTreeSet<Ident>,
pub(crate) interfaces: BTreeMap<&'src str, InterfaceData<'src>>,
pub(crate) enums: BTreeMap<&'src str, &'src weedle::EnumDefinition<'src>>,
pub(crate) enums: BTreeMap<&'src str, EnumData<'src>>,
/// The mixins, mapping their name to the webidl ast node for the mixin.
pub(crate) mixins: BTreeMap<&'src str, MixinData<'src>>,
pub(crate) typedefs: BTreeMap<&'src str, &'src weedle::types::Type<'src>>,
@ -40,6 +39,11 @@ pub(crate) struct FirstPassRecord<'src> {
pub(crate) immutable_slice_whitelist: BTreeSet<&'static str>,
}
pub(crate) struct AttributeInterfaceData<'src> {
pub(crate) definition: &'src AttributeInterfaceMember<'src>,
pub(crate) stability: ApiStability,
}
/// We need to collect interface data during the first pass, to be used later.
#[derive(Default)]
pub(crate) struct InterfaceData<'src> {
@ -47,11 +51,17 @@ pub(crate) struct InterfaceData<'src> {
pub(crate) partial: bool,
pub(crate) has_interface: bool,
pub(crate) deprecated: Option<String>,
pub(crate) attributes: Vec<&'src AttributeInterfaceMember<'src>>,
pub(crate) attributes: Vec<AttributeInterfaceData<'src>>,
pub(crate) consts: Vec<&'src ConstMember<'src>>,
pub(crate) operations: BTreeMap<OperationId<'src>, OperationData<'src>>,
pub(crate) superclass: Option<&'src str>,
pub(crate) definition_attributes: Option<&'src ExtendedAttributeList<'src>>,
pub(crate) stability: ApiStability,
}
pub(crate) struct AttributeMixinData<'src> {
pub(crate) definition: &'src AttributeMixinMember<'src>,
pub(crate) stability: ApiStability,
}
/// We need to collect mixin data during the first pass, to be used later.
@ -59,10 +69,11 @@ pub(crate) struct InterfaceData<'src> {
pub(crate) struct MixinData<'src> {
/// Whether only partial mixins were encountered
pub(crate) partial: bool,
pub(crate) attributes: Vec<&'src AttributeMixinMember<'src>>,
pub(crate) attributes: Vec<AttributeMixinData<'src>>,
pub(crate) consts: Vec<&'src ConstMember<'src>>,
pub(crate) operations: BTreeMap<OperationId<'src>, OperationData<'src>>,
pub(crate) definition_attributes: Option<&'src ExtendedAttributeList<'src>>,
pub(crate) stability: ApiStability,
}
/// We need to collect namespace data during the first pass, to be used later.
@ -75,6 +86,12 @@ pub(crate) struct NamespaceData<'src> {
pub(crate) struct DictionaryData<'src> {
pub(crate) partials: Vec<&'src PartialDictionaryDefinition<'src>>,
pub(crate) definition: Option<&'src DictionaryDefinition<'src>>,
pub(crate) stability: ApiStability,
}
pub(crate) struct EnumData<'src> {
pub(crate) definition: &'src weedle::EnumDefinition<'src>,
pub(crate) stability: ApiStability,
}
pub(crate) struct CallbackInterfaceData<'src> {
@ -121,29 +138,29 @@ pub(crate) trait FirstPass<'src, Ctx> {
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, ctx: Ctx) -> Result<()>;
}
impl<'src> FirstPass<'src, ()> for [weedle::Definition<'src>] {
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> {
impl<'src> FirstPass<'src, ApiStability> for [weedle::Definition<'src>] {
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, stability: ApiStability) -> Result<()> {
for def in self {
def.first_pass(record, ())?;
def.first_pass(record, stability)?;
}
Ok(())
}
}
impl<'src> FirstPass<'src, ()> for weedle::Definition<'src> {
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> {
impl<'src> FirstPass<'src, ApiStability> for weedle::Definition<'src> {
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, stability: ApiStability) -> Result<()> {
use weedle::Definition::*;
match self {
Dictionary(dictionary) => dictionary.first_pass(record, ()),
Dictionary(dictionary) => dictionary.first_pass(record, stability),
PartialDictionary(dictionary) => dictionary.first_pass(record, ()),
Enum(enum_) => enum_.first_pass(record, ()),
Enum(enum_) => enum_.first_pass(record, stability),
IncludesStatement(includes) => includes.first_pass(record, ()),
Interface(interface) => interface.first_pass(record, ()),
PartialInterface(interface) => interface.first_pass(record, ()),
InterfaceMixin(mixin) => mixin.first_pass(record, ()),
PartialInterfaceMixin(mixin) => mixin.first_pass(record, ()),
Interface(interface) => interface.first_pass(record, stability),
PartialInterface(interface) => interface.first_pass(record, stability),
InterfaceMixin(mixin) => mixin.first_pass(record, stability),
PartialInterfaceMixin(mixin) => mixin.first_pass(record, stability),
Namespace(namespace) => namespace.first_pass(record, ()),
PartialNamespace(namespace) => namespace.first_pass(record, ()),
Typedef(typedef) => typedef.first_pass(record, ()),
@ -154,17 +171,20 @@ 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<()> {
impl<'src> FirstPass<'src, ApiStability> for weedle::DictionaryDefinition<'src> {
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, stability: ApiStability) -> Result<()> {
if util::is_chrome_only(&self.attributes) {
return Ok(());
}
record
let dictionary_data = record
.dictionaries
.entry(self.identifier.0)
.or_default()
.definition = Some(self);
.or_default();
dictionary_data.definition = Some(self);
dictionary_data.stability = stability;
Ok(())
}
}
@ -181,17 +201,23 @@ impl<'src> FirstPass<'src, ()> for weedle::PartialDictionaryDefinition<'src> {
.or_default()
.partials
.push(self);
Ok(())
}
}
impl<'src> FirstPass<'src, ()> for weedle::EnumDefinition<'src> {
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> {
impl<'src> FirstPass<'src, ApiStability> for weedle::EnumDefinition<'src> {
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, stability: ApiStability) -> Result<()> {
if util::is_chrome_only(&self.attributes) {
return Ok(());
}
if record.enums.insert(self.identifier.0, self).is_some() {
let enum_data = EnumData {
definition: self,
stability,
};
if record.enums.insert(self.identifier.0, enum_data).is_some() {
log::info!(
"Encountered multiple enum declarations: {}",
self.identifier.0
@ -298,8 +324,8 @@ fn first_pass_operation<'src>(
}
}
impl<'src> FirstPass<'src, ()> for weedle::InterfaceDefinition<'src> {
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> {
impl<'src> FirstPass<'src, ApiStability> for weedle::InterfaceDefinition<'src> {
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, stability: ApiStability) -> Result<()> {
if util::is_chrome_only(&self.attributes) {
return Ok(());
}
@ -311,6 +337,7 @@ impl<'src> FirstPass<'src, ()> for weedle::InterfaceDefinition<'src> {
interface_data.deprecated =
util::get_rust_deprecated(&self.attributes).map(|s| s.to_string());
interface_data.has_interface = !util::is_no_interface_object(&self.attributes);
interface_data.stability = stability;
if let Some(attrs) = &self.attributes {
for attr in attrs.body.list.iter() {
process_interface_attribute(record, self.identifier.0, attr);
@ -318,7 +345,7 @@ impl<'src> FirstPass<'src, ()> for weedle::InterfaceDefinition<'src> {
}
for member in &self.members.body {
member.first_pass(record, self.identifier.0)?;
member.first_pass(record, (self.identifier.0, stability))?;
}
Ok(())
@ -382,8 +409,8 @@ fn process_interface_attribute<'src>(
}
}
impl<'src> FirstPass<'src, ()> for weedle::PartialInterfaceDefinition<'src> {
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> {
impl<'src> FirstPass<'src, ApiStability> for weedle::PartialInterfaceDefinition<'src> {
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, stability: ApiStability) -> Result<()> {
if util::is_chrome_only(&self.attributes) {
return Ok(());
}
@ -392,31 +419,33 @@ impl<'src> FirstPass<'src, ()> for weedle::PartialInterfaceDefinition<'src> {
.entry(self.identifier.0)
.or_insert_with(|| InterfaceData {
partial: true,
stability,
..Default::default()
});
for member in &self.members.body {
member.first_pass(record, self.identifier.0)?;
member.first_pass(record, (self.identifier.0, stability))?;
}
Ok(())
}
}
impl<'src> FirstPass<'src, &'src str> for weedle::interface::InterfaceMember<'src> {
impl<'src> FirstPass<'src, (&'src str, ApiStability)> for weedle::interface::InterfaceMember<'src> {
fn first_pass(
&'src self,
record: &mut FirstPassRecord<'src>,
self_name: &'src str,
ctx: (&'src str, ApiStability),
) -> Result<()> {
match self {
InterfaceMember::Attribute(attr) => attr.first_pass(record, self_name),
InterfaceMember::Operation(op) => op.first_pass(record, self_name),
InterfaceMember::Attribute(attr) => attr.first_pass(record, ctx),
InterfaceMember::Operation(op) => op.first_pass(record, ctx.0),
InterfaceMember::Const(const_) => {
if util::is_chrome_only(&const_.attributes) {
return Ok(());
}
record
.interfaces
.get_mut(self_name)
.get_mut(ctx.0)
.unwrap()
.consts
.push(const_);
@ -484,11 +513,11 @@ impl<'src> FirstPass<'src, &'src str> for weedle::interface::OperationInterfaceM
}
}
impl<'src> FirstPass<'src, &'src str> for weedle::interface::AttributeInterfaceMember<'src> {
impl<'src> FirstPass<'src, (&'src str, ApiStability)> for weedle::interface::AttributeInterfaceMember<'src> {
fn first_pass(
&'src self,
record: &mut FirstPassRecord<'src>,
self_name: &'src str,
ctx: (&'src str, ApiStability),
) -> Result<()> {
if util::is_chrome_only(&self.attributes) {
return Ok(());
@ -496,16 +525,19 @@ impl<'src> FirstPass<'src, &'src str> for weedle::interface::AttributeInterfaceM
record
.interfaces
.get_mut(self_name)
.get_mut(ctx.0)
.unwrap()
.attributes
.push(self);
.push(AttributeInterfaceData {
definition: self,
stability: ctx.1
});
Ok(())
}
}
impl<'src> FirstPass<'src, ()> for weedle::InterfaceMixinDefinition<'src> {
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> {
impl<'src> FirstPass<'src, ApiStability> for weedle::InterfaceMixinDefinition<'src> {
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, stability: ApiStability) -> Result<()> {
if util::is_chrome_only(&self.attributes) {
return Ok(());
}
@ -514,18 +546,19 @@ impl<'src> FirstPass<'src, ()> for weedle::InterfaceMixinDefinition<'src> {
let mixin_data = record.mixins.entry(self.identifier.0).or_default();
mixin_data.partial = false;
mixin_data.definition_attributes = self.attributes.as_ref();
mixin_data.stability = stability;
}
for member in &self.members.body {
member.first_pass(record, self.identifier.0)?;
member.first_pass(record, (self.identifier.0, stability))?;
}
Ok(())
}
}
impl<'src> FirstPass<'src, ()> for weedle::PartialInterfaceMixinDefinition<'src> {
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> {
impl<'src> FirstPass<'src, ApiStability> for weedle::PartialInterfaceMixinDefinition<'src> {
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, stability: ApiStability) -> Result<()> {
if util::is_chrome_only(&self.attributes) {
return Ok(());
}
@ -535,31 +568,32 @@ impl<'src> FirstPass<'src, ()> for weedle::PartialInterfaceMixinDefinition<'src>
.entry(self.identifier.0)
.or_insert_with(|| MixinData {
partial: true,
stability,
..Default::default()
});
for member in &self.members.body {
member.first_pass(record, self.identifier.0)?;
member.first_pass(record, (self.identifier.0, stability))?;
}
Ok(())
}
}
impl<'src> FirstPass<'src, &'src str> for weedle::mixin::MixinMember<'src> {
impl<'src> FirstPass<'src, (&'src str, ApiStability)> for weedle::mixin::MixinMember<'src> {
fn first_pass(
&'src self,
record: &mut FirstPassRecord<'src>,
self_name: &'src str,
ctx: (&'src str, ApiStability),
) -> Result<()> {
match self {
MixinMember::Operation(op) => op.first_pass(record, self_name),
MixinMember::Attribute(a) => a.first_pass(record, self_name),
MixinMember::Operation(op) => op.first_pass(record, ctx),
MixinMember::Attribute(a) => a.first_pass(record, ctx),
MixinMember::Const(a) => {
if util::is_chrome_only(&a.attributes) {
return Ok(());
}
record.mixins.get_mut(self_name).unwrap().consts.push(a);
record.mixins.get_mut(ctx.0).unwrap().consts.push(a);
Ok(())
}
MixinMember::Stringifier(_) => {
@ -570,11 +604,11 @@ impl<'src> FirstPass<'src, &'src str> for weedle::mixin::MixinMember<'src> {
}
}
impl<'src> FirstPass<'src, &'src str> for weedle::mixin::OperationMixinMember<'src> {
impl<'src> FirstPass<'src, (&'src str, ApiStability)> for weedle::mixin::OperationMixinMember<'src> {
fn first_pass(
&'src self,
record: &mut FirstPassRecord<'src>,
self_name: &'src str,
ctx: (&'src str, ApiStability),
) -> Result<()> {
if self.stringifier.is_some() {
log::warn!("Unsupported webidl stringifier: {:?}", self);
@ -584,7 +618,7 @@ impl<'src> FirstPass<'src, &'src str> for weedle::mixin::OperationMixinMember<'s
first_pass_operation(
record,
FirstPassOperationType::Mixin,
self_name,
ctx.0,
&[OperationId::Operation(self.identifier.map(|s| s.0.clone()))],
&self.args.body.list,
&self.return_type,
@ -595,21 +629,24 @@ impl<'src> FirstPass<'src, &'src str> for weedle::mixin::OperationMixinMember<'s
}
}
impl<'src> FirstPass<'src, &'src str> for weedle::mixin::AttributeMixinMember<'src> {
impl<'src> FirstPass<'src, (&'src str, ApiStability)> for weedle::mixin::AttributeMixinMember<'src> {
fn first_pass(
&'src self,
record: &mut FirstPassRecord<'src>,
self_name: &'src str,
ctx: (&'src str, ApiStability),
) -> Result<()> {
if util::is_chrome_only(&self.attributes) {
return Ok(());
}
record
.mixins
.get_mut(self_name)
.get_mut(ctx.0)
.unwrap()
.attributes
.push(self);
.push(AttributeMixinData {
definition: self,
stability: ctx.1
});
Ok(())
}
}

View File

@ -20,7 +20,7 @@ use crate::util::{
camel_case_ident, mdn_doc, public, shouty_snake_case_ident, snake_case_ident,
webidl_const_v_to_backend_const_v, TypePosition,
};
use anyhow::{bail, Result};
use anyhow::Result;
use proc_macro2::{Ident, Span};
use quote::{quote, ToTokens};
use std::collections::{BTreeSet, HashSet};
@ -55,37 +55,59 @@ impl fmt::Display for WebIDLParseError {
impl std::error::Error for WebIDLParseError {}
/// Parse a string of WebIDL source text into a wasm-bindgen AST.
fn parse(webidl_source: &str, allowed_types: Option<&[&str]>) -> Result<Program> {
let definitions = match weedle::parse(webidl_source) {
Ok(def) => def,
Err(e) => {
match &e {
weedle::Err::Incomplete(needed) => bail!("needed {:?} more bytes", needed),
weedle::Err::Error(cx) | weedle::Err::Failure(cx) => {
// Note that #[allow] here is a workaround for Geal/nom#843
// because the `Context` type here comes from `nom` and if
// something else in our crate graph enables the
// `verbose-errors` feature then we need to still compiled
// against the changed enum definition.
#[allow(unreachable_patterns)]
let remaining = match cx {
weedle::Context::Code(remaining, _) => remaining.len(),
_ => 0,
};
let pos = webidl_source.len() - remaining;
#[derive(Clone, Copy, PartialEq)]
pub(crate) enum ApiStability {
Stable,
Unstable,
}
bail!(WebIDLParseError(pos))
}
impl ApiStability {
pub(crate) fn is_unstable(self) -> bool {
self == Self::Unstable
}
}
impl Default for ApiStability {
fn default() -> Self {
Self::Stable
}
}
fn parse_source(source: &str) -> Result<Vec<weedle::Definition>> {
weedle::parse(source).map_err(|e| {
match &e {
weedle::Err::Incomplete(needed) => anyhow::anyhow!("needed {:?} more bytes", needed),
weedle::Err::Error(cx) | weedle::Err::Failure(cx) => {
// Note that #[allow] here is a workaround for Geal/nom#843
// because the `Context` type here comes from `nom` and if
// something else in our crate graph enables the
// `verbose-errors` feature then we need to still compiled
// against the changed enum definition.
#[allow(unreachable_patterns)]
let remaining = match cx {
weedle::Context::Code(remaining, _) => remaining.len(),
_ => 0,
};
let pos = source.len() - remaining;
WebIDLParseError(pos).into()
}
}
};
})
}
/// Parse a string of WebIDL source text into a wasm-bindgen AST.
fn parse(webidl_source: &str, unstable_source: &str, allowed_types: Option<&[&str]>) -> Result<Program> {
let mut first_pass_record: FirstPassRecord = Default::default();
first_pass_record.builtin_idents = builtin_idents();
first_pass_record.immutable_slice_whitelist = immutable_slice_whitelist();
definitions.first_pass(&mut first_pass_record, ())?;
let definitions = parse_source(webidl_source)?;
definitions.first_pass(&mut first_pass_record, ApiStability::Stable)?;
let unstable_definitions = parse_source(unstable_source)?;
unstable_definitions.first_pass(&mut first_pass_record, ApiStability::Unstable)?;
let mut program = Default::default();
let mut submodules = Vec::new();
@ -136,14 +158,14 @@ fn parse(webidl_source: &str, allowed_types: Option<&[&str]>) -> Result<Program>
Ok(Program {
main: program,
submodules: submodules,
submodules,
})
}
/// Compile the given WebIDL source text into Rust source text containing
/// `wasm-bindgen` bindings to the things described in the WebIDL.
pub fn compile(webidl_source: &str, allowed_types: Option<&[&str]>) -> Result<String> {
let ast = parse(webidl_source, allowed_types)?;
pub fn compile(webidl_source: &str, experimental_source: &str, allowed_types: Option<&[&str]>) -> Result<String> {
let ast = parse(webidl_source, experimental_source, allowed_types)?;
Ok(compile_ast(ast))
}
@ -292,8 +314,10 @@ fn compile_ast(mut ast: Program) -> String {
}
impl<'src> FirstPassRecord<'src> {
fn append_enum(&self, program: &mut ast::Program, enum_: &'src weedle::EnumDefinition<'src>) {
fn append_enum(&self, program: &mut ast::Program, data: &first_pass::EnumData<'src>) {
let enum_ = data.definition;
let variants = &enum_.values.body.list;
let unstable_api = data.stability.is_unstable();
program.imports.push(ast::Import {
module: ast::ImportModule::None,
js_namespace: None,
@ -312,7 +336,9 @@ impl<'src> FirstPassRecord<'src> {
.collect(),
variant_values: variants.iter().map(|v| v.0.to_string()).collect(),
rust_attrs: vec![syn::parse_quote!(#[derive(Copy, Clone, PartialEq, Debug)])],
unstable_api,
}),
unstable_api,
});
}
@ -352,6 +378,7 @@ impl<'src> FirstPassRecord<'src> {
ctor: true,
doc_comment: Some(doc_comment),
ctor_doc_comment: None,
unstable_api: data.stability.is_unstable(),
};
let mut ctor_doc_comment = Some(format!("Construct a new `{}`\n", def.identifier.0));
self.append_required_features_doc(&dict, &mut ctor_doc_comment, &[&extra_feature]);
@ -504,7 +531,7 @@ impl<'src> FirstPassRecord<'src> {
let kind = ast::ImportFunctionKind::Normal;
let extra = snake_case_ident(self_name);
let extra = &[&extra[..]];
for mut import_function in self.create_imports(None, kind, id, data) {
for mut import_function in self.create_imports(None, kind, id, data, false) {
let mut doc = Some(doc_comment.clone());
self.append_required_features_doc(&import_function, &mut doc, extra);
import_function.doc_comment = doc;
@ -512,6 +539,7 @@ impl<'src> FirstPassRecord<'src> {
module: ast::ImportModule::None,
js_namespace: Some(raw_ident(self_name)),
kind: ast::ImportKind::Function(import_function),
unstable_api: false,
});
}
}
@ -521,6 +549,7 @@ impl<'src> FirstPassRecord<'src> {
program: &mut ast::Program,
self_name: &'src str,
member: &'src weedle::interface::ConstMember<'src>,
unstable_api: bool,
) {
let idl_type = member.const_type.to_idl_type(self);
let ty = match idl_type.to_syn_type(TypePosition::Return) {
@ -542,6 +571,7 @@ impl<'src> FirstPassRecord<'src> {
class: Some(rust_ident(camel_case_ident(&self_name).as_str())),
ty,
value: webidl_const_v_to_backend_const_v(&member.const_value),
unstable_api,
});
}
@ -553,6 +583,8 @@ impl<'src> FirstPassRecord<'src> {
) {
let mut doc_comment = Some(format!("The `{}` object\n\n{}", name, mdn_doc(name, None),));
let interface_unstable = data.stability.is_unstable();
let mut attrs = Vec::new();
attrs.push(syn::parse_quote!( #[derive(Debug, Clone, PartialEq, Eq)] ));
self.add_deprecated(data, &mut attrs);
@ -561,6 +593,7 @@ impl<'src> FirstPassRecord<'src> {
rust_name: rust_ident(camel_case_ident(name).as_str()),
js_name: name.to_string(),
attrs,
unstable_api: interface_unstable,
doc_comment: None,
instanceof_shim: format!("__widl_instanceof_{}", name),
is_type_of: if data.has_interface {
@ -596,25 +629,28 @@ impl<'src> FirstPassRecord<'src> {
module: ast::ImportModule::None,
js_namespace: None,
kind: ast::ImportKind::Type(import_type),
unstable_api: interface_unstable,
});
for (id, op_data) in data.operations.iter() {
self.member_operation(program, name, data, id, op_data);
}
for member in data.consts.iter() {
self.append_const(program, name, member);
self.append_const(program, name, member, interface_unstable);
}
for member in data.attributes.iter() {
let member_def = member.definition;
self.member_attribute(
program,
name,
data,
member.modifier,
member.readonly.is_some(),
&member.type_,
member.identifier.0,
&member.attributes,
member_def.modifier,
member_def.readonly.is_some(),
&member_def.type_,
member_def.identifier.0,
&member_def.attributes,
data.definition_attributes,
interface_unstable || member.stability.is_unstable(),
);
}
@ -623,23 +659,25 @@ impl<'src> FirstPassRecord<'src> {
self.member_operation(program, name, data, id, op_data);
}
for member in &mixin_data.consts {
self.append_const(program, name, member);
self.append_const(program, name, member, interface_unstable);
}
for member in &mixin_data.attributes {
let member_def = member.definition;
self.member_attribute(
program,
name,
data,
if let Some(s) = member.stringifier {
if let Some(s) = member_def.stringifier {
Some(weedle::interface::StringifierOrInheritOrStatic::Stringifier(s))
} else {
None
},
member.readonly.is_some(),
&member.type_,
member.identifier.0,
&member.attributes,
member_def.readonly.is_some(),
&member_def.type_,
member_def.identifier.0,
&member_def.attributes,
data.definition_attributes,
interface_unstable || member.stability.is_unstable(),
);
}
}
@ -656,6 +694,7 @@ impl<'src> FirstPassRecord<'src> {
identifier: &'src str,
attrs: &'src Option<ExtendedAttributeList<'src>>,
container_attrs: Option<&'src ExtendedAttributeList<'src>>,
unstable_api: bool,
) {
use weedle::interface::StringifierOrInheritOrStatic::*;
@ -673,6 +712,7 @@ impl<'src> FirstPassRecord<'src> {
is_static,
attrs,
container_attrs,
unstable_api,
) {
let mut doc = import_function.doc_comment.take();
self.append_required_features_doc(&import_function, &mut doc, &[]);
@ -688,6 +728,7 @@ impl<'src> FirstPassRecord<'src> {
is_static,
attrs,
container_attrs,
unstable_api,
) {
let mut doc = import_function.doc_comment.take();
self.append_required_features_doc(&import_function, &mut doc, &[]);
@ -742,7 +783,7 @@ impl<'src> FirstPassRecord<'src> {
OperationId::IndexingDeleter => Some(format!("The indexing deleter\n\n")),
};
let attrs = data.definition_attributes;
for mut method in self.create_imports(attrs, kind, id, op_data) {
for mut method in self.create_imports(attrs, kind, id, op_data, data.stability.is_unstable()) {
let mut doc = doc.clone();
self.append_required_features_doc(&method, &mut doc, &[]);
method.doc_comment = doc;
@ -843,6 +884,7 @@ impl<'src> FirstPassRecord<'src> {
ctor: true,
doc_comment: None,
ctor_doc_comment: None,
unstable_api: false,
});
}
}

View File

@ -231,6 +231,7 @@ impl<'src> FirstPassRecord<'src> {
catch: bool,
variadic: bool,
doc_comment: Option<String>,
unstable_api: bool,
) -> Option<ast::ImportFunction>
where
'src: 'a,
@ -327,6 +328,7 @@ impl<'src> FirstPassRecord<'src> {
},
kind,
doc_comment,
unstable_api,
})
}
@ -339,6 +341,7 @@ impl<'src> FirstPassRecord<'src> {
is_static: bool,
attrs: &Option<ExtendedAttributeList>,
container_attrs: Option<&ExtendedAttributeList>,
unstable_api: bool,
) -> Option<ast::ImportFunction> {
let kind = ast::OperationKind::Getter(Some(raw_ident(name)));
let kind = self.import_function_kind(self_name, is_static, kind);
@ -357,6 +360,7 @@ impl<'src> FirstPassRecord<'src> {
name,
mdn_doc(self_name, Some(name))
)),
unstable_api,
)
}
@ -369,6 +373,7 @@ impl<'src> FirstPassRecord<'src> {
is_static: bool,
attrs: &Option<ExtendedAttributeList>,
container_attrs: Option<&ExtendedAttributeList>,
unstable_api: bool,
) -> Option<ast::ImportFunction> {
let kind = ast::OperationKind::Setter(Some(raw_ident(name)));
let kind = self.import_function_kind(self_name, is_static, kind);
@ -387,6 +392,7 @@ impl<'src> FirstPassRecord<'src> {
name,
mdn_doc(self_name, Some(name))
)),
unstable_api,
)
}
@ -414,6 +420,7 @@ impl<'src> FirstPassRecord<'src> {
kind: ast::ImportFunctionKind,
id: &OperationId<'src>,
data: &OperationData<'src>,
unstable_api: bool,
) -> Vec<ast::ImportFunction> {
// First up, prune all signatures that reference unsupported arguments.
// We won't consider these until said arguments are implemented.
@ -618,6 +625,7 @@ impl<'src> FirstPassRecord<'src> {
catch,
variadic,
None,
unstable_api,
),
);
if !variadic {
@ -644,6 +652,7 @@ impl<'src> FirstPassRecord<'src> {
catch,
false,
None,
unstable_api,
),
);
}