Skip to content

Commit fa47f57

Browse files
Merge branch 'main' into import-getter-rename
2 parents e140112 + 03207b3 commit fa47f57

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+1537
-201
lines changed

CHANGELOG.md

+12
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,15 @@
1414
* Added JSDoc type annotations to C-style enums.
1515
[#4192](https://github.com/rustwasm/wasm-bindgen/pull/4192)
1616

17+
* Added support for C-style enums with negative discriminants.
18+
[#4204](https://github.com/rustwasm/wasm-bindgen/pull/4204)
19+
20+
* Added bindings for `MediaStreamTrack.getCapabilities`.
21+
[#4236](https://github.com/rustwasm/wasm-bindgen/pull/4236)
22+
23+
* Added WASM ABI support for `u128` and `i128`
24+
[#4222](https://github.com/rustwasm/wasm-bindgen/pull/4222)
25+
1726
### Changed
1827

1928
* String enums now generate private TypeScript types but only if used.
@@ -66,6 +75,9 @@
6675
* Fixed calls to `JsCast::instanceof()` not respecting JavaScript namespaces.
6776
[#4241](https://github.com/rustwasm/wasm-bindgen/pull/4241)
6877

78+
* Fixed imports for functions using `this` and late binding.
79+
[#4225](https://github.com/rustwasm/wasm-bindgen/pull/4225)
80+
6981
--------------------------------------------------------------------------------
7082

7183
## [0.2.95](https://github.com/rustwasm/wasm-bindgen/compare/0.2.94...0.2.95)

crates/backend/src/ast.rs

+3
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,9 @@ pub struct Enum {
445445
pub rust_name: Ident,
446446
/// The name of this enum in JS code
447447
pub js_name: String,
448+
/// Whether the variant values and hole are signed, meaning that they
449+
/// represent the bits of a `i32` value.
450+
pub signed: bool,
448451
/// The variants provided by this enum
449452
pub variants: Vec<Variant>,
450453
/// The doc comments on this enum, if any

crates/backend/src/codegen.rs

+17-12
Original file line numberDiff line numberDiff line change
@@ -1535,10 +1535,15 @@ impl ToTokens for ast::Enum {
15351535
let name_len = name_str.len() as u32;
15361536
let name_chars = name_str.chars().map(|c| c as u32);
15371537
let hole = &self.hole;
1538+
let underlying = if self.signed {
1539+
quote! { i32 }
1540+
} else {
1541+
quote! { u32 }
1542+
};
15381543
let cast_clauses = self.variants.iter().map(|variant| {
15391544
let variant_name = &variant.name;
15401545
quote! {
1541-
if js == #enum_name::#variant_name as u32 {
1546+
if js == #enum_name::#variant_name as #underlying {
15421547
#enum_name::#variant_name
15431548
}
15441549
}
@@ -1548,20 +1553,20 @@ impl ToTokens for ast::Enum {
15481553
(quote! {
15491554
#[automatically_derived]
15501555
impl #wasm_bindgen::convert::IntoWasmAbi for #enum_name {
1551-
type Abi = u32;
1556+
type Abi = #underlying;
15521557

15531558
#[inline]
1554-
fn into_abi(self) -> u32 {
1555-
self as u32
1559+
fn into_abi(self) -> #underlying {
1560+
self as #underlying
15561561
}
15571562
}
15581563

15591564
#[automatically_derived]
15601565
impl #wasm_bindgen::convert::FromWasmAbi for #enum_name {
1561-
type Abi = u32;
1566+
type Abi = #underlying;
15621567

15631568
#[inline]
1564-
unsafe fn from_abi(js: u32) -> Self {
1569+
unsafe fn from_abi(js: #underlying) -> Self {
15651570
#(#cast_clauses else)* {
15661571
#wasm_bindgen::throw_str("invalid enum value passed")
15671572
}
@@ -1571,13 +1576,13 @@ impl ToTokens for ast::Enum {
15711576
#[automatically_derived]
15721577
impl #wasm_bindgen::convert::OptionFromWasmAbi for #enum_name {
15731578
#[inline]
1574-
fn is_none(val: &u32) -> bool { *val == #hole }
1579+
fn is_none(val: &Self::Abi) -> bool { *val == #hole as #underlying }
15751580
}
15761581

15771582
#[automatically_derived]
15781583
impl #wasm_bindgen::convert::OptionIntoWasmAbi for #enum_name {
15791584
#[inline]
1580-
fn none() -> Self::Abi { #hole }
1585+
fn none() -> Self::Abi { #hole as #underlying }
15811586
}
15821587

15831588
#[automatically_derived]
@@ -1597,7 +1602,7 @@ impl ToTokens for ast::Enum {
15971602
#wasm_bindgen::JsValue
15981603
{
15991604
fn from(value: #enum_name) -> Self {
1600-
#wasm_bindgen::JsValue::from_f64((value as u32).into())
1605+
#wasm_bindgen::JsValue::from_f64((value as #underlying).into())
16011606
}
16021607
}
16031608

@@ -1608,7 +1613,7 @@ impl ToTokens for ast::Enum {
16081613
fn try_from_js_value(value: #wasm_bindgen::JsValue)
16091614
-> #wasm_bindgen::__rt::core::result::Result<Self, <#enum_name as #wasm_bindgen::convert::TryFromJsValue>::Error> {
16101615
use #wasm_bindgen::__rt::core::convert::TryFrom;
1611-
let js = f64::try_from(&value)? as u32;
1616+
let js = f64::try_from(&value)? as #underlying;
16121617

16131618
#wasm_bindgen::__rt::core::result::Result::Ok(
16141619
#(#try_from_cast_clauses else)* {
@@ -1894,9 +1899,9 @@ fn respan(input: TokenStream, span: &dyn ToTokens) -> TokenStream {
18941899

18951900
for (i, token) in spans.into_iter().enumerate() {
18961901
if i == 0 {
1897-
first_span = token.span();
1902+
first_span = Span::call_site().located_at(token.span());
18981903
}
1899-
last_span = token.span();
1904+
last_span = Span::call_site().located_at(token.span());
19001905
}
19011906

19021907
let mut new_tokens = Vec::new();

crates/backend/src/encode.rs

+1
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ fn shared_function<'a>(func: &'a ast::Function, _intern: &'a Interner) -> Functi
239239
fn shared_enum<'a>(e: &'a ast::Enum, intern: &'a Interner) -> Enum<'a> {
240240
Enum {
241241
name: &e.js_name,
242+
signed: e.signed,
242243
variants: e
243244
.variants
244245
.iter()

crates/cli-support/src/descriptor.rs

+6
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ tys! {
1919
U32
2020
I64
2121
U64
22+
I128
23+
U128
2224
F32
2325
F64
2426
BOOLEAN
@@ -55,6 +57,8 @@ pub enum Descriptor {
5557
U32,
5658
I64,
5759
U64,
60+
I128,
61+
U128,
5862
F32,
5963
F64,
6064
Boolean,
@@ -132,11 +136,13 @@ impl Descriptor {
132136
I16 => Descriptor::I16,
133137
I32 => Descriptor::I32,
134138
I64 => Descriptor::I64,
139+
I128 => Descriptor::I128,
135140
U8 if clamped => Descriptor::ClampedU8,
136141
U8 => Descriptor::U8,
137142
U16 => Descriptor::U16,
138143
U32 => Descriptor::U32,
139144
U64 => Descriptor::U64,
145+
U128 => Descriptor::U128,
140146
F32 => Descriptor::F32,
141147
F64 => Descriptor::F64,
142148
BOOLEAN => Descriptor::Boolean,

crates/cli-support/src/js/binding.rs

+54-1
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,23 @@ fn instruction(
620620
)
621621
}
622622

623+
fn int128_to_int64x2(val: &str) -> (String, String) {
624+
// we don't need to perform any conversion here, because the JS
625+
// WebAssembly API will automatically convert the bigints to 64 bits
626+
// for us. This even allows us to ignore signedness.
627+
let low = val.to_owned();
628+
let high = format!("{val} >> BigInt(64)");
629+
(low, high)
630+
}
631+
fn int64x2_to_int128(low: String, high: String, signed: bool) -> String {
632+
let low = format!("BigInt.asUintN(64, {low})");
633+
if signed {
634+
format!("({low} | ({high} << BigInt(64)))")
635+
} else {
636+
format!("({low} | (BigInt.asUintN(64, {high}) << BigInt(64)))")
637+
}
638+
}
639+
623640
match instr {
624641
Instruction::ArgGet(n) => {
625642
let arg = js.arg(*n).to_string();
@@ -712,6 +729,36 @@ fn instruction(
712729
}
713730
}
714731

732+
Instruction::Int128ToWasm => {
733+
let val = js.pop();
734+
js.assert_bigint(&val);
735+
let (low, high) = int128_to_int64x2(&val);
736+
js.push(low);
737+
js.push(high);
738+
}
739+
Instruction::WasmToInt128 { signed } => {
740+
let high = js.pop();
741+
let low = js.pop();
742+
js.push(int64x2_to_int128(low, high, *signed));
743+
}
744+
745+
Instruction::OptionInt128ToWasm => {
746+
let val = js.pop();
747+
js.cx.expose_is_like_none();
748+
js.assert_optional_bigint(&val);
749+
let (low, high) = int128_to_int64x2(&val);
750+
js.push(format!("!isLikeNone({val})"));
751+
js.push(format!("isLikeNone({val}) ? BigInt(0) : {low}"));
752+
js.push(format!("isLikeNone({val}) ? BigInt(0) : {high}"));
753+
}
754+
Instruction::OptionWasmToInt128 { signed } => {
755+
let high = js.pop();
756+
let low = js.pop();
757+
let present = js.pop();
758+
let val = int64x2_to_int128(low, high, *signed);
759+
js.push(format!("{present} === 0 ? undefined : {val}"));
760+
}
761+
715762
Instruction::WasmToStringEnum { name } => {
716763
let index = js.pop();
717764
js.cx.expose_string_enum(name);
@@ -983,6 +1030,8 @@ fn instruction(
9831030
js.push(format!(
9841031
"isLikeNone({val}) ? {zero} : {val}",
9851032
zero = if *ty == ValType::I64 {
1033+
// We can't use bigint literals for now. See:
1034+
// https://github.com/rustwasm/wasm-bindgen/issues/4246
9861035
"BigInt(0)"
9871036
} else {
9881037
"0"
@@ -1500,7 +1549,11 @@ fn adapter2ts(ty: &AdapterType, dst: &mut String, refs: Option<&mut HashSet<TsRe
15001549
| AdapterType::F32
15011550
| AdapterType::F64
15021551
| AdapterType::NonNull => dst.push_str("number"),
1503-
AdapterType::I64 | AdapterType::S64 | AdapterType::U64 => dst.push_str("bigint"),
1552+
AdapterType::I64
1553+
| AdapterType::S64
1554+
| AdapterType::U64
1555+
| AdapterType::S128
1556+
| AdapterType::U128 => dst.push_str("bigint"),
15041557
AdapterType::String => dst.push_str("string"),
15051558
AdapterType::Externref => dst.push_str("any"),
15061559
AdapterType::Bool => dst.push_str("boolean"),

crates/cli-support/src/js/mod.rs

+15
Original file line numberDiff line numberDiff line change
@@ -3062,6 +3062,21 @@ __wbg_set_wasm(wasm);"
30623062
}
30633063
}
30643064

3065+
if let JsImportName::Global { .. } | JsImportName::VendorPrefixed { .. } = js.name {
3066+
// We generally cannot import globals directly, because users can
3067+
// change most globals at runtime.
3068+
//
3069+
// An obvious example of this when the object literally changes
3070+
// (e.g. binding `foo.bar`), but polyfills can also change the
3071+
// object or fundtion.
3072+
//
3073+
// Late binding is another issue. The function might not even be
3074+
// defined when the Wasm module is instantiated. In such cases,
3075+
// there is an observable difference between a direct import and a
3076+
// JS shim calling the function.
3077+
return Ok(false);
3078+
}
3079+
30653080
self.expose_not_defined();
30663081
let name = self.import_name(js)?;
30673082
let js = format!(

crates/cli-support/src/wit/incoming.rs

+28
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,20 @@ impl InstructionBuilder<'_, '_> {
9090
Descriptor::U32 => self.number(AdapterType::U32, WasmVT::I32),
9191
Descriptor::I64 => self.number(AdapterType::S64, WasmVT::I64),
9292
Descriptor::U64 => self.number(AdapterType::U64, WasmVT::I64),
93+
Descriptor::I128 => {
94+
self.instruction(
95+
&[AdapterType::S128],
96+
Instruction::Int128ToWasm,
97+
&[AdapterType::I64, AdapterType::I64],
98+
);
99+
}
100+
Descriptor::U128 => {
101+
self.instruction(
102+
&[AdapterType::U128],
103+
Instruction::Int128ToWasm,
104+
&[AdapterType::I64, AdapterType::I64],
105+
);
106+
}
93107
Descriptor::F32 => {
94108
self.get(AdapterType::F32);
95109
self.output.push(AdapterType::F32);
@@ -285,6 +299,20 @@ impl InstructionBuilder<'_, '_> {
285299
Descriptor::F32 => self.in_option_sentinel64_f32(AdapterType::F32),
286300
Descriptor::F64 => self.in_option_native(ValType::F64),
287301
Descriptor::I64 | Descriptor::U64 => self.in_option_native(ValType::I64),
302+
Descriptor::I128 => {
303+
self.instruction(
304+
&[AdapterType::S128.option()],
305+
Instruction::OptionInt128ToWasm,
306+
&[AdapterType::I32, AdapterType::I64, AdapterType::I64],
307+
);
308+
}
309+
Descriptor::U128 => {
310+
self.instruction(
311+
&[AdapterType::U128.option()],
312+
Instruction::OptionInt128ToWasm,
313+
&[AdapterType::I32, AdapterType::I64, AdapterType::I64],
314+
);
315+
}
288316
Descriptor::Boolean => {
289317
self.instruction(
290318
&[AdapterType::Bool.option()],

crates/cli-support/src/wit/mod.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -894,18 +894,20 @@ impl<'a> Context<'a> {
894894
}
895895

896896
fn enum_(&mut self, enum_: decode::Enum<'_>) -> Result<(), Error> {
897+
let signed = enum_.signed;
897898
let aux = AuxEnum {
898899
name: enum_.name.to_string(),
899900
comments: concatenate_comments(&enum_.comments),
900901
variants: enum_
901902
.variants
902903
.iter()
903904
.map(|v| {
904-
(
905-
v.name.to_string(),
906-
v.value,
907-
concatenate_comments(&v.comments),
908-
)
905+
let value = if signed {
906+
v.value as i32 as i64
907+
} else {
908+
v.value as i64
909+
};
910+
(v.name.to_string(), value, concatenate_comments(&v.comments))
909911
})
910912
.collect(),
911913
generate_typescript: enum_.generate_typescript,

crates/cli-support/src/wit/nonstandard.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ pub struct AuxEnum {
170170
pub comments: String,
171171
/// A list of variants with their name, value and comments
172172
/// and whether typescript bindings should be generated for each variant
173-
pub variants: Vec<(String, u32, String)>,
173+
pub variants: Vec<(String, i64, String)>,
174174
/// Whether typescript bindings should be generated for this enum.
175175
pub generate_typescript: bool,
176176
}

crates/cli-support/src/wit/outgoing.rs

+30
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,20 @@ impl InstructionBuilder<'_, '_> {
6565
Descriptor::U32 => self.outgoing_i32(AdapterType::U32),
6666
Descriptor::I64 => self.outgoing_i64(AdapterType::I64),
6767
Descriptor::U64 => self.outgoing_i64(AdapterType::U64),
68+
Descriptor::I128 => {
69+
self.instruction(
70+
&[AdapterType::I64, AdapterType::I64],
71+
Instruction::WasmToInt128 { signed: true },
72+
&[AdapterType::S128],
73+
);
74+
}
75+
Descriptor::U128 => {
76+
self.instruction(
77+
&[AdapterType::I64, AdapterType::I64],
78+
Instruction::WasmToInt128 { signed: false },
79+
&[AdapterType::U128],
80+
);
81+
}
6882
Descriptor::F32 => {
6983
self.get(AdapterType::F32);
7084
self.output.push(AdapterType::F32);
@@ -267,6 +281,20 @@ impl InstructionBuilder<'_, '_> {
267281
Descriptor::U64 => self.option_native(false, ValType::I64),
268282
Descriptor::F32 => self.out_option_sentinel64(AdapterType::F32),
269283
Descriptor::F64 => self.option_native(true, ValType::F64),
284+
Descriptor::I128 => {
285+
self.instruction(
286+
&[AdapterType::I32, AdapterType::I64, AdapterType::I64],
287+
Instruction::OptionWasmToInt128 { signed: true },
288+
&[AdapterType::S128.option()],
289+
);
290+
}
291+
Descriptor::U128 => {
292+
self.instruction(
293+
&[AdapterType::I32, AdapterType::I64, AdapterType::I64],
294+
Instruction::OptionWasmToInt128 { signed: false },
295+
&[AdapterType::U128.option()],
296+
);
297+
}
270298
Descriptor::Boolean => {
271299
self.instruction(
272300
&[AdapterType::I32],
@@ -360,6 +388,8 @@ impl InstructionBuilder<'_, '_> {
360388
| Descriptor::F64
361389
| Descriptor::I64
362390
| Descriptor::U64
391+
| Descriptor::I128
392+
| Descriptor::U128
363393
| Descriptor::Boolean
364394
| Descriptor::Char
365395
| Descriptor::Enum { .. }

0 commit comments

Comments
 (0)