Skip to content

Commit 87406a6

Browse files
authored
Adding unstable APIs for stable types. (#2586)
1 parent 8594cae commit 87406a6

File tree

3 files changed

+112
-21
lines changed

3 files changed

+112
-21
lines changed

crates/webidl/src/generator.rs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -624,19 +624,21 @@ pub struct DictionaryField {
624624
pub js_name: String,
625625
pub ty: Type,
626626
pub required: bool,
627+
pub unstable: bool,
627628
}
628629

629630
impl DictionaryField {
630-
fn generate_rust(&self, options: &Options, parent_name: String, unstable: bool) -> TokenStream {
631+
fn generate_rust(&self, options: &Options, parent_name: String) -> TokenStream {
631632
let DictionaryField {
632633
name,
633634
js_name,
634635
ty,
635636
required: _,
637+
unstable,
636638
} = self;
637639

638-
let unstable_attr = maybe_unstable_attr(unstable);
639-
let unstable_docs = maybe_unstable_docs(unstable);
640+
let unstable_attr = maybe_unstable_attr(*unstable);
641+
let unstable_docs = maybe_unstable_docs(*unstable);
640642

641643
let mut features = BTreeSet::new();
642644

@@ -708,6 +710,15 @@ impl Dictionary {
708710
}
709711
}
710712

713+
// The constructor is unstable if any of the fields are
714+
let (unstable_ctor, unstable_ctor_docs) = match unstable {
715+
true => (None, None),
716+
false => {
717+
let unstable = fields.iter().any(|f| f.unstable);
718+
(maybe_unstable_attr(unstable), maybe_unstable_docs(unstable))
719+
}
720+
};
721+
711722
required_features.remove(&name.to_string());
712723

713724
let cfg_features = get_cfg_features(options, &required_features);
@@ -725,7 +736,7 @@ impl Dictionary {
725736

726737
let fields = fields
727738
.into_iter()
728-
.map(|field| field.generate_rust(options, name.to_string(), *unstable))
739+
.map(|field| field.generate_rust(options, name.to_string()))
729740
.collect::<Vec<_>>();
730741

731742
quote! {
@@ -745,9 +756,11 @@ impl Dictionary {
745756

746757
#unstable_attr
747758
impl #name {
759+
#unstable_ctor
748760
#cfg_features
749761
#ctor_doc_comment
750762
#unstable_docs
763+
#unstable_ctor_docs
751764
pub fn new(#(#required_args),*) -> Self {
752765
#[allow(unused_mut)]
753766
let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());

crates/webidl/src/lib.rs

Lines changed: 66 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,22 +25,23 @@ use crate::generator::{
2525
use crate::idl_type::ToIdlType;
2626
use crate::traverse::TraverseType;
2727
use crate::util::{
28-
camel_case_ident, is_structural, read_dir, shouty_snake_case_ident, snake_case_ident, throws,
29-
webidl_const_v_to_backend_const_v, TypePosition,
28+
camel_case_ident, is_structural, is_type_unstable, read_dir, shouty_snake_case_ident,
29+
snake_case_ident, throws, webidl_const_v_to_backend_const_v, TypePosition,
3030
};
3131
use anyhow::Context;
3232
use anyhow::Result;
3333
use proc_macro2::{Ident, TokenStream};
3434
use quote::ToTokens;
3535
use sourcefile::SourceFile;
36-
use std::collections::{BTreeMap, BTreeSet};
36+
use std::collections::{BTreeMap, BTreeSet, HashSet};
3737
use std::ffi::OsStr;
3838
use std::fmt;
3939
use std::fs;
4040
use std::path::{Path, PathBuf};
4141
use std::process::Command;
4242
use wasm_bindgen_backend::util::rust_ident;
4343
use weedle::attribute::ExtendedAttributeList;
44+
use weedle::common::Identifier;
4445
use weedle::dictionary::DictionaryMember;
4546
use weedle::interface::InterfaceMember;
4647
use weedle::Parse;
@@ -126,6 +127,22 @@ fn parse(
126127
definitions.first_pass(&mut first_pass_record, ApiStability::Stable)?;
127128

128129
let unstable_definitions = parse_source(unstable_source)?;
130+
131+
// Gather unstable type Identifiers so that stable APIs can be downgraded
132+
// to unstable if they accept one of these types
133+
let unstable_types: HashSet<Identifier> = unstable_definitions
134+
.iter()
135+
.flat_map(|definition| {
136+
use weedle::Definition::*;
137+
match definition {
138+
Dictionary(v) => Some(v.identifier),
139+
Enum(v) => Some(v.identifier),
140+
Interface(v) => Some(v.identifier),
141+
_ => None,
142+
}
143+
})
144+
.collect();
145+
129146
unstable_definitions.first_pass(&mut first_pass_record, ApiStability::Unstable)?;
130147

131148
let mut types: BTreeMap<String, Program> = BTreeMap::new();
@@ -138,7 +155,14 @@ fn parse(
138155
for (js_name, d) in first_pass_record.dictionaries.iter() {
139156
let name = rust_ident(&camel_case_ident(js_name));
140157
let program = types.entry(name.to_string()).or_default();
141-
first_pass_record.append_dictionary(&options, program, name, js_name.to_string(), d);
158+
first_pass_record.append_dictionary(
159+
&options,
160+
program,
161+
name,
162+
js_name.to_string(),
163+
d,
164+
&unstable_types,
165+
);
142166
}
143167
for (js_name, n) in first_pass_record.namespaces.iter() {
144168
let name = rust_ident(&snake_case_ident(js_name));
@@ -148,7 +172,14 @@ fn parse(
148172
for (js_name, d) in first_pass_record.interfaces.iter() {
149173
let name = rust_ident(&camel_case_ident(js_name));
150174
let program = types.entry(name.to_string()).or_default();
151-
first_pass_record.append_interface(&options, program, name, js_name.to_string(), d);
175+
first_pass_record.append_interface(
176+
&options,
177+
program,
178+
name,
179+
js_name.to_string(),
180+
&unstable_types,
181+
d,
182+
);
152183
}
153184
for (js_name, d) in first_pass_record.callback_interfaces.iter() {
154185
let name = rust_ident(&camel_case_ident(js_name));
@@ -252,6 +283,7 @@ impl<'src> FirstPassRecord<'src> {
252283
name: Ident,
253284
js_name: String,
254285
data: &first_pass::DictionaryData<'src>,
286+
unstable_types: &HashSet<Identifier>,
255287
) {
256288
let def = match data.definition {
257289
Some(def) => def,
@@ -264,7 +296,7 @@ impl<'src> FirstPassRecord<'src> {
264296

265297
let mut fields = Vec::new();
266298

267-
if !self.append_dictionary_members(&js_name, &mut fields) {
299+
if !self.append_dictionary_members(&js_name, &mut fields, unstable, unstable_types) {
268300
return;
269301
}
270302

@@ -278,15 +310,21 @@ impl<'src> FirstPassRecord<'src> {
278310
.to_tokens(&mut program.tokens);
279311
}
280312

281-
fn append_dictionary_members(&self, dict: &'src str, dst: &mut Vec<DictionaryField>) -> bool {
313+
fn append_dictionary_members(
314+
&self,
315+
dict: &'src str,
316+
dst: &mut Vec<DictionaryField>,
317+
unstable: bool,
318+
unstable_types: &HashSet<Identifier>,
319+
) -> bool {
282320
let dict_data = &self.dictionaries[&dict];
283321
let definition = dict_data.definition.unwrap();
284322

285323
// > The order of the dictionary members on a given dictionary is
286324
// > such that inherited dictionary members are ordered before
287325
// > non-inherited members ...
288326
if let Some(parent) = &definition.inheritance {
289-
if !self.append_dictionary_members(parent.identifier.0, dst) {
327+
if !self.append_dictionary_members(parent.identifier.0, dst, unstable, unstable_types) {
290328
return false;
291329
}
292330
}
@@ -299,7 +337,7 @@ impl<'src> FirstPassRecord<'src> {
299337
let members = definition.members.body.iter();
300338
let partials = dict_data.partials.iter().flat_map(|d| &d.members.body);
301339
for member in members.chain(partials) {
302-
match self.dictionary_field(member) {
340+
match self.dictionary_field(member, unstable, unstable_types) {
303341
Some(f) => dst.push(f),
304342
None => {
305343
log::warn!(
@@ -321,7 +359,17 @@ impl<'src> FirstPassRecord<'src> {
321359
return true;
322360
}
323361

324-
fn dictionary_field(&self, field: &'src DictionaryMember<'src>) -> Option<DictionaryField> {
362+
fn dictionary_field(
363+
&self,
364+
field: &'src DictionaryMember<'src>,
365+
unstable: bool,
366+
unstable_types: &HashSet<Identifier>,
367+
) -> Option<DictionaryField> {
368+
let unstable_override = match unstable {
369+
true => true,
370+
false => is_type_unstable(&field.type_, unstable_types),
371+
};
372+
325373
// use argument position now as we're just binding setters
326374
let ty = field
327375
.type_
@@ -376,6 +424,7 @@ impl<'src> FirstPassRecord<'src> {
376424
name: rust_ident(&snake_case_ident(field.identifier.0)),
377425
js_name: field.identifier.0.to_string(),
378426
ty,
427+
unstable: unstable_override,
379428
})
380429
}
381430

@@ -424,7 +473,7 @@ impl<'src> FirstPassRecord<'src> {
424473
}
425474
}
426475

427-
for x in self.create_imports(None, id, data, false) {
476+
for x in self.create_imports(None, id, data, false, &HashSet::new()) {
428477
functions.push(Function {
429478
name: x.name,
430479
js_name: x.js_name,
@@ -465,6 +514,7 @@ impl<'src> FirstPassRecord<'src> {
465514
program: &mut Program,
466515
name: Ident,
467516
js_name: String,
517+
unstable_types: &HashSet<Identifier>,
468518
data: &InterfaceData<'src>,
469519
) {
470520
let unstable = data.stability.is_unstable();
@@ -505,7 +555,7 @@ impl<'src> FirstPassRecord<'src> {
505555
}
506556

507557
for (id, op_data) in data.operations.iter() {
508-
self.member_operation(&mut methods, data, id, op_data);
558+
self.member_operation(&mut methods, data, id, op_data, unstable_types);
509559
}
510560

511561
for mixin_data in self.all_mixins(&js_name) {
@@ -533,7 +583,7 @@ impl<'src> FirstPassRecord<'src> {
533583
}
534584

535585
for (id, op_data) in mixin_data.operations.iter() {
536-
self.member_operation(&mut methods, data, id, op_data);
586+
self.member_operation(&mut methods, data, id, op_data, unstable_types);
537587
}
538588
}
539589

@@ -625,11 +675,12 @@ impl<'src> FirstPassRecord<'src> {
625675
data: &InterfaceData<'src>,
626676
id: &OperationId<'src>,
627677
op_data: &OperationData<'src>,
678+
unstable_types: &HashSet<Identifier>,
628679
) {
629680
let attrs = data.definition_attributes;
630681
let unstable = data.stability.is_unstable();
631682

632-
for method in self.create_imports(attrs, id, op_data, unstable) {
683+
for method in self.create_imports(attrs, id, op_data, unstable, unstable_types) {
633684
methods.push(method);
634685
}
635686
}
@@ -663,6 +714,7 @@ impl<'src> FirstPassRecord<'src> {
663714
.to_syn_type(pos)
664715
.unwrap()
665716
.unwrap(),
717+
unstable: false,
666718
})
667719
}
668720
_ => {

crates/webidl/src/util.rs

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::collections::BTreeSet;
1+
use std::collections::{BTreeSet, HashSet};
22
use std::fs;
33
use std::iter::FromIterator;
44
use std::path::{Path, PathBuf};
@@ -11,7 +11,9 @@ use syn;
1111
use wasm_bindgen_backend::util::{ident_ty, raw_ident, rust_ident};
1212
use weedle;
1313
use weedle::attribute::{ExtendedAttribute, ExtendedAttributeList, IdentifierOrString};
14+
use weedle::common::Identifier;
1415
use weedle::literal::{ConstValue, FloatLit, IntegerLit};
16+
use weedle::types::{NonAnyType, SingleType};
1517

1618
use crate::constants::IMMUTABLE_SLICE_WHITELIST;
1719
use crate::first_pass::{FirstPassRecord, OperationData, OperationId, Signature};
@@ -204,6 +206,7 @@ impl<'src> FirstPassRecord<'src> {
204206
id: &OperationId<'src>,
205207
data: &OperationData<'src>,
206208
unstable: bool,
209+
unstable_types: &HashSet<Identifier>,
207210
) -> Vec<InterfaceMethod> {
208211
let is_static = data.is_static;
209212

@@ -437,6 +440,19 @@ impl<'src> FirstPassRecord<'src> {
437440
.map(|(idl_type, orig_arg)| (orig_arg.name.to_string(), idl_type)),
438441
);
439442

443+
// Stable types can have methods that have unstable argument types.
444+
// If any of the arguments types are `unstable` then this method is downgraded
445+
// to be unstable.
446+
let unstable_override = match unstable {
447+
// only downgrade stable methods
448+
false => signature
449+
.orig
450+
.args
451+
.iter()
452+
.any(|arg| is_type_unstable(arg.ty, unstable_types)),
453+
true => true,
454+
};
455+
440456
if let Some(arguments) = arguments {
441457
if let Ok(ret_ty) = ret_ty.to_syn_type(TypePosition::Return) {
442458
ret.push(InterfaceMethod {
@@ -449,7 +465,7 @@ impl<'src> FirstPassRecord<'src> {
449465
structural,
450466
catch,
451467
variadic,
452-
unstable,
468+
unstable: unstable_override,
453469
});
454470
}
455471
}
@@ -480,7 +496,7 @@ impl<'src> FirstPassRecord<'src> {
480496
structural,
481497
catch,
482498
variadic: false,
483-
unstable,
499+
unstable: unstable_override,
484500
});
485501
}
486502
}
@@ -514,6 +530,16 @@ impl<'src> FirstPassRecord<'src> {
514530
}
515531
}
516532

533+
pub fn is_type_unstable(ty: &weedle::types::Type, unstable_types: &HashSet<Identifier>) -> bool {
534+
match ty {
535+
weedle::types::Type::Single(SingleType::NonAny(NonAnyType::Identifier(i))) => {
536+
// Check if the type in the unstable type list
537+
unstable_types.contains(&i.type_)
538+
}
539+
_ => false,
540+
}
541+
}
542+
517543
/// Search for an attribute by name in some webidl object's attributes.
518544
fn has_named_attribute(list: Option<&ExtendedAttributeList>, attribute: &str) -> bool {
519545
let list = match list {

0 commit comments

Comments
 (0)