[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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 1387 additions and 792 deletions

View File

@ -14,7 +14,8 @@ Easy support for interacting between JS and Rust.
edition = "2018"
[package.metadata.docs.rs]
features = ['serde-serialize']
features = ["serde-serialize"]
rustc-args = ["--cfg=web_sys_unstable_apis"]
[lib]
test = false

View File

@ -122,6 +122,10 @@ jobs:
- script: cargo build --manifest-path crates/web-sys/Cargo.toml --target wasm32-unknown-unknown --features Element
- script: cargo build --manifest-path crates/web-sys/Cargo.toml --target wasm32-unknown-unknown --features Window
- script: cargo test --manifest-path crates/web-sys/Cargo.toml --target wasm32-unknown-unknown --all-features
- script: cargo test --manifest-path crates/web-sys/Cargo.toml --target wasm32-unknown-unknown --all-features
displayName: "web-sys unstable APIs"
env:
RUSTFLAGS: --cfg=web_sys_unstable_apis
- job: test_js_sys
displayName: "Run js-sys crate tests"
@ -150,6 +154,10 @@ jobs:
- script: cargo test -p webidl-tests --target wasm32-unknown-unknown
env:
WBINDGEN_I_PROMISE_JS_SYNTAX_WORKS_IN_NODE: 1
- script: cargo test -p webidl-tests --target wasm32-unknown-unknown
displayName: "webidl-tests unstable APIs"
env:
RUSTFLAGS: --cfg=web_sys_unstable_apis
- job: test_ui
displayName: "Run UI tests"
@ -319,6 +327,8 @@ jobs:
displayName: "Document js-sys"
- script: cargo doc --no-deps --manifest-path crates/web-sys/Cargo.toml --all-features
displayName: "Document web-sys"
env:
RUSTFLAGS: --cfg=web_sys_unstable_apis
- script: cargo doc --no-deps --manifest-path crates/futures/Cargo.toml
displayName: "Document wasm-bindgen-futures"
# Make a tarball even though a zip is uploaded, it looks like the tarball

View File

@ -51,6 +51,8 @@ pub struct Export {
/// Whether or not this function should be flagged as the wasm start
/// function.
pub start: bool,
/// Whether the API is unstable. This is only used internally.
pub unstable_api: bool,
}
/// The 3 types variations of `self`.
@ -71,6 +73,7 @@ pub struct Import {
pub module: ImportModule,
pub js_namespace: Option<Ident>,
pub kind: ImportKind,
pub unstable_api: bool,
}
#[cfg_attr(feature = "extra-traits", derive(Debug))]
@ -126,6 +129,7 @@ pub struct ImportFunction {
pub kind: ImportFunctionKind,
pub shim: Ident,
pub doc_comment: Option<String>,
pub unstable_api: bool,
}
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
@ -182,6 +186,7 @@ pub struct ImportType {
pub js_name: String,
pub attrs: Vec<syn::Attribute>,
pub typescript_name: Option<String>,
pub unstable_api: bool,
pub doc_comment: Option<String>,
pub instanceof_shim: String,
pub is_type_of: Option<syn::Expr>,
@ -202,6 +207,8 @@ pub struct ImportEnum {
pub variant_values: Vec<String>,
/// Attributes to apply to the Rust enum
pub rust_attrs: Vec<syn::Attribute>,
/// Whether the enum is part of an unstable WebIDL
pub unstable_api: bool,
}
#[cfg_attr(feature = "extra-traits", derive(Debug))]
@ -237,6 +244,7 @@ pub struct StructField {
pub getter: Ident,
pub setter: Ident,
pub comments: Vec<String>,
pub unstable_api: bool,
}
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
@ -246,6 +254,7 @@ pub struct Enum {
pub variants: Vec<Variant>,
pub comments: Vec<String>,
pub hole: u32,
pub unstable_api: bool,
}
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
@ -278,6 +287,7 @@ pub struct Const {
pub class: Option<Ident>,
pub ty: syn::Type,
pub value: ConstValue,
pub unstable_api: bool,
}
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq))]
@ -299,6 +309,7 @@ pub struct Dictionary {
pub ctor: bool,
pub doc_comment: Option<String>,
pub ctor_doc_comment: Option<String>,
pub unstable_api: bool,
}
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]

View File

@ -1,6 +1,6 @@
use crate::ast;
use crate::encode;
use crate::util::ShortHash;
use crate::util::{self, ShortHash};
use crate::Diagnostic;
use proc_macro2::{Ident, Literal, Span, TokenStream};
use quote::{quote, ToTokens};
@ -39,7 +39,10 @@ impl TryToTokens for ast::Program {
}
}
for i in self.imports.iter() {
DescribeImport(&i.kind).to_tokens(tokens);
DescribeImport {
kind: &i.kind,
unstable_api: i.unstable_api,
}.to_tokens(tokens);
// If there is a js namespace, check that name isn't a type. If it is,
// this import might be a method on that type.
@ -296,12 +299,13 @@ impl ToTokens for ast::StructField {
})
.to_tokens(tokens);
Descriptor(
&getter,
quote! {
Descriptor {
ident: &getter,
inner: quote! {
<#ty as WasmDescribe>::describe();
},
)
unstable_api: self.unstable_api,
}
.to_tokens(tokens);
if self.readonly {
@ -528,16 +532,17 @@ impl TryToTokens for ast::Export {
// this, but the tl;dr; is that this is stripped from the final wasm
// binary along with anything it references.
let export = Ident::new(&export_name, Span::call_site());
Descriptor(
&export,
quote! {
Descriptor {
ident: &export,
inner: quote! {
inform(FUNCTION);
inform(0);
inform(#nargs);
#(<#argtys as WasmDescribe>::describe();)*
#describe_ret
},
)
unstable_api: self.unstable_api,
}
.to_tokens(into);
Ok(())
@ -562,6 +567,7 @@ impl ToTokens for ast::ImportType {
let vis = &self.vis;
let rust_name = &self.rust_name;
let attrs = &self.attrs;
let unstable_api_attr = util::maybe_unstable_api_attr(self.unstable_api);
let doc_comment = match &self.doc_comment {
None => "",
Some(comment) => comment,
@ -610,12 +616,14 @@ impl ToTokens for ast::ImportType {
#[doc = #doc_comment]
#[repr(transparent)]
#[allow(clippy::all)]
#unstable_api_attr
#vis struct #rust_name {
obj: #internal_obj
}
#[allow(bad_style)]
#[allow(clippy::all)]
#unstable_api_attr
const #const_name: () = {
use wasm_bindgen::convert::{IntoWasmAbi, FromWasmAbi};
use wasm_bindgen::convert::{OptionIntoWasmAbi, OptionFromWasmAbi};
@ -766,6 +774,7 @@ impl ToTokens for ast::ImportType {
for superclass in self.extends.iter() {
(quote! {
#[allow(clippy::all)]
#unstable_api_attr
impl From<#rust_name> for #superclass {
#[inline]
fn from(obj: #rust_name) -> #superclass {
@ -775,6 +784,7 @@ impl ToTokens for ast::ImportType {
}
#[allow(clippy::all)]
#unstable_api_attr
impl AsRef<#superclass> for #rust_name {
#[inline]
fn as_ref(&self) -> &#superclass {
@ -796,6 +806,7 @@ impl ToTokens for ast::ImportEnum {
let variants = &self.variants;
let variant_strings = &self.variant_values;
let attrs = &self.rust_attrs;
let unstable_api_attr = util::maybe_unstable_api_attr(self.unstable_api);
let mut current_idx: usize = 0;
let variant_indexes: Vec<Literal> = variants
@ -824,6 +835,7 @@ impl ToTokens for ast::ImportEnum {
#[allow(bad_style)]
#(#attrs)*
#[allow(clippy::all)]
#unstable_api_attr
#vis enum #name {
#(#variants = #variant_indexes_ref,)*
#[doc(hidden)]
@ -831,6 +843,7 @@ impl ToTokens for ast::ImportEnum {
}
#[allow(clippy::all)]
#unstable_api_attr
impl #name {
#vis fn from_js_value(obj: &wasm_bindgen::JsValue) -> Option<#name> {
obj.as_string().and_then(|obj_str| match obj_str.as_str() {
@ -841,6 +854,7 @@ impl ToTokens for ast::ImportEnum {
}
#[allow(clippy::all)]
#unstable_api_attr
impl wasm_bindgen::describe::WasmDescribe for #name {
fn describe() {
wasm_bindgen::JsValue::describe()
@ -848,6 +862,7 @@ impl ToTokens for ast::ImportEnum {
}
#[allow(clippy::all)]
#unstable_api_attr
impl wasm_bindgen::convert::IntoWasmAbi for #name {
type Abi = <wasm_bindgen::JsValue as
wasm_bindgen::convert::IntoWasmAbi>::Abi;
@ -859,6 +874,7 @@ impl ToTokens for ast::ImportEnum {
}
#[allow(clippy::all)]
#unstable_api_attr
impl wasm_bindgen::convert::FromWasmAbi for #name {
type Abi = <wasm_bindgen::JsValue as
wasm_bindgen::convert::FromWasmAbi>::Abi;
@ -869,18 +885,21 @@ impl ToTokens for ast::ImportEnum {
}
#[allow(clippy::all)]
#unstable_api_attr
impl wasm_bindgen::convert::OptionIntoWasmAbi for #name {
#[inline]
fn none() -> Self::Abi { Object::none() }
}
#[allow(clippy::all)]
#unstable_api_attr
impl wasm_bindgen::convert::OptionFromWasmAbi for #name {
#[inline]
fn is_none(abi: &Self::Abi) -> bool { Object::is_none(abi) }
}
#[allow(clippy::all)]
#unstable_api_attr
impl From<#name> for wasm_bindgen::JsValue {
fn from(obj: #name) -> wasm_bindgen::JsValue {
match obj {
@ -992,6 +1011,7 @@ impl TryToTokens for ast::ImportFunction {
let arguments = &arguments;
let abi_arguments = &abi_arguments;
let abi_argument_names = &abi_argument_names;
let unstable_api_attr = util::maybe_unstable_api_attr(self.unstable_api);
let doc_comment = match &self.doc_comment {
None => "",
@ -1058,6 +1078,7 @@ impl TryToTokens for ast::ImportFunction {
if let Some(class) = class_ty {
(quote! {
#unstable_api_attr
impl #class {
#invocation
}
@ -1072,11 +1093,14 @@ impl TryToTokens for ast::ImportFunction {
}
// See comment above in ast::Export for what's going on here.
struct DescribeImport<'a>(&'a ast::ImportKind);
struct DescribeImport<'a> {
kind: &'a ast::ImportKind,
unstable_api: bool,
}
impl<'a> ToTokens for DescribeImport<'a> {
fn to_tokens(&self, tokens: &mut TokenStream) {
let f = match *self.0 {
let f = match *self.kind {
ast::ImportKind::Function(ref f) => f,
ast::ImportKind::Static(_) => return,
ast::ImportKind::Type(_) => return,
@ -1089,16 +1113,17 @@ impl<'a> ToTokens for DescribeImport<'a> {
None => quote! { <() as WasmDescribe>::describe(); },
};
Descriptor(
&f.shim,
quote! {
Descriptor {
ident: &f.shim,
inner: quote! {
inform(FUNCTION);
inform(0);
inform(#nargs);
#(<#argtys as WasmDescribe>::describe();)*
#inform_ret
},
)
unstable_api: self.unstable_api,
}
.to_tokens(tokens);
}
}
@ -1107,6 +1132,7 @@ impl ToTokens for ast::Enum {
fn to_tokens(&self, into: &mut TokenStream) {
let enum_name = &self.name;
let hole = &self.hole;
let unstable_api_attr = util::maybe_unstable_api_attr(self.unstable_api);
let cast_clauses = self.variants.iter().map(|variant| {
let variant_name = &variant.name;
quote! {
@ -1117,6 +1143,7 @@ impl ToTokens for ast::Enum {
});
(quote! {
#[allow(clippy::all)]
#unstable_api_attr
impl wasm_bindgen::convert::IntoWasmAbi for #enum_name {
type Abi = u32;
@ -1127,6 +1154,7 @@ impl ToTokens for ast::Enum {
}
#[allow(clippy::all)]
#unstable_api_attr
impl wasm_bindgen::convert::FromWasmAbi for #enum_name {
type Abi = u32;
@ -1139,18 +1167,21 @@ impl ToTokens for ast::Enum {
}
#[allow(clippy::all)]
#unstable_api_attr
impl wasm_bindgen::convert::OptionFromWasmAbi for #enum_name {
#[inline]
fn is_none(val: &u32) -> bool { *val == #hole }
}
#[allow(clippy::all)]
#unstable_api_attr
impl wasm_bindgen::convert::OptionIntoWasmAbi for #enum_name {
#[inline]
fn none() -> Self::Abi { #hole }
}
#[allow(clippy::all)]
#unstable_api_attr
impl wasm_bindgen::describe::WasmDescribe for #enum_name {
fn describe() {
use wasm_bindgen::describe::*;
@ -1196,12 +1227,13 @@ impl ToTokens for ast::ImportStatic {
})
.to_tokens(into);
Descriptor(
&shim_name,
quote! {
Descriptor {
ident: &shim_name,
inner: quote! {
<#ty as WasmDescribe>::describe();
},
)
unstable_api: false,
}
.to_tokens(into);
}
}
@ -1213,6 +1245,7 @@ impl ToTokens for ast::Const {
let vis = &self.vis;
let name = &self.name;
let ty = &self.ty;
let unstable_api_attr = util::maybe_unstable_api_attr(self.unstable_api);
let value: TokenStream = match self.value {
BooleanLiteral(false) => quote!(false),
@ -1244,6 +1277,7 @@ impl ToTokens for ast::Const {
if let Some(class) = &self.class {
(quote! {
#unstable_api_attr
impl #class {
#declaration
}
@ -1257,6 +1291,7 @@ impl ToTokens for ast::Const {
impl ToTokens for ast::Dictionary {
fn to_tokens(&self, tokens: &mut TokenStream) {
let unstable_api_attr = util::maybe_unstable_api_attr(self.unstable_api);
let name = &self.name;
let mut methods = TokenStream::new();
for field in self.fields.iter() {
@ -1304,11 +1339,13 @@ impl ToTokens for ast::Dictionary {
#[repr(transparent)]
#[allow(clippy::all)]
#[doc = #doc_comment]
#unstable_api_attr
pub struct #name {
obj: ::js_sys::Object,
}
#[allow(clippy::all)]
#unstable_api_attr
impl #name {
#ctor
#methods
@ -1316,6 +1353,7 @@ impl ToTokens for ast::Dictionary {
#[allow(bad_style)]
#[allow(clippy::all)]
#unstable_api_attr
const #const_name: () = {
use js_sys::Object;
use wasm_bindgen::describe::WasmDescribe;
@ -1443,7 +1481,11 @@ impl ToTokens for ast::DictionaryField {
/// Emits the necessary glue tokens for "descriptor", generating an appropriate
/// symbol name as well as attributes around the descriptor function itself.
struct Descriptor<'a, T>(&'a Ident, T);
struct Descriptor<'a, T> {
ident: &'a Ident,
inner: T,
unstable_api: bool,
}
impl<'a, T: ToTokens> ToTokens for Descriptor<'a, T> {
fn to_tokens(&self, tokens: &mut TokenStream) {
@ -1458,22 +1500,27 @@ impl<'a, T: ToTokens> ToTokens for Descriptor<'a, T> {
lazy_static::lazy_static! {
static ref DESCRIPTORS_EMITTED: Mutex<HashSet<String>> = Default::default();
}
let ident = self.ident;
if !DESCRIPTORS_EMITTED
.lock()
.unwrap()
.insert(self.0.to_string())
.insert(ident.to_string())
{
return;
}
let name = Ident::new(&format!("__wbindgen_describe_{}", self.0), self.0.span());
let inner = &self.1;
let name = Ident::new(&format!("__wbindgen_describe_{}", ident), ident.span());
let unstable_api_attr = util::maybe_unstable_api_attr(self.unstable_api);
let inner = &self.inner;
(quote! {
#[no_mangle]
#[allow(non_snake_case)]
#[doc(hidden)]
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
#[allow(clippy::all)]
#unstable_api_attr
pub extern "C" fn #name() {
use wasm_bindgen::describe::*;
// See definition of `link_mem_intrinsics` for what this is doing

View File

@ -188,6 +188,7 @@ fn shared_export<'a>(
function: shared_function(&export.function, intern),
method_kind,
start: export.start,
unstable_api: export.unstable_api,
})
}

View File

@ -113,10 +113,12 @@ pub fn ident_ty(ident: Ident) -> syn::Type {
}
pub fn wrap_import_function(function: ast::ImportFunction) -> ast::Import {
let unstable_api = function.unstable_api;
ast::Import {
module: ast::ImportModule::None,
js_namespace: None,
kind: ast::ImportKind::Function(function),
unstable_api,
}
}
@ -154,3 +156,20 @@ impl<T: Hash> fmt::Display for ShortHash<T> {
write!(f, "{:016x}", h.finish())
}
}
/// Create syn attribute for `#[cfg(web_sys_unstable_apis)]` and a doc comment.
pub fn unstable_api_attrs() -> proc_macro2::TokenStream {
quote::quote!(
#[cfg(web_sys_unstable_apis)]
#[doc = "\n\n*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as [described in the `wasm-bindgen` guide](https://rustwasm.github.io/docs/wasm-bindgen/web-sys/unstable-apis.html)*"]
)
}
pub fn maybe_unstable_api_attr(unstable_api: bool) -> Option<proc_macro2::TokenStream> {
if unstable_api {
Some(unstable_api_attrs())
} else {
None
}
}

View File

@ -22,6 +22,7 @@ struct AttributeParseState {
pub struct BindgenAttrs {
/// List of parsed attributes
pub attrs: Vec<(Cell<bool>, BindgenAttr)>,
pub unstable_api_attr: Option<syn::Attribute>,
}
macro_rules! attrgen {
@ -186,7 +187,7 @@ impl Default for BindgenAttrs {
// sanity check that we call `check_used` an appropriate number of
// times.
ATTRS.with(|state| state.parsed.set(state.parsed.get() + 1));
BindgenAttrs { attrs: Vec::new() }
BindgenAttrs { attrs: Vec::new(), unstable_api_attr: None, }
}
}
@ -353,6 +354,7 @@ impl<'a> ConvertToAst<BindgenAttrs> for &'a mut syn::ItemStruct {
getter: Ident::new(&getter, Span::call_site()),
setter: Ident::new(&setter, Span::call_site()),
comments,
unstable_api: false,
});
attrs.check_used()?;
}
@ -513,6 +515,7 @@ impl<'a> ConvertToAst<(BindgenAttrs, &'a ast::ImportModule)> for syn::ForeignIte
rust_name: self.sig.ident.clone(),
shim: Ident::new(&shim, Span::call_site()),
doc_comment: None,
unstable_api: false,
});
opts.check_used()?;
@ -550,6 +553,7 @@ impl ConvertToAst<BindgenAttrs> for syn::ForeignItemType {
Ok(ast::ImportKind::Type(ast::ImportType {
vis: self.vis,
attrs: self.attrs,
unstable_api: false,
doc_comment: None,
instanceof_shim: shim,
is_type_of,
@ -777,6 +781,7 @@ impl<'a> MacroParse<(Option<BindgenAttrs>, &'a mut TokenStream)> for syn::Item {
rust_class: None,
rust_name,
start,
unstable_api: false,
});
}
syn::Item::Struct(mut s) => {
@ -977,6 +982,7 @@ impl<'a, 'b> MacroParse<(&'a Ident, &'a str)> for &'b mut syn::ImplItemMethod {
rust_class: Some(class.clone()),
rust_name: self.sig.ident.clone(),
start: false,
unstable_api: false,
});
opts.check_used()?;
Ok(())
@ -1071,6 +1077,7 @@ impl MacroParse<()> for syn::ItemEnum {
variants,
comments,
hole,
unstable_api: false,
});
Ok(())
}
@ -1175,6 +1182,7 @@ impl MacroParse<ast::ImportModule> for syn::ForeignItem {
module,
js_namespace,
kind,
unstable_api: false,
});
Ok(())

View File

@ -94,6 +94,7 @@ macro_rules! shared_api {
function: Function<'a>,
method_kind: MethodKind<'a>,
start: bool,
unstable_api: bool,
}
struct Enum<'a> {

View File

@ -389,6 +389,105 @@ Geolocation = []
GetNotificationOptions = []
GetRootNodeOptions = []
GetUserMediaRequest = []
Gpu = []
GpuAdapter = []
GpuAddressMode = []
GpuBindGroup = []
GpuBindGroupBinding = []
GpuBindGroupDescriptor = []
GpuBindGroupLayout = []
GpuBindGroupLayoutBinding = []
GpuBindGroupLayoutDescriptor = []
GpuBindingType = []
GpuBlendDescriptor = []
GpuBlendFactor = []
GpuBlendOperation = []
GpuBuffer = []
GpuBufferBinding = []
GpuBufferCopyView = []
GpuBufferDescriptor = []
GpuBufferUsage = []
GpuCanvasContext = []
GpuColorDict = []
GpuColorStateDescriptor = []
GpuColorWrite = []
GpuCommandBuffer = []
GpuCommandBufferDescriptor = []
GpuCommandEncoder = []
GpuCommandEncoderDescriptor = []
GpuCompareFunction = []
GpuComputePassDescriptor = []
GpuComputePassEncoder = []
GpuComputePipeline = []
GpuComputePipelineDescriptor = []
GpuCullMode = []
GpuDepthStencilStateDescriptor = []
GpuDevice = []
GpuDeviceDescriptor = []
GpuDeviceLostInfo = []
GpuErrorFilter = []
GpuExtensionName = []
GpuExtent3dDict = []
GpuFence = []
GpuFenceDescriptor = []
GpuFilterMode = []
GpuFrontFace = []
GpuImageBitmapCopyView = []
GpuIndexFormat = []
GpuInputStepMode = []
GpuLimits = []
GpuLoadOp = []
GpuObjectDescriptorBase = []
GpuOrigin2dDict = []
GpuOrigin3dDict = []
GpuOutOfMemoryError = []
GpuPipelineDescriptorBase = []
GpuPipelineLayout = []
GpuPipelineLayoutDescriptor = []
GpuPowerPreference = []
GpuPrimitiveTopology = []
GpuProgrammableStageDescriptor = []
GpuQueue = []
GpuRasterizationStateDescriptor = []
GpuRenderBundle = []
GpuRenderBundleDescriptor = []
GpuRenderBundleEncoder = []
GpuRenderBundleEncoderDescriptor = []
GpuRenderPassColorAttachmentDescriptor = []
GpuRenderPassDepthStencilAttachmentDescriptor = []
GpuRenderPassDescriptor = []
GpuRenderPassEncoder = []
GpuRenderPipeline = []
GpuRenderPipelineDescriptor = []
GpuRequestAdapterOptions = []
GpuSampler = []
GpuSamplerDescriptor = []
GpuShaderModule = []
GpuShaderModuleDescriptor = []
GpuShaderStage = []
GpuStencilOperation = []
GpuStencilStateFaceDescriptor = []
GpuStoreOp = []
GpuSwapChain = []
GpuSwapChainDescriptor = []
GpuTexture = []
GpuTextureAspect = []
GpuTextureComponentType = []
GpuTextureCopyView = []
GpuTextureDescriptor = []
GpuTextureDimension = []
GpuTextureFormat = []
GpuTextureUsage = []
GpuTextureView = []
GpuTextureViewDescriptor = []
GpuTextureViewDimension = []
GpuUncapturedErrorEvent = []
GpuUncapturedErrorEventInit = []
GpuValidationError = []
GpuVertexAttributeDescriptor = []
GpuVertexBufferLayoutDescriptor = []
GpuVertexFormat = []
GpuVertexStateDescriptor = []
GridDeclaration = []
GridTrackState = []
GroupedHistoryEventInit = []

View File

@ -7,16 +7,12 @@ use std::fs;
use std::path::{self, PathBuf};
use std::process::Command;
fn main() -> Result<()> {
#[cfg(feature = "env_logger")]
env_logger::init();
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=webidls/enabled");
let entries = fs::read_dir("webidls/enabled").context("reading webidls/enabled directory")?;
/// Read all WebIDL files in a directory into a single `SourceFile`
fn read_source_from_path(dir: &str) -> Result<SourceFile> {
let entries = fs::read_dir(dir).context("reading webidls/enabled directory")?;
let mut source = SourceFile::default();
for entry in entries {
let entry = entry.context("getting webidls/enabled/*.webidl entry")?;
let entry = entry.context(format!("getting {}/*.webidl entry", dir))?;
let path = entry.path();
if path.extension() != Some(OsStr::new("webidl")) {
continue;
@ -27,6 +23,16 @@ fn main() -> Result<()> {
.with_context(|| format!("reading contents of file \"{}\"", path.display()))?;
}
Ok(source)
}
fn main() -> Result<()> {
#[cfg(feature = "env_logger")]
env_logger::init();
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=webidls/enabled");
println!("cargo:rerun-if-changed=webidls/unstable");
// Read our manifest, learn all `[feature]` directives with "toml parsing".
// Use all these names to match against environment variables set by Cargo
// to figure out which features are activated to we can pass that down to
@ -63,7 +69,10 @@ fn main() -> Result<()> {
Some(&allowed[..])
};
let bindings = match wasm_bindgen_webidl::compile(&source.contents, allowed) {
let source = read_source_from_path("webidls/enabled")?;
let unstable_source = read_source_from_path("webidls/unstable")?;
let bindings = match wasm_bindgen_webidl::compile(&source.contents, &unstable_source.contents, allowed) {
Ok(bindings) => bindings,
Err(e) => {
if let Some(err) = e.downcast_ref::<wasm_bindgen_webidl::WebIDLParseError>() {

View File

@ -1,638 +0,0 @@
/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*
* The origin of this IDL file is
* https://github.com/gpuweb/gpuweb/blob/master/design/sketch.webidl
*/
typedef unsigned long u32;
typedef unsigned long long u64;
// ****************************************************************************
// ERROR HANDLING
// ****************************************************************************
enum WebGPULogEntryType {
"device-lost",
"validation-error",
"recoverable-out-of-memory",
};
[Pref="dom.webgpu.enable"]
interface WebGPULogEntry {
readonly attribute WebGPULogEntryType type;
readonly attribute any obj;
readonly attribute DOMString? reason;
};
enum WebGPUObjectStatus {
"valid",
"out-of-memory",
"invalid",
};
typedef (WebGPUBuffer or WebGPUTexture) WebGPUStatusable;
callback WebGPULogCallback = void (WebGPULogEntry error);
// ****************************************************************************
// SHADER RESOURCES (buffer, textures, texture views, samples)
// ****************************************************************************
// Buffer
typedef u32 WebGPUBufferUsageFlags;
[Pref="dom.webgpu.enable"]
interface WebGPUBufferUsage {
const u32 NONE = 0;
const u32 MAP_READ = 1;
const u32 MAP_WRITE = 2;
const u32 TRANSFER_SRC = 4;
const u32 TRANSFER_DST = 8;
const u32 INDEX = 16;
const u32 VERTEX = 32;
const u32 UNIFORM = 64;
const u32 STORAGE = 128;
};
dictionary WebGPUBufferDescriptor {
u32 size;
WebGPUBufferUsageFlags usage;
};
[Pref="dom.webgpu.enable"]
interface WebGPUBuffer {
readonly attribute ArrayBuffer? mapping;
void unmap();
};
// Texture view
dictionary WebGPUTextureViewDescriptor {
// TODO Investigate what goes in there.
};
[Pref="dom.webgpu.enable"]
interface WebGPUTextureView {
};
// Texture
typedef u32 WebGPUTextureDimensionEnum;
[Pref="dom.webgpu.enable"]
interface WebGPUTextureDimension {
const u32 e1D = 0;
const u32 e2D = 1;
const u32 e3D = 2;
// TODO other dimensions (cube, arrays)
};
typedef u32 WebGPUTextureFormatEnum;
[Pref="dom.webgpu.enable"]
interface WebGPUTextureFormat {
const u32 R8_G8_B8_A8_UNORM = 0;
const u32 R8_G8_B8_A8_UINT = 1;
const u32 B8_G8_R8_A8_UNORM = 2;
const u32 D32_FLOAT_S8_UINT = 3;
// TODO other formats
};
typedef u32 WebGPUTextureUsageFlags;
[Pref="dom.webgpu.enable"]
interface WebGPUTextureUsage {
const u32 NONE = 0;
const u32 TRANSFER_SRC = 1;
const u32 TRANSFER_DST = 2;
const u32 SAMPLED = 4;
const u32 STORAGE = 8;
const u32 OUTPUT_ATTACHMENT = 16;
const u32 PRESENT = 32;
};
dictionary WebGPUTextureDescriptor {
u32 width;
u32 height;
u32 depth;
u32 arraySize;
WebGPUTextureDimensionEnum dimension;
WebGPUTextureFormatEnum format;
WebGPUTextureUsageFlags usage;
};
[Pref="dom.webgpu.enable"]
interface WebGPUTexture {
WebGPUTextureView createTextureView(optional WebGPUTextureViewDescriptor desc);
};
// Sampler
typedef u32 WebGPUFilterModeEnum;
[Pref="dom.webgpu.enable"]
interface WebGPUFilterMode {
const u32 NEAREST = 0;
const u32 LINEAR = 1;
};
dictionary WebGPUSamplerDescriptor {
WebGPUFilterModeEnum magFilter;
WebGPUFilterModeEnum minFilter;
WebGPUFilterModeEnum mipmapFilter;
};
[Pref="dom.webgpu.enable"]
interface WebGPUSampler {
};
// ****************************************************************************
// BINDING MODEL (bindgroup layout, bindgroup)
// ****************************************************************************
// BindGroupLayout
typedef u32 WebGPUShaderStageFlags;
[Pref="dom.webgpu.enable"]
interface WebGPUShaderStageBit {
const u32 NONE = 0;
const u32 VERTEX = 1;
const u32 FRAGMENT = 2;
const u32 COMPUTE = 4;
};
typedef u32 WebGPUBindingTypeEnum;
[Pref="dom.webgpu.enable"]
interface WebGPUBindingType {
const u32 UNIFORM_BUFFER = 0;
const u32 SAMPLER = 1;
const u32 SAMPLED_TEXTURE = 2;
const u32 STORAGE_BUFFER = 3;
// TODO other binding types (storage images, ...)
};
dictionary WebGPUBindGroupBinding {
WebGPUShaderStageFlags visibility;
WebGPUBindingTypeEnum type;
u32 start;
u32 count;
};
dictionary WebGPUBindGroupLayoutDescriptor {
sequence<WebGPUBindGroupBinding> bindingTypes;
};
[Pref="dom.webgpu.enable"]
interface WebGPUBindGroupLayout {
};
// PipelineLayout
dictionary WebGPUPipelineLayoutDescriptor {
sequence<WebGPUBindGroupLayout> bindGroupLayouts;
};
[Pref="dom.webgpu.enable"]
interface WebGPUPipelineLayout {
};
// BindGroup
/* Moved to WebGPUExtras.webidl for now.
dictionary WebGPUBufferBinding {
WebGPUBuffer buffer;
u32 offset;
u32 size;
};
*/
typedef (WebGPUSampler or WebGPUTextureView or WebGPUBufferBinding) WebGPUBindingResource;
dictionary WebGPUBinding {
sequence<WebGPUBindingResource> resources;
u32 start;
u32 count;
};
dictionary WebGPUBindGroupDescriptor {
WebGPUBindGroupLayout layout;
sequence<WebGPUBinding> bindings;
};
[Pref="dom.webgpu.enable"]
interface WebGPUBindGroup {
};
// ****************************************************************************
// PIPELINE CREATION (blend state, DS state, ..., pipelines)
// ****************************************************************************
// BlendState
typedef u32 WebGPUBlendFactorEnum;
[Pref="dom.webgpu.enable"]
interface WebGPUBlendFactor {
const u32 ZERO = 0;
const u32 ONE = 1;
const u32 SRC_COLOR = 2;
const u32 ONE_MINUS_SRC_COLOR = 3;
const u32 SRC_ALPHA = 4;
const u32 ONE_MINUS_SRC_ALPHA = 5;
const u32 DST_COLOR = 6;
const u32 ONE_MINUS_DST_COLOR = 7;
const u32 DST_ALPHA = 8;
const u32 ONE_MINUS_DST_ALPHA = 9;
const u32 SRC_ALPHA_SATURATED = 10;
const u32 BLEND_COLOR = 11;
const u32 ONE_MINUS_BLEND_COLOR = 12;
};
typedef u32 WebGPUBlendOperationEnum;
[Pref="dom.webgpu.enable"]
interface WebGPUBlendOperation {
const u32 ADD = 0;
const u32 SUBTRACT = 1;
const u32 REVERSE_SUBTRACT = 2;
const u32 MIN = 3;
const u32 MAX = 4;
};
typedef u32 WebGPUColorWriteFlags;
[Pref="dom.webgpu.enable"]
interface WebGPUColorWriteBits {
const u32 NONE = 0;
const u32 RED = 1;
const u32 GREEN = 2;
const u32 BLUE = 4;
const u32 ALPHA = 8;
const u32 ALL = 15;
};
dictionary WebGPUBlendDescriptor {
WebGPUBlendFactorEnum srcFactor;
WebGPUBlendFactorEnum dstFactor;
WebGPUBlendOperationEnum operation;
};
dictionary WebGPUBlendStateDescriptor {
boolean blendEnabled;
WebGPUBlendDescriptor alpha;
WebGPUBlendDescriptor color;
WebGPUColorWriteFlags writeMask;
};
[Pref="dom.webgpu.enable"]
interface WebGPUBlendState {
};
// DepthStencilState
typedef u32 WebGPUCompareFunctionEnum;
[Pref="dom.webgpu.enable"]
interface WebGPUCompareFunction {
const u32 NEVER = 0;
const u32 LESS = 1;
const u32 LESS_EQUAL = 2;
const u32 GREATER = 3;
const u32 GREATER_EQUAL = 4;
const u32 EQUAL = 5;
const u32 NOT_EQUAL = 6;
const u32 ALWAYS = 7;
};
typedef u32 WebGPUStencilOperationEnum;
[Pref="dom.webgpu.enable"]
interface WebGPUStencilOperation {
const u32 KEEP = 0;
const u32 ZERO = 1;
const u32 REPLACE = 2;
const u32 INVERT = 3;
const u32 INCREMENT_CLAMP = 4;
const u32 DECREMENT_CLAMP = 5;
const u32 INCREMENT_WRAP = 6;
const u32 DECREMENT_WRAP = 7;
};
dictionary WebGPUStencilStateFaceDescriptor {
WebGPUCompareFunctionEnum compare;
WebGPUStencilOperationEnum stencilFailOp;
WebGPUStencilOperationEnum depthFailOp;
WebGPUStencilOperationEnum passOp;
};
dictionary WebGPUDepthStencilStateDescriptor {
boolean depthWriteEnabled;
WebGPUCompareFunctionEnum depthCompare;
WebGPUStencilStateFaceDescriptor front;
WebGPUStencilStateFaceDescriptor back;
u32 stencilReadMask;
u32 stencilWriteMask;
};
[Pref="dom.webgpu.enable"]
interface WebGPUDepthStencilState {
};
// InputState
typedef u32 WebGPUIndexFormatEnum;
[Pref="dom.webgpu.enable"]
interface WebGPUIndexFormat {
const u32 UINT16 = 0;
const u32 UINT32 = 1;
};
typedef u32 WebGPUVertexFormatEnum;
[Pref="dom.webgpu.enable"]
interface WebGPUVertexFormat {
const u32 FLOAT_R32_G32_B32_A32 = 0;
const u32 FLOAT_R32_G32_B32 = 1;
const u32 FLOAT_R32_G32 = 2;
const u32 FLOAT_R32 = 3;
// TODO other vertex formats
};
typedef u32 WebGPUInputStepModeEnum;
[Pref="dom.webgpu.enable"]
interface WebGPUInputStepMode {
const u32 VERTEX = 0;
const u32 INSTANCE = 1;
};
dictionary WebGPUVertexAttributeDescriptor {
u32 shaderLocation;
u32 inputSlot;
u32 offset;
WebGPUVertexFormatEnum format;
};
dictionary WebGPUVertexInputDescriptor {
u32 inputSlot;
u32 stride;
WebGPUInputStepModeEnum stepMode;
};
dictionary WebGPUInputStateDescriptor {
WebGPUIndexFormatEnum indexFormat;
sequence<WebGPUVertexAttributeDescriptor> attributes;
sequence<WebGPUVertexInputDescriptor> inputs;
};
[Pref="dom.webgpu.enable"]
interface WebGPUInputState {
};
// ShaderModule
dictionary WebGPUShaderModuleDescriptor {
required ArrayBuffer code;
};
[Pref="dom.webgpu.enable"]
interface WebGPUShaderModule {
};
// AttachmentState
dictionary WebGPUAttachmentStateDescriptor {
sequence<WebGPUTextureFormatEnum> formats;
// TODO other stuff like sample count etc.
};
[Pref="dom.webgpu.enable"]
interface WebGPUAttachmentState {
};
// Common stuff for ComputePipeline and RenderPipeline
typedef u32 WebGPUShaderStageEnum;
[Pref="dom.webgpu.enable"]
interface WebGPUShaderStage {
const u32 VERTEX = 0;
const u32 FRAGMENT = 1;
const u32 COMPUTE = 2;
};
dictionary WebGPUPipelineStageDescriptor {
required WebGPUShaderModule shaderModule;
required WebGPUShaderStageEnum stage;
required DOMString entryPoint;
// TODO other stuff like specialization constants?
};
dictionary WebGPUPipelineDescriptorBase {
required WebGPUPipelineLayout layout;
sequence<WebGPUPipelineStageDescriptor> stages;
};
// ComputePipeline
dictionary WebGPUComputePipelineDescriptor : WebGPUPipelineDescriptorBase {
};
[Pref="dom.webgpu.enable"]
interface WebGPUComputePipeline {
};
// WebGPURenderPipeline
typedef u32 WebGPUPrimitiveTopologyEnum;
[Pref="dom.webgpu.enable"]
interface WebGPUPrimitiveTopology {
const u32 POINT_LIST = 0;
const u32 LINE_LIST = 1;
const u32 LINE_STRIP = 2;
const u32 TRIANGLE_LIST = 3;
const u32 TRIANGLE_STRIP = 4;
};
dictionary WebGPURenderPipelineDescriptor : WebGPUPipelineDescriptorBase {
WebGPUPrimitiveTopologyEnum primitiveTopology;
sequence<WebGPUBlendState> blendState;
WebGPUDepthStencilState depthStencilState;
WebGPUInputState inputState;
WebGPUAttachmentState attachmentState;
// TODO other properties
};
[Pref="dom.webgpu.enable"]
interface WebGPURenderPipeline {
};
// ****************************************************************************
// COMMAND RECORDING (Command buffer and all relevant structures)
// ****************************************************************************
typedef u32 WebGPULoadOpEnum;
[Pref="dom.webgpu.enable"]
interface WebGPULoadOp {
const u32 CLEAR = 0;
const u32 LOAD = 1;
};
typedef u32 WebGPUStoreOpEnum;
[Pref="dom.webgpu.enable"]
interface WebGPUStoreOp {
const u32 STORE = 0;
};
dictionary WebGPURenderPassAttachmentDescriptor {
WebGPUTextureView attachment;
WebGPULoadOpEnum loadOp;
WebGPUStoreOpEnum storeOp;
};
dictionary WebGPURenderPassDescriptor {
sequence<WebGPURenderPassAttachmentDescriptor> colorAttachments;
WebGPURenderPassAttachmentDescriptor depthStencilAttachment;
};
[Pref="dom.webgpu.enable"]
interface WebGPUCommandBuffer {
};
dictionary WebGPUCommandEncoderDescriptor {
};
[Pref="dom.webgpu.enable"]
interface WebGPUCommandEncoder {
WebGPUCommandBuffer finishEncoding();
// Commands allowed outside of "passes"
void copyBufferToBuffer(WebGPUBuffer src,
u32 srcOffset,
WebGPUBuffer dst,
u32 dstOffset,
u32 size);
// TODO figure out all the arguments required for these
void copyBufferToTexture();
void copyTextureToBuffer();
void copyTextureToTexture();
void blit();
void transitionBuffer(WebGPUBuffer b, WebGPUBufferUsageFlags f);
// Allowed in both compute and render passes
void setPushConstants(WebGPUShaderStageFlags stage,
u32 offset,
u32 count,
ArrayBuffer data);
void setBindGroup(u32 index, WebGPUBindGroup bindGroup);
void setPipeline((WebGPUComputePipeline or WebGPURenderPipeline) pipeline);
// Compute pass commands
void beginComputePass();
void endComputePass();
void dispatch(u32 x, u32 y, u32 z);
// Render pass commands
void beginRenderPass(optional WebGPURenderPassDescriptor descriptor);
void endRenderPass();
void setBlendColor(float r, float g, float b, float a);
void setIndexBuffer(WebGPUBuffer buffer, u32 offset);
void setVertexBuffers(u32 startSlot, sequence<WebGPUBuffer> buffers, sequence<u32> offsets);
void draw(u32 vertexCount, u32 instanceCount, u32 firstVertex, u32 firstInstance);
void drawIndexed(u32 indexCount, u32 instanceCount, u32 firstIndex, u32 firstInstance, u32 firstVertex);
// TODO add missing commands
};
// ****************************************************************************
// OTHER (Fence, Queue SwapChain, Device)
// ****************************************************************************
// Fence
[Pref="dom.webgpu.enable"]
interface WebGPUFence {
boolean wait(double milliseconds);
readonly attribute Promise<void> promise;
};
// Queue
[Pref="dom.webgpu.enable"]
interface WebGPUQueue {
void submit(sequence<WebGPUCommandBuffer> buffers);
WebGPUFence insertFence();
};
// SwapChain / RenderingContext
dictionary WebGPUSwapChainDescriptor {
WebGPUTextureUsageFlags usage;
WebGPUTextureFormatEnum format;
u32 width;
u32 height;
};
[Pref="dom.webgpu.enable"]
interface WebGPUSwapChain {
void configure(optional WebGPUSwapChainDescriptor descriptor);
WebGPUTexture getNextTexture();
void present();
};
//[Pref="dom.webgpu.enable"]
//interface WebGPURenderingContext : WebGPUSwapChain {
//};
// WebGPU "namespace" used for device creation
dictionary WebGPUExtensions {
boolean anisotropicFiltering;
boolean logicOp; // Previously a "Feature".
};
dictionary WebGPULimits {
u32 maxBindGroups;
};
// Device
[Pref="dom.webgpu.enable"]
interface WebGPUDevice {
readonly attribute WebGPUAdapter adapter;
WebGPUExtensions extensions();
WebGPULimits limits();
WebGPUBuffer createBuffer(optional WebGPUBufferDescriptor descriptor);
WebGPUTexture createTexture(optional WebGPUTextureDescriptor descriptor);
WebGPUSampler createSampler(optional WebGPUSamplerDescriptor descriptor);
WebGPUBindGroupLayout createBindGroupLayout(optional WebGPUBindGroupLayoutDescriptor descriptor);
WebGPUPipelineLayout createPipelineLayout(optional WebGPUPipelineLayoutDescriptor descriptor);
WebGPUBindGroup createBindGroup(optional WebGPUBindGroupDescriptor descriptor);
WebGPUBlendState createBlendState(optional WebGPUBlendStateDescriptor descriptor);
WebGPUDepthStencilState createDepthStencilState(optional WebGPUDepthStencilStateDescriptor descriptor);
WebGPUInputState createInputState(optional WebGPUInputStateDescriptor descriptor);
WebGPUShaderModule createShaderModule(WebGPUShaderModuleDescriptor descriptor);
WebGPUAttachmentState createAttachmentState(optional WebGPUAttachmentStateDescriptor descriptor);
WebGPUComputePipeline createComputePipeline(WebGPUComputePipelineDescriptor descriptor);
WebGPURenderPipeline createRenderPipeline(WebGPURenderPipelineDescriptor descriptor);
WebGPUCommandEncoder createCommandEncoder(optional WebGPUCommandEncoderDescriptor descriptor);
WebGPUQueue getQueue();
attribute WebGPULogCallback onLog;
Promise<WebGPUObjectStatus> getObjectStatus(WebGPUStatusable obj);
};
dictionary WebGPUDeviceDescriptor {
WebGPUExtensions extensions;
//WebGPULimits limits; Don't expose higher limits for now.
// TODO are other things configurable like queues?
};
[Pref="dom.webgpu.enable"]
interface WebGPUAdapter {
readonly attribute DOMString name;
WebGPUExtensions extensions();
//WebGPULimits limits(); Don't expose higher limits for now.
WebGPUDevice createDevice(optional WebGPUDeviceDescriptor descriptor);
};
enum WebGPUPowerPreference { "default", "low-power", "high-performance" };
dictionary WebGPUAdapterDescriptor {
WebGPUPowerPreference powerPreference;
};
[Pref="dom.webgpu.enable"]
interface WebGPU {
WebGPUAdapter getAdapter(optional WebGPUAdapterDescriptor desc);
};
// Add a "webgpu" member to Window that contains the global instance of a "WebGPU"
interface mixin WebGPUProvider {
[SameObject, Replaceable, Pref="dom.webgpu.enable"] readonly attribute WebGPU webgpu;
};
//Window includes WebGPUProvider;

View File

@ -1,14 +0,0 @@
/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Some parts of WebGPU.webidl need to be pulled into a different file due to a codegen
* bug/missing support.
*/
dictionary WebGPUBufferBinding {
WebGPUBuffer buffer;
u32 offset;
u32 size;
};

View File

@ -272,5 +272,3 @@ dictionary IdleRequestOptions {
};
callback IdleRequestCallback = void (IdleDeadline deadline);
//Window includes WebGPUProvider;

View File

@ -0,0 +1,864 @@
interface mixin GPUObjectBase {
attribute DOMString? label;
};
dictionary GPUObjectDescriptorBase {
DOMString label;
};
[Exposed=Window]
partial interface Navigator {
[SameObject] readonly attribute GPU gpu;
};
[Exposed=DedicatedWorker]
partial interface WorkerNavigator {
[SameObject] readonly attribute GPU gpu;
};
[Exposed=(Window, DedicatedWorker)]
interface GPU {
Promise<GPUAdapter> requestAdapter(optional GPURequestAdapterOptions options = {});
};
dictionary GPURequestAdapterOptions {
GPUPowerPreference powerPreference;
};
enum GPUPowerPreference {
"low-power",
"high-performance"
};
interface GPUAdapter {
readonly attribute DOMString name;
readonly attribute FrozenArray<GPUExtensionName> extensions;
//readonly attribute GPULimits limits; Dont expose higher limits for now.
Promise<GPUDevice> requestDevice(optional GPUDeviceDescriptor descriptor = {});
};
dictionary GPUDeviceDescriptor : GPUObjectDescriptorBase {
sequence<GPUExtensionName> extensions = [];
GPULimits limits = {};
};
enum GPUExtensionName {
"texture-compression-bc"
};
dictionary GPULimits {
GPUSize32 maxBindGroups = 4;
GPUSize32 maxDynamicUniformBuffersPerPipelineLayout = 8;
GPUSize32 maxDynamicStorageBuffersPerPipelineLayout = 4;
GPUSize32 maxSampledTexturesPerShaderStage = 16;
GPUSize32 maxSamplersPerShaderStage = 16;
GPUSize32 maxStorageBuffersPerShaderStage = 4;
GPUSize32 maxStorageTexturesPerShaderStage = 4;
GPUSize32 maxUniformBuffersPerShaderStage = 12;
};
[Exposed=(Window, DedicatedWorker), Serializable]
interface GPUDevice : EventTarget {
[SameObject] readonly attribute GPUAdapter adapter;
readonly attribute FrozenArray<GPUExtensionName> extensions;
readonly attribute object limits;
[SameObject] readonly attribute GPUQueue defaultQueue;
GPUBuffer createBuffer(GPUBufferDescriptor descriptor);
GPUMappedBuffer createBufferMapped(GPUBufferDescriptor descriptor);
GPUTexture createTexture(GPUTextureDescriptor descriptor);
GPUSampler createSampler(optional GPUSamplerDescriptor descriptor = {});
GPUBindGroupLayout createBindGroupLayout(GPUBindGroupLayoutDescriptor descriptor);
GPUPipelineLayout createPipelineLayout(GPUPipelineLayoutDescriptor descriptor);
GPUBindGroup createBindGroup(GPUBindGroupDescriptor descriptor);
GPUShaderModule createShaderModule(GPUShaderModuleDescriptor descriptor);
GPUComputePipeline createComputePipeline(GPUComputePipelineDescriptor descriptor);
GPURenderPipeline createRenderPipeline(GPURenderPipelineDescriptor descriptor);
GPUCommandEncoder createCommandEncoder(optional GPUCommandEncoderDescriptor descriptor = {});
GPURenderBundleEncoder createRenderBundleEncoder(GPURenderBundleEncoderDescriptor descriptor);
};
GPUDevice includes GPUObjectBase;
[Serializable]
interface GPUBuffer {
Promise<ArrayBuffer> mapReadAsync();
Promise<ArrayBuffer> mapWriteAsync();
void unmap();
void destroy();
};
GPUBuffer includes GPUObjectBase;
dictionary GPUBufferDescriptor : GPUObjectDescriptorBase {
required GPUSize64 size;
required GPUBufferUsageFlags usage;
};
typedef [EnforceRange] unsigned long GPUBufferUsageFlags;
interface GPUBufferUsage {
const GPUBufferUsageFlags MAP_READ = 0x0001;
const GPUBufferUsageFlags MAP_WRITE = 0x0002;
const GPUBufferUsageFlags COPY_SRC = 0x0004;
const GPUBufferUsageFlags COPY_DST = 0x0008;
const GPUBufferUsageFlags INDEX = 0x0010;
const GPUBufferUsageFlags VERTEX = 0x0020;
const GPUBufferUsageFlags UNIFORM = 0x0040;
const GPUBufferUsageFlags STORAGE = 0x0080;
const GPUBufferUsageFlags INDIRECT = 0x0100;
};
[Serializable]
interface GPUTexture {
GPUTextureView createView(optional GPUTextureViewDescriptor descriptor = {});
void destroy();
};
GPUTexture includes GPUObjectBase;
dictionary GPUTextureDescriptor : GPUObjectDescriptorBase {
required GPUExtent3D size;
GPUIntegerCoordinate arrayLayerCount = 1;
GPUIntegerCoordinate mipLevelCount = 1;
GPUSize32 sampleCount = 1;
GPUTextureDimension dimension = "2d";
required GPUTextureFormat format;
required GPUTextureUsageFlags usage;
};
enum GPUTextureDimension {
"1d",
"2d",
"3d"
};
typedef [EnforceRange] unsigned long GPUTextureUsageFlags;
interface GPUTextureUsage {
const GPUTextureUsageFlags COPY_SRC = 0x01;
const GPUTextureUsageFlags COPY_DST = 0x02;
const GPUTextureUsageFlags SAMPLED = 0x04;
const GPUTextureUsageFlags STORAGE = 0x08;
const GPUTextureUsageFlags OUTPUT_ATTACHMENT = 0x10;
};
interface GPUTextureView {
};
GPUTextureView includes GPUObjectBase;
dictionary GPUTextureViewDescriptor : GPUObjectDescriptorBase {
GPUTextureFormat format;
GPUTextureViewDimension dimension;
GPUTextureAspect aspect = "all";
GPUIntegerCoordinate baseMipLevel = 0;
GPUIntegerCoordinate mipLevelCount = 0;
GPUIntegerCoordinate baseArrayLayer = 0;
GPUIntegerCoordinate arrayLayerCount = 0;
};
enum GPUTextureViewDimension {
"1d",
"2d",
"2d-array",
"cube",
"cube-array",
"3d"
};
enum GPUTextureAspect {
"all",
"stencil-only",
"depth-only"
};
enum GPUTextureFormat {
// 8-bit formats
"r8unorm",
"r8snorm",
"r8uint",
"r8sint",
// 16-bit formats
"r16uint",
"r16sint",
"r16float",
"rg8unorm",
"rg8snorm",
"rg8uint",
"rg8sint",
// 32-bit formats
"r32uint",
"r32sint",
"r32float",
"rg16uint",
"rg16sint",
"rg16float",
"rgba8unorm",
"rgba8unorm-srgb",
"rgba8snorm",
"rgba8uint",
"rgba8sint",
"bgra8unorm",
"bgra8unorm-srgb",
// Packed 32-bit formats
"rgb10a2unorm",
"rg11b10float",
// 64-bit formats
"rg32uint",
"rg32sint",
"rg32float",
"rgba16uint",
"rgba16sint",
"rgba16float",
// 128-bit formats
"rgba32uint",
"rgba32sint",
"rgba32float",
// Depth and stencil formats
"depth32float",
"depth24plus",
"depth24plus-stencil8"
};
enum GPUTextureComponentType {
"float",
"sint",
"uint"
};
interface GPUSampler {
};
GPUSampler includes GPUObjectBase;
dictionary GPUSamplerDescriptor : GPUObjectDescriptorBase {
GPUAddressMode addressModeU = "clamp-to-edge";
GPUAddressMode addressModeV = "clamp-to-edge";
GPUAddressMode addressModeW = "clamp-to-edge";
GPUFilterMode magFilter = "nearest";
GPUFilterMode minFilter = "nearest";
GPUFilterMode mipmapFilter = "nearest";
float lodMinClamp = 0;
float lodMaxClamp = 0xffffffff; // TODO: What should this be? Was Number.MAX_VALUE.
GPUCompareFunction compare = "never";
};
enum GPUAddressMode {
"clamp-to-edge",
"repeat",
"mirror-repeat"
};
enum GPUFilterMode {
"nearest",
"linear"
};
enum GPUCompareFunction {
"never",
"less",
"equal",
"less-equal",
"greater",
"not-equal",
"greater-equal",
"always"
};
[Serializable]
interface GPUBindGroupLayout {
};
GPUBindGroupLayout includes GPUObjectBase;
dictionary GPUBindGroupLayoutDescriptor : GPUObjectDescriptorBase {
required sequence<GPUBindGroupLayoutBinding> bindings;
};
dictionary GPUBindGroupLayoutBinding {
required GPUIndex32 binding;
required GPUShaderStageFlags visibility;
required GPUBindingType type;
GPUTextureViewDimension textureDimension = "2d";
GPUTextureComponentType textureComponentType = "float";
boolean multisampled = false;
boolean hasDynamicOffset = false;
};
typedef [EnforceRange] unsigned long GPUShaderStageFlags;
interface GPUShaderStage {
const GPUShaderStageFlags VERTEX = 0x1;
const GPUShaderStageFlags FRAGMENT = 0x2;
const GPUShaderStageFlags COMPUTE = 0x4;
};
enum GPUBindingType {
"uniform-buffer",
"storage-buffer",
"readonly-storage-buffer",
"sampler",
"sampled-texture",
"storage-texture"
// TODO: other binding types
};
interface GPUBindGroup {
};
GPUBindGroup includes GPUObjectBase;
dictionary GPUBindGroupDescriptor : GPUObjectDescriptorBase {
required GPUBindGroupLayout layout;
required sequence<GPUBindGroupBinding> bindings;
};
typedef (GPUSampler or GPUTextureView or GPUBufferBinding) GPUBindingResource;
dictionary GPUBindGroupBinding {
required GPUIndex32 binding;
required GPUBindingResource resource;
};
dictionary GPUBufferBinding {
required GPUBuffer buffer;
GPUSize64 offset = 0;
GPUSize64 size;
};
interface GPUPipelineLayout {
};
GPUPipelineLayout includes GPUObjectBase;
dictionary GPUPipelineLayoutDescriptor : GPUObjectDescriptorBase {
required sequence<GPUBindGroupLayout> bindGroupLayouts;
};
[Serializable]
interface GPUShaderModule {
};
GPUShaderModule includes GPUObjectBase;
typedef (Uint32Array or DOMString) GPUShaderCode;
dictionary GPUShaderModuleDescriptor : GPUObjectDescriptorBase {
required GPUShaderCode code;
};
dictionary GPUPipelineDescriptorBase : GPUObjectDescriptorBase {
required GPUPipelineLayout layout;
};
dictionary GPUProgrammableStageDescriptor {
required GPUShaderModule module;
required DOMString entryPoint;
// TODO: other stuff like specialization constants?
};
[Serializable]
interface GPUComputePipeline {
};
GPUComputePipeline includes GPUObjectBase;
dictionary GPUComputePipelineDescriptor : GPUPipelineDescriptorBase {
required GPUProgrammableStageDescriptor computeStage;
};
[Serializable]
interface GPURenderPipeline {
};
GPURenderPipeline includes GPUObjectBase;
dictionary GPURenderPipelineDescriptor : GPUPipelineDescriptorBase {
required GPUProgrammableStageDescriptor vertexStage;
GPUProgrammableStageDescriptor fragmentStage;
required GPUPrimitiveTopology primitiveTopology;
GPURasterizationStateDescriptor rasterizationState = {};
required sequence<GPUColorStateDescriptor> colorStates;
GPUDepthStencilStateDescriptor depthStencilState;
GPUVertexStateDescriptor vertexState = {};
GPUSize32 sampleCount = 1;
GPUSampleMask sampleMask = 0xFFFFFFFF;
boolean alphaToCoverageEnabled = false;
// TODO: other properties
};
enum GPUPrimitiveTopology {
"point-list",
"line-list",
"line-strip",
"triangle-list",
"triangle-strip"
};
dictionary GPURasterizationStateDescriptor {
GPUFrontFace frontFace = "ccw";
GPUCullMode cullMode = "none";
GPUDepthBias depthBias = 0;
float depthBiasSlopeScale = 0;
float depthBiasClamp = 0;
};
enum GPUFrontFace {
"ccw",
"cw"
};
enum GPUCullMode {
"none",
"front",
"back"
};
dictionary GPUColorStateDescriptor {
required GPUTextureFormat format;
GPUBlendDescriptor alphaBlend = {};
GPUBlendDescriptor colorBlend = {};
GPUColorWriteFlags writeMask = 0xF; // GPUColorWrite.ALL
};
typedef [EnforceRange] unsigned long GPUColorWriteFlags;
interface GPUColorWrite {
const GPUColorWriteFlags RED = 0x1;
const GPUColorWriteFlags GREEN = 0x2;
const GPUColorWriteFlags BLUE = 0x4;
const GPUColorWriteFlags ALPHA = 0x8;
const GPUColorWriteFlags ALL = 0xF;
};
dictionary GPUBlendDescriptor {
GPUBlendFactor srcFactor = "one";
GPUBlendFactor dstFactor = "zero";
GPUBlendOperation operation = "add";
};
enum GPUBlendFactor {
"zero",
"one",
"src-color",
"one-minus-src-color",
"src-alpha",
"one-minus-src-alpha",
"dst-color",
"one-minus-dst-color",
"dst-alpha",
"one-minus-dst-alpha",
"src-alpha-saturated",
"blend-color",
"one-minus-blend-color"
};
enum GPUBlendOperation {
"add",
"subtract",
"reverse-subtract",
"min",
"max"
};
enum GPUStencilOperation {
"keep",
"zero",
"replace",
"invert",
"increment-clamp",
"decrement-clamp",
"increment-wrap",
"decrement-wrap"
};
dictionary GPUDepthStencilStateDescriptor {
required GPUTextureFormat format;
boolean depthWriteEnabled = false;
GPUCompareFunction depthCompare = "always";
GPUStencilStateFaceDescriptor stencilFront = {};
GPUStencilStateFaceDescriptor stencilBack = {};
GPUStencilValue stencilReadMask = 0xFFFFFFFF;
GPUStencilValue stencilWriteMask = 0xFFFFFFFF;
};
dictionary GPUStencilStateFaceDescriptor {
GPUCompareFunction compare = "always";
GPUStencilOperation failOp = "keep";
GPUStencilOperation depthFailOp = "keep";
GPUStencilOperation passOp = "keep";
};
enum GPUIndexFormat {
"uint16",
"uint32"
};
enum GPUVertexFormat {
"uchar2",
"uchar4",
"char2",
"char4",
"uchar2norm",
"uchar4norm",
"char2norm",
"char4norm",
"ushort2",
"ushort4",
"short2",
"short4",
"ushort2norm",
"ushort4norm",
"short2norm",
"short4norm",
"half2",
"half4",
"float",
"float2",
"float3",
"float4",
"uint",
"uint2",
"uint3",
"uint4",
"int",
"int2",
"int3",
"int4"
};
enum GPUInputStepMode {
"vertex",
"instance"
};
dictionary GPUVertexStateDescriptor {
GPUIndexFormat indexFormat = "uint32";
sequence<GPUVertexBufferLayoutDescriptor?> vertexBuffers = [];
};
dictionary GPUVertexBufferLayoutDescriptor {
required GPUSize64 arrayStride;
GPUInputStepMode stepMode = "vertex";
required sequence<GPUVertexAttributeDescriptor> attributes;
};
dictionary GPUVertexAttributeDescriptor {
required GPUVertexFormat format;
required GPUSize64 offset;
required GPUIndex32 shaderLocation;
};
interface GPUCommandBuffer {
};
GPUCommandBuffer includes GPUObjectBase;
dictionary GPUCommandBufferDescriptor : GPUObjectDescriptorBase {
};
interface GPUCommandEncoder {
GPURenderPassEncoder beginRenderPass(GPURenderPassDescriptor descriptor);
GPUComputePassEncoder beginComputePass(optional GPUComputePassDescriptor descriptor = {});
void copyBufferToBuffer(
GPUBuffer source,
GPUSize64 sourceOffset,
GPUBuffer destination,
GPUSize64 destinationOffset,
GPUSize64 size);
void copyBufferToTexture(
GPUBufferCopyView source,
GPUTextureCopyView destination,
GPUExtent3D copySize);
void copyTextureToBuffer(
GPUTextureCopyView source,
GPUBufferCopyView destination,
GPUExtent3D copySize);
void copyTextureToTexture(
GPUTextureCopyView source,
GPUTextureCopyView destination,
GPUExtent3D copySize);
void pushDebugGroup(DOMString groupLabel);
void popDebugGroup();
void insertDebugMarker(DOMString markerLabel);
GPUCommandBuffer finish(optional GPUCommandBufferDescriptor descriptor = {});
};
GPUCommandEncoder includes GPUObjectBase;
dictionary GPUCommandEncoderDescriptor : GPUObjectDescriptorBase {
// TODO: reusability flag?
};
dictionary GPUBufferCopyView {
required GPUBuffer buffer;
GPUSize64 offset = 0;
required GPUSize32 rowPitch;
required GPUSize32 imageHeight;
};
dictionary GPUTextureCopyView {
required GPUTexture texture;
GPUIntegerCoordinate mipLevel = 0;
GPUIntegerCoordinate arrayLayer = 0;
GPUOrigin3D origin = {};
};
dictionary GPUImageBitmapCopyView {
required ImageBitmap imageBitmap;
GPUOrigin2D origin = {};
};
interface mixin GPUProgrammablePassEncoder {
void setBindGroup(GPUIndex32 index, GPUBindGroup bindGroup,
optional sequence<GPUBufferDynamicOffset> dynamicOffsets = []);
void setBindGroup(GPUIndex32 index, GPUBindGroup bindGroup,
Uint32Array dynamicOffsetsData,
GPUSize64 dynamicOffsetsDataStart,
GPUSize32 dynamicOffsetsDataLength);
void pushDebugGroup(DOMString groupLabel);
void popDebugGroup();
void insertDebugMarker(DOMString markerLabel);
};
interface GPUComputePassEncoder {
void setPipeline(GPUComputePipeline pipeline);
void dispatch(GPUSize32 x, optional GPUSize32 y = 1, optional GPUSize32 z = 1);
void dispatchIndirect(GPUBuffer indirectBuffer, GPUSize64 indirectOffset);
void endPass();
};
GPUComputePassEncoder includes GPUObjectBase;
GPUComputePassEncoder includes GPUProgrammablePassEncoder;
dictionary GPUComputePassDescriptor : GPUObjectDescriptorBase {
};
interface mixin GPURenderEncoderBase {
void setPipeline(GPURenderPipeline pipeline);
void setIndexBuffer(GPUBuffer buffer, optional GPUSize64 offset = 0);
void setVertexBuffer(GPUIndex32 slot, GPUBuffer buffer, optional GPUSize64 offset = 0);
void draw(GPUSize32 vertexCount, GPUSize32 instanceCount,
GPUSize32 firstVertex, GPUSize32 firstInstance);
void drawIndexed(GPUSize32 indexCount, GPUSize32 instanceCount,
GPUSize32 firstIndex, GPUSignedOffset32 baseVertex, GPUSize32 firstInstance);
void drawIndirect(GPUBuffer indirectBuffer, GPUSize64 indirectOffset);
void drawIndexedIndirect(GPUBuffer indirectBuffer, GPUSize64 indirectOffset);
};
interface GPURenderPassEncoder {
void setViewport(float x, float y,
float width, float height,
float minDepth, float maxDepth);
void setScissorRect(GPUIntegerCoordinate x, GPUIntegerCoordinate y,
GPUIntegerCoordinate width, GPUIntegerCoordinate height);
void setBlendColor(GPUColor color);
void setStencilReference(GPUStencilValue reference);
void executeBundles(sequence<GPURenderBundle> bundles);
void endPass();
};
GPURenderPassEncoder includes GPUObjectBase;
GPURenderPassEncoder includes GPUProgrammablePassEncoder;
GPURenderPassEncoder includes GPURenderEncoderBase;
dictionary GPURenderPassDescriptor : GPUObjectDescriptorBase {
required sequence<GPURenderPassColorAttachmentDescriptor> colorAttachments;
GPURenderPassDepthStencilAttachmentDescriptor depthStencilAttachment;
};
dictionary GPURenderPassColorAttachmentDescriptor {
required GPUTextureView attachment;
GPUTextureView resolveTarget;
required (GPULoadOp or GPUColor) loadValue;
GPUStoreOp storeOp = "store";
};
dictionary GPURenderPassDepthStencilAttachmentDescriptor {
required GPUTextureView attachment;
required (GPULoadOp or float) depthLoadValue;
required GPUStoreOp depthStoreOp;
required (GPULoadOp or GPUStencilValue) stencilLoadValue;
required GPUStoreOp stencilStoreOp;
};
enum GPULoadOp {
"load"
};
enum GPUStoreOp {
"store",
"clear"
};
interface GPURenderBundle {
};
GPURenderBundle includes GPUObjectBase;
dictionary GPURenderBundleDescriptor : GPUObjectDescriptorBase {
};
interface GPURenderBundleEncoder {
GPURenderBundle finish(optional GPURenderBundleDescriptor descriptor = {});
};
GPURenderBundleEncoder includes GPUObjectBase;
GPURenderBundleEncoder includes GPUProgrammablePassEncoder;
GPURenderBundleEncoder includes GPURenderEncoderBase;
dictionary GPURenderBundleEncoderDescriptor : GPUObjectDescriptorBase {
required sequence<GPUTextureFormat> colorFormats;
GPUTextureFormat depthStencilFormat;
GPUSize32 sampleCount = 1;
};
interface GPUQueue {
void submit(sequence<GPUCommandBuffer> commandBuffers);
GPUFence createFence(optional GPUFenceDescriptor descriptor = {});
void signal(GPUFence fence, GPUFenceValue signalValue);
void copyImageBitmapToTexture(
GPUImageBitmapCopyView source,
GPUTextureCopyView destination,
GPUExtent3D copySize);
};
GPUQueue includes GPUObjectBase;
interface GPUFence {
GPUFenceValue getCompletedValue();
Promise<void> onCompletion(GPUFenceValue completionValue);
};
GPUFence includes GPUObjectBase;
dictionary GPUFenceDescriptor : GPUObjectDescriptorBase {
GPUFenceValue initialValue = 0;
};
interface GPUCanvasContext {
GPUSwapChain configureSwapChain(GPUSwapChainDescriptor descriptor);
Promise<GPUTextureFormat> getSwapChainPreferredFormat(GPUDevice device);
};
dictionary GPUSwapChainDescriptor : GPUObjectDescriptorBase {
required GPUDevice device;
required GPUTextureFormat format;
GPUTextureUsageFlags usage = 0x10; // GPUTextureUsage.OUTPUT_ATTACHMENT
};
interface GPUSwapChain {
GPUTexture getCurrentTexture();
};
GPUSwapChain includes GPUObjectBase;
interface GPUDeviceLostInfo {
readonly attribute DOMString message;
};
partial interface GPUDevice {
readonly attribute Promise<GPUDeviceLostInfo> lost;
};
enum GPUErrorFilter {
"none",
"out-of-memory",
"validation"
};
interface GPUOutOfMemoryError {
constructor();
};
interface GPUValidationError {
constructor(DOMString message);
readonly attribute DOMString message;
};
typedef (GPUOutOfMemoryError or GPUValidationError) GPUError;
partial interface GPUDevice {
void pushErrorScope(GPUErrorFilter filter);
Promise<GPUError?> popErrorScope();
};
[
Exposed=(Window, DedicatedWorker)
]
interface GPUUncapturedErrorEvent : Event {
constructor(
DOMString type,
GPUUncapturedErrorEventInit gpuUncapturedErrorEventInitDict
);
[SameObject] readonly attribute GPUError error;
};
dictionary GPUUncapturedErrorEventInit : EventInit {
required GPUError error;
};
partial interface GPUDevice {
[Exposed=(Window, DedicatedWorker)]
attribute EventHandler onuncapturederror;
};
typedef [EnforceRange] unsigned long GPUBufferDynamicOffset;
typedef [EnforceRange] unsigned long long GPUFenceValue;
typedef [EnforceRange] unsigned long GPUStencilValue;
typedef [EnforceRange] unsigned long GPUSampleMask;
typedef [EnforceRange] long GPUDepthBias;
typedef [EnforceRange] unsigned long long GPUSize64;
typedef [EnforceRange] unsigned long GPUIntegerCoordinate;
typedef [EnforceRange] unsigned long GPUIndex32;
typedef [EnforceRange] unsigned long GPUSize32;
typedef [EnforceRange] long GPUSignedOffset32;
dictionary GPUColorDict {
required double r;
required double g;
required double b;
required double a;
};
typedef (sequence<double> or GPUColorDict) GPUColor;
dictionary GPUOrigin2DDict {
GPUIntegerCoordinate x = 0;
GPUIntegerCoordinate y = 0;
};
typedef (sequence<GPUIntegerCoordinate> or GPUOrigin2DDict) GPUOrigin2D;
dictionary GPUOrigin3DDict {
GPUIntegerCoordinate x = 0;
GPUIntegerCoordinate y = 0;
GPUIntegerCoordinate z = 0;
};
typedef (sequence<GPUIntegerCoordinate> or GPUOrigin3DDict) GPUOrigin3D;
dictionary GPUExtent3DDict {
required GPUIntegerCoordinate width;
required GPUIntegerCoordinate height;
required GPUIntegerCoordinate depth;
};
typedef (sequence<GPUIntegerCoordinate> or GPUExtent3DDict) GPUExtent3D;
typedef sequence<(GPUBuffer or ArrayBuffer)> GPUMappedBuffer;

View File

@ -11,13 +11,22 @@ fn main() {
let idls = fs::read_dir(".")
.unwrap()
.map(|f| f.unwrap().path())
.filter(|f| f.extension().and_then(|s| s.to_str()) == Some("webidl"))
.map(|f| (fs::read_to_string(&f).unwrap(), f));
.filter(|path| path.extension().and_then(|s| s.to_str()) == Some("webidl"))
.map(|path| {
let unstable = path.file_name().and_then(|s| s.to_str()) == Some("unstable.webidl");
let file = fs::read_to_string(&path).unwrap();
(file, path, unstable)
});
let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
for (i, (idl, path)) in idls.enumerate() {
for (i, (idl, path, unstable)) in idls.enumerate() {
println!("processing {:?}", path);
let mut generated_rust = wasm_bindgen_webidl::compile(&idl, None).unwrap();
let (stable_source, experimental_source) = if unstable {
(String::new(), idl)
} else {
(idl, String::new())
};
let mut generated_rust = wasm_bindgen_webidl::compile(&stable_source, &experimental_source, None).unwrap();
generated_rust.insert_str(
0,

View File

@ -13,3 +13,4 @@ pub mod namespace;
pub mod no_interface;
pub mod simple;
pub mod throws;
pub mod unstable;

View File

@ -0,0 +1,13 @@
global.GetUnstableInterface = class {
static get() {
return {
enum_value(dict) {
if (!dict) {
return 0;
}
return dict.unstableEnum === "a" ? 1 : 2;
}
}
}
}

View File

@ -0,0 +1,17 @@
use wasm_bindgen_test::*;
include!(concat!(env!("OUT_DIR"), "/unstable.rs"));
#[cfg(web_sys_unstable_apis)]
#[wasm_bindgen_test]
fn can_use_unstable_apis() {
let unstable_interface = GetUnstableInterface::get();
assert_eq!(0u32, unstable_interface.enum_value());
let mut dict = UnstableDictionary::new();
dict.unstable_enum(UnstableEnum::B);
assert_eq!(
2u32,
unstable_interface.enum_value_with_unstable_dictionary(&dict)
);
}

19
crates/webidl-tests/unstable.webidl vendored Normal file
View File

@ -0,0 +1,19 @@
enum UnstableEnum {
"a",
"b"
};
dictionary UnstableDictionary {
UnstableEnum unstableEnum;
};
typedef unsigned long UnstableTypedef;
[NoInterfaceObject]
partial interface UnstableInterface {
UnstableTypedef enum_value(optional UnstableDictionary unstableDictionary = {});
};
interface GetUnstableInterface {
static UnstableInterface get();
};

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,13 +55,28 @@ 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) => {
#[derive(Clone, Copy, PartialEq)]
pub(crate) enum ApiStability {
Stable,
Unstable,
}
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) => bail!("needed {:?} more bytes", needed),
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
@ -73,19 +88,26 @@ fn parse(webidl_source: &str, allowed_types: Option<&[&str]>) -> Result<Program>
weedle::Context::Code(remaining, _) => remaining.len(),
_ => 0,
};
let pos = webidl_source.len() - remaining;
let pos = source.len() - remaining;
bail!(WebIDLParseError(pos))
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,
),
);
}

View File

@ -88,6 +88,7 @@
- [Function Overloads](./web-sys/function-overloads.md)
- [Type Translations](./web-sys/type-translations.md)
- [Inheritance](./web-sys/inheritance.md)
- [Unstable APIs](./web-sys/unstable-apis.md)
- [Testing with `wasm-bindgen-test`](./wasm-bindgen-test/index.md)
- [Usage](./wasm-bindgen-test/usage.md)

View File

@ -0,0 +1,31 @@
# Unstable APIs
It's common for browsers to implement parts of a web API while the specification
for that API is still being written. The API may require frequent changes as the
specification continues to be developed, so the WebIDL is relatively unstable.
This causes some challenges for `web-sys` because it means `web-sys` would have
to make breaking API changes whenever the WebIDL changes. It also means that
previously published `web-sys` versions would be invalid, because the browser
API may have been changed to match the updated WebIDL.
To avoid frequent breaking changes for unstable APIs, `web-sys` hides all
unstable APIs through an attribute that looks like:
```rust
#[cfg(web_sys_unstable_apis)]
pub struct Foo;
```
By hiding unstable APIs through an attribute, it's necessary for crates to
explicitly opt-in to these reduced stability guarantees in order to use these
APIs. Specifically, these APIs do not follow semver and may break whenever the
WebIDL changes.
Crates can opt-in to unstable APIs at compile-time by passing the `cfg` flag
`web_sys_unstable_apis`. Typically the `RUSTFLAGS` environment variable is used
to do this. For example:
```bash
RUSTFLAGS=--cfg=web_sys_unstable_apis cargo run
```