profile
viewpoint

alexcrichton/AudioStreamer 70

A streaming audio player class (AudioStreamer) for Mac OS X and iPhone.

alexcrichton/bzip2-rs 40

libbz2 (bzip2 compression) bindings for Rust

alexcrichton/bufstream 29

A buffered I/O stream for Rust

alexcrichton/brotli2-rs 22

Brotli encoders/decoers for Rust

alexcrichton/ars 1

ar in Rust

alexcrichton/atty 1

are you or are you not a tty?

alexcrichton/binaryen 1

Compiler infrastructure and toolchain library for WebAssembly, in C++

alexcrichton/bzip2-ruby 1

Original libbz2 ruby C bindings from Guy Decoux, with some new love

alexcrichton/1password-teams-open-source 0

Get a free 1Password Teams membership for your open source project

push eventbytecodealliance/wasmtime

Deploy from CI

commit sha 0a0e02e9476846e0fe2e16d9a311e4d24e281f42

Deploy c32c2e8272a5aad1a22436beaf854f6092964715 to gh-pages

view details

push time in 20 hours

push eventbytecodealliance/wasmtime

Deploy from CI

commit sha 2e681e51ebc8bd0af596b789487e1695c9393ccb

Deploy 0dd77d36f895df70c6e82758f23f553365c2f25f to gh-pages

view details

push time in 2 days

push eventbytecodealliance/wasmtime

Deploy from CI

commit sha 6cb031f1ac4a05fc7cb9486907f60341471684de

Deploy e430984ac4eebda508b485e431b3ae041758c9e4 to gh-pages

view details

push time in 2 days

pull request commentbytecodealliance/wasmtime

print TrapReason::Error causes

This is actually intentional I believe that Display for T where T: Error should only print one "stack frame" of error. The full backtrace of errors (causes) should be printed elsewhere. The connection via fn source should cause other libraries, like anyhow print the full list of causes. Could you elaborate a bit on how this came up for you?

leoyvens

comment created time in 2 days

push eventbytecodealliance/wasmtime

Deploy from CI

commit sha eb010046b7caf6b579deabf44a2cfc129957e094

Deploy 16afca445106add1b85bb2d0d3aada61e078f5e5 to gh-pages

view details

push time in 2 days

delete branch alexcrichton/wasmtime

delete branch : update-checkout

delete time in 2 days

push eventbytecodealliance/wasmtime

Alex Crichton

commit sha 16afca445106add1b85bb2d0d3aada61e078f5e5

Update the checkout action to v2 (#1791) I think this pulls in a few nice updates like depth 0 cloning, better logs, etc. In any case seems good to update while we can!

view details

push time in 2 days

PR merged bytecodealliance/wasmtime

Update the checkout action to v2

I think this pulls in a few nice updates like depth 0 cloning, better logs, etc. In any case seems good to update while we can!

+11 -11

0 comment

1 changed file

alexcrichton

pr closed time in 2 days

push eventbytecodealliance/wasmtime

Deploy from CI

commit sha 745c0ebe68ce77fc06976b9a696fe8581e313267

Deploy c274efe9c10c68450cb044b42d759625a6b51853 to gh-pages

view details

push time in 2 days

PR opened bytecodealliance/wasmtime

Update the checkout action to v2

I think this pulls in a few nice updates like depth 0 cloning, better logs, etc. In any case seems good to update while we can!

+11 -11

0 comment

1 changed file

pr created time in 2 days

create barnchalexcrichton/wasmtime

branch : update-checkout

created branch time in 2 days

push eventbytecodealliance/wasmtime

Deploy from CI

commit sha cb2b5ad4633258d0bdd8f3e9363813eaaf7a038b

Deploy 0b3b9c298ef8709ef97686fd7ebeeb1f4802b6b7 to gh-pages

view details

push time in 2 days

delete branch alexcrichton/wasm-tools

delete branch : update-wabt-again

delete time in 2 days

PR merged bytecodealliance/wasm-tools

Run even more tests!

Wabt is quite speedy to update, so let's go ahead and check these in while I'm thinking about it so we can run these tests.

+4 -40

0 comment

3 changed files

alexcrichton

pr closed time in 2 days

push eventbytecodealliance/wasmtime

Deploy from CI

commit sha 4d9e8abc167693bfde61b6f4a90a99c7de6780dc

Deploy 8fce8ddefc975a6cff2d6bdef57665582f620376 to gh-pages

view details

push time in 2 days

pull request commentrust-lang/rust

Update compiler-builtins

Looks like beta is using 0.1.27 of compiler-builtins (as opposed to 0.1.28 for master). The one extra version of changes brings in only one other PR, which looks like it should work on beta too.

With that in mind I believe that it should be pretty safe to update the compiler-builtins dependency on beta, so yeah seems like a good backport to me. I do think it's worthwhile to first let this land in this repo though to make sure all the wasm tests pass.

alexcrichton

comment created time in 2 days

push eventalexcrichton/wasm-tools

Alex Crichton

commit sha c06900021f08ff223fbad39c3c306eb36ddacc11

Update upstream spec test suite Allows us to run more simd tests!

view details

push time in 2 days

delete branch alexcrichton/testsuite

delete branch : update-simd

delete time in 2 days

PR opened bytecodealliance/wasm-tools

Run even more tests!

Wabt is quite speedy to update, so let's go ahead and check these in while I'm thinking about it so we can run these tests.

+1 -12

0 comment

2 changed files

pr created time in 2 days

create barnchalexcrichton/wasm-tools

branch : update-wabt-again

created branch time in 2 days

PR opened WebAssembly/testsuite

Update repos

simd: https://github.com/WebAssembly/simd/commit/710f8708

This change was automatically generated by update-testsuite.sh

+7 -7

0 comment

1 changed file

pr created time in 2 days

create barnchalexcrichton/testsuite

branch : update-simd

created branch time in 2 days

create barnchalexcrichton/rust

branch : update-compiler-builtins

created branch time in 2 days

issue commentrust-lang/rust

Wasm with lto and use of f32::sin() gets an unknown reference to `env` module

For some more background on this issue, this is a standalone reproduction:

#[no_mangle]
pub extern "C" fn wasm_sin(key: i32) -> i32 {
    (key as f32).sin() as i32
}

where the issue can be seen with:

$ rustc +stable foo.rs --crate-type cdylib --target wasm32-unknown-unknown -O
$ wasm2wat foo.wasm | rg import
$ rustc +stable foo.rs --crate-type cdylib --target wasm32-unknown-unknown -O -C lto
$ wasm2wat foo.wasm | rg import
$ rustc +beta foo.rs --crate-type cdylib --target wasm32-unknown-unknown -O
$ wasm2wat foo.wasm | rg import
$ rustc +beta foo.rs --crate-type cdylib --target wasm32-unknown-unknown -O -C lto
$ wasm2wat foo.wasm | rg import
  (import "env" "_ZN4core9panicking18panic_bounds_check17hadfb911a22557eccE" (func $_ZN4core9panicking18panic_bounds_check17hadfb911a22557eccE (type 0)))

Here the bug is that, in beta (and onwards) with LTO turned on there's an unresolved symbol.

Bisection pointed out https://github.com/rust-lang/rust/pull/70846 as the cause of the bug here. The problem appears to be that previously compiler-builtins was optimized enough such that llvm removed all panics and bounds checks, but with a codegen unit reshuffling of compiler-builtins this did not happen and a bounds check was still in there.

It's intended that compiler-builtins cannot panic, and we even have a test that this is the case. Unfortunately though WebAssembly wasn't tested on CI which notably activates the libm intrinsics, which is where the panic was coming from here (the sin function). I expanded testing to include wasm in https://github.com/rust-lang/compiler-builtins/pull/360, and the first few commits failed showing lots of references to panics.

I fixed the implementation in libm at https://github.com/rust-lang/libm/pull/244. Most of those changes were so in debug mode there weren't panic checks. In release mode there was only one panic check which needed fixing, but fixing them all was needed for compiler-builtins's CI.

I've submitted a fix for this in https://github.com/rust-lang/rust/pull/72759 by updating compiler-builtins.

rodrigorc

comment created time in 2 days

PR opened rust-lang/rust

Update compiler-builtins

Pulls in a fix for #72758, more details on the linked issue.

Crate changes included here

+3 -3

0 comment

2 changed files

pr created time in 2 days

created tagrust-lang/compiler-builtins

tag0.1.31

Porting `compiler-rt` intrinsics to Rust

created time in 2 days

push eventrust-lang/compiler-builtins

Alex Crichton

commit sha d837cce3d36ac99d899b5743aa7371bb887a39a4

Add back in unsafe for bootstrapping And add an `#[allow]` for now to appease stage0

view details

Alex Crichton

commit sha 5488a098002d848d37d8775889f162812249876b

Bump to 0.1.31

view details

push time in 2 days

push eventrustwasm/wasm-bindgen

Deploy from CI

commit sha ee2c42dc97e522d3fafbce5ce9697cd6f5deae4f

Deploy cc36bdc00d3a41c67ee0a2c0af04a7c4323637a5 to gh-pages

view details

push time in 2 days

push eventrust-lang/compiler-builtins

Alex Crichton

commit sha d3efbd29152be38df0231156097d5dc122139183

Bump to 0.1.30

view details

push time in 2 days

created tagrust-lang/compiler-builtins

tag0.1.30

Porting `compiler-rt` intrinsics to Rust

created time in 2 days

push eventrustwasm/wasm-bindgen

Deploy from CI

commit sha 44d4bde42cfb9a47a9e1f1605444a4f0a78fb7c3

Deploy b5e377da784f209911689a53ea906f08b0dd2302 to gh-pages

view details

push time in 2 days

delete branch alexcrichton/wasm-bindgen

delete branch : consume-getter

delete time in 2 days

push eventrustwasm/wasm-bindgen

Alex Crichton

commit sha cc36bdc00d3a41c67ee0a2c0af04a7c4323637a5

Fix codegen of consuming setters/getters (#2172) Make sure they reset their internal pointer to null after we call Rust since it invalidates the Rust pointer after being called! Closes #2168

view details

push time in 2 days

PR merged rustwasm/wasm-bindgen

Fix codegen of consuming setters/getters

Make sure they reset their internal pointer to null after we call Rust since it invalidates the Rust pointer after being called!

Closes #2168

+60 -10

0 comment

7 changed files

alexcrichton

pr closed time in 2 days

issue closedrustwasm/wasm-bindgen

Getter/setter attribute on functions that take self causes memory access out of bounds.

Steps to Reproduce

It should be easy to reproduce in the simplest context. First create an normal empty wasm-pack project using cargo generate or write it by hand, then write the following code in lib.rs:

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
#[derive(Copy, Clone)]
pub struct Answer(u32);

#[wasm_bindgen]
impl Answer {
  pub fn new() -> Answer { Answer(41) }
  #[wasm_bindgen(getter)]
  pub fn the_answer(self) -> u32 { self.0 + 1 }
}

Then (after some configuration in package.json or something else) in your frontend index.js, write the following code:

import { Answer } from '<the project name>';

let answer = Answer.new();
console.log(answer.the_answer);
console.log(answer.the_answer);

Start it and test in the browser.

Expected Behavior

Like when without #[wasm_bindgen(getter)], reporting null pointer exception:

Error importing `index.js`: Error: null pointer passed to rust

Actual Behavior

You may actually get:

Error importing `index.js`: RuntimeError: memory access out of bounds

According to the experience of other static languages, such memory error cannot always be caught and reported. In another project of mine, I have succeed in fetching trash data using such method, but that context is quite complex and hard to reproduce.

Additional Context

Actually I have fully understood the reason of the bug, the related source code is located at https://github.com/rustwasm/wasm-bindgen/blob/87663c6d2a442d98b3d8ea6242f20c5c21fc0174/crates/cli-support/src/js/mod.rs#L2193 .

Here if the function is marked as a getter/setter, the generated JS won't assign 0 to this.ptr after the call, while the WASM side will free the memory, so here comes the memory access out of bounds

closed time in 2 days

MashPlant

push eventalexcrichton/curl-rust

Alex Crichton

commit sha 4f5c7284b8e6858d3eca6b777d46385838987189

Pin nightly to fix CI (#338) Waiting for upstream fixes, so let's unbreak our own CI to avoid blocking on that. Closes #337

view details

push time in 2 days

delete branch alexcrichton/curl-rust

delete branch : pin

delete time in 2 days

PR merged alexcrichton/curl-rust

Pin nightly to fix CI

Waiting for upstream fixes, so let's unbreak our own CI to avoid blocking on that.

Closes #337

+2 -1

0 comment

1 changed file

alexcrichton

pr closed time in 2 days

issue closedalexcrichton/curl-rust

systest fails to compile on latest nightly

Running cargo +nightly run -p systest fails to compile:

error: legacy asm! syntax is no longer supported
   --> /Users/scoakley/.cargo/registry/src/github.com-1ecc6299db9ec823/extprim-1.7.0/src/u128.rs:884:9
    |
884 |           asm!("
    |           ^---
    |           |
    |  _________help: replace with: `llvm_asm!`
    | |
885 | |             movq $2, %rax
886 | |             mulq $3
887 | |             movq %rax, $0
...   |
891 | |         : "r"(left), "r"(right)
892 | |         : "rax", "rdx");
    | |________________________^

error: aborting due to previous error

error: could not compile `extprim`.

CI is currently broken on nightly because of this. It looks like extprim, a dependency of ctest, has merged a fix for this but it hasn't been published to crates.io yet: https://github.com/kennytm/extprim/issues/19

closed time in 2 days

sagebind

pull request commentbytecodealliance/wasmtime

impl From<anyhow::Error> for Trap

This all looks great to me, thanks again for this!

leoyvens

comment created time in 2 days

push eventbytecodealliance/wasmtime

Leonardo Yvens

commit sha 0b3b9c298ef8709ef97686fd7ebeeb1f4802b6b7

impl From<anyhow::Error> for Trap (#1753) * From<anyhow::Error> for Trap * Add TrapReason::Error * wasmtime: Improve Error to Trap conversion * Remove Trap::message

view details

push time in 2 days

PR merged bytecodealliance/wasmtime

impl From<anyhow::Error> for Trap wasmtime:api wasmtime:c-api

This is useful for using ? in functions exported from the host which return Result<_, Trap>.

+74 -65

7 comments

11 changed files

leoyvens

pr closed time in 2 days

PR opened alexcrichton/curl-rust

Pin nightly to fix CI

Waiting for upstream fixes, so let's unbreak our own CI to avoid blocking on that.

Closes #337

+2 -1

0 comment

1 changed file

pr created time in 2 days

create barnchalexcrichton/curl-rust

branch : pin

created branch time in 2 days

pull request commentrustwasm/wasm-bindgen

enhance wasm-bindgen installation doc

Thanks for this! Definitely good clarifications to have :)

MaciekTalaska

comment created time in 2 days

push eventrustwasm/wasm-bindgen

Maciek Talaśka

commit sha b5e377da784f209911689a53ea906f08b0dd2302

enhance wasm-bindgen installation doc (#2171)

view details

push time in 2 days

PR merged rustwasm/wasm-bindgen

enhance wasm-bindgen installation doc

I am new to WASM and I had some problems understanding how wasm-pack uses wasm-bindgen. Documentation (in my opinion) is a bit lacking, that's why I decided to enhance it and describe in more detail what I have learned thanks to the discussion (https://github.com/rustwasm/wasm-bindgen/issues/2167) with one of experienced WASM developers.

I hope this change will make newcomers' to WASM life a little bit easier (especially at the very beginning).

+19 -5

0 comment

1 changed file

MaciekTalaska

pr closed time in 2 days

pull request commentrust-lang/cargo

Add environment variables to identify the binary and crate name

Nah yeah that's a good point, I think just clarifying that the name doesn't include the extension should do the trick!

reggaemuffin

comment created time in 2 days

pull request commentrust-lang/cargo

Add support for rustdoc root URL mappings.

Ok sounds reasonable to me! Do you think this is all worth writing up in a tracking issue on the Cargo side? Or perhaps directly into the unstable documentation?

In any case r=me with a tracking issue filled in

ehuss

comment created time in 2 days

pull request commentrust-lang/backtrace-rs

sys: Split dep-of-std and symbol prefix features

Er yes my point though is that users of backtrace on crates.io typically have std so there's no need for the symbol switcharoo. It's pretty unlikely now that libbacktrace is used both through libstd and through crates.io.

Are you seeing link errors locally from multiply defined symbols?

roblabla

comment created time in 2 days

issue closedrustwasm/wasm-bindgen

Incorrect package.json generated

Describe the Bug

I believe the package.json generated is missing a file in files when the target is browser.

Expected Behavior

I expect to have a filecoin_signer_wasm_bg.js

 "files": [
    "filecoin_signer_wasm_bg.wasm",
    "filecoin_signer_wasm.js",
    "filecoin_signer_wasm_bg.js"
  ],

Actual Behavior

What I actually have

  "files": [
    "filecoin_signer_wasm_bg.wasm",
    "filecoin_signer_wasm.js"
  ],

Additional Context

Then when publising to npm I am missing this file.

closed time in 2 days

rllola

issue commentrustwasm/wasm-bindgen

Incorrect package.json generated

Thanks for the report! I don't think this is in the package.json generated by wasm-bindgen, but instead I believe this is an issue with wasm-pack. Mind filing an issue over there for this?

rllola

comment created time in 2 days

issue commentbytecodealliance/wasmtime

Incorrect wasmtime::MemoryCreator implementation or documentation

The intention here is that the values are all in bytes, so if it's in wasm pages that's definitely a bug! Renaming the parameter names sounds great too!

Would you be up for a PR to update this?

lostman

comment created time in 2 days

Pull request review commentrust-lang/cargo

Default values for `readme` if not specified

 pub fn basic_lib_manifest(name: &str) -> String {     ) } +pub fn basic_bin_manifest_with_readme(name: &str, readme_filename: &str) -> String {

Eh I just find these constructors weird and super old, most tests aren't written with them. Mind moving this constructor to the readme tests? Seems fine to have a shorthand at least for a few tests!

tverghis

comment created time in 2 days

Pull request review commentrust-lang/cargo

Default values for `readme` if not specified

 impl TomlManifest {     } } +/// Returns the name of the README file for a `TomlProject`.+fn readme_for_project(package_root: &Path, project: &TomlProject) -> Option<String> {+    match &project.readme {+        None => default_readme_from_package_root(package_root),+        Some(value) => match value {+            StringOrBool::Bool(false) => None,+            StringOrBool::Bool(true) => default_readme_from_package_root(package_root),

This is an interesting case because readme = true could actually still infer no actual README if the files are missing. Perhaps for now we could just make readme = true an error? Otherwise we probably want to assert that the default readme is indeed present, and None isn't returned in this case.

tverghis

comment created time in 2 days

create barnchalexcrichton/wasm-bindgen

branch : consume-getter

created branch time in 2 days

issue commentrustwasm/wasm-bindgen

Getter/setter attribute on functions that take self causes memory access out of bounds.

Thanks for the report! I've filed a fix for this at https://github.com/rustwasm/wasm-bindgen/pull/2172

MashPlant

comment created time in 2 days

PR opened rustwasm/wasm-bindgen

Fix codegen of consuming setters/getters

Make sure they reset their internal pointer to null after we call Rust since it invalidates the Rust pointer after being called!

Closes #2168

+60 -10

0 comment

7 changed files

pr created time in 2 days

pull request commentbytecodealliance/wasmtime-py

Add type annotations almost everywhere

I'm not a huge fan of all the type checking here either, but it's required for the semantics I was shooting for. If the type checking is removed then it's very easy to segfault this extension because the wrong type was passed around by accident. I basically want to guarantee that any buggy usage results in exceptions, not segfaults, so type checking was required.

I'm pretty hesitant to merge this without any CI checking the annotations. Once there's CI I think it's fine, but this should be checked somehow in an automated way.

tobych

comment created time in 2 days

Pull request review commentbytecodealliance/wasmtime-py

Add type annotations almost everywhere

 from ._ffi import * from wasmtime import ValType+import typing   class Val:     @classmethod-    def i32(cls, val):+    def i32(cls, val: int) -> "Val":

That makes sense I'm just surprised that such a common pattern doesn't have better support...

tobych

comment created time in 2 days

Pull request review commentbytecodealliance/wasmtime-py

Add type annotations almost everywhere

 def type(self):     def __del__(self):         if self.__owner__ is None:             dll.wasm_exporttype_delete(self.__ptr__)+++ExternalType = typing.Union[FuncType, TableType, MemoryType, GlobalType]

Er well in any case I think the naming here and the actual instances themselves should be the same. For example External and ExternalType

tobych

comment created time in 2 days

Pull request review commentbytecodealliance/wasmtime-py

Add type annotations almost everywhere

 from ._ffi import *+ from ctypes import *+import typing++if typing.TYPE_CHECKING:+    from ._exportable import Exportable

That makes sense, but this is all code controlled in this package, so I think we can ensure that we keep a handle on our own imports?

tobych

comment created time in 2 days

pull request commentrust-lang/cargo

Fix tests with enoent error message on non-english systems.

@bors: r+

ehuss

comment created time in 2 days

push eventrust-lang/compiler-builtins

Alex Crichton

commit sha 4bf8cad593c06c7996cf4e25834bef159d59f48b

Expand wasm32 testing on CI (#360) * Expand wasm32 testing on CI Run the full `run.sh` test script to get full assertions, including that nothing in the wasm compiler-builtins is panicking. Unfortunately it's currently panicking, so this is good to weed out! * Update libm

view details

push time in 2 days

delete branch alexcrichton/compiler-builtins

delete branch : wasm-testing

delete time in 2 days

PR merged rust-lang/compiler-builtins

Expand wasm32 testing on CI

Run the full run.sh test script to get full assertions, including that nothing in the wasm compiler-builtins is panicking. Unfortunately it's currently panicking, so this is good to weed out!

+19 -38

1 comment

6 changed files

alexcrichton

pr closed time in 2 days

Pull request review commentbytecodealliance/wasmtime

Initial, partial support for `externref`

  use std::any::Any; use std::cell::{self, RefCell};+use std::convert::TryFrom; use std::fmt;+use std::marker::PhantomData; use std::rc::{Rc, Weak};--trait InternalRefBase: Any {-    fn as_any(&self) -> &dyn Any;-    fn host_info(&self) -> Option<cell::RefMut<Box<dyn Any>>>;-    fn set_host_info(&self, info: Option<Box<dyn Any>>);-    fn ptr_eq(&self, other: &dyn InternalRefBase) -> bool;-}--#[derive(Clone)]-pub struct InternalRef(Rc<dyn InternalRefBase>);--impl InternalRef {-    pub fn is_ref<T: 'static>(&self) -> bool {-        let r = self.0.as_any();-        Any::is::<HostRef<T>>(r)-    }-    pub fn get_ref<T: 'static>(&self) -> HostRef<T> {-        let r = self.0.as_any();-        r.downcast_ref::<HostRef<T>>()-            .expect("reference is not T type")-            .clone()-    }-}--struct AnyAndHostInfo {-    any: Box<dyn Any>,-    host_info: Option<Box<dyn Any>>,-}--#[derive(Clone)]-pub struct OtherRef(Rc<RefCell<AnyAndHostInfo>>);+use wasmtime_runtime::VMExternRef;  /// Represents an opaque reference to any data within WebAssembly. #[derive(Clone)]-pub enum ExternRef {-    /// A reference to no data.-    Null,-    /// A reference to data stored internally in `wasmtime`.-    Ref(InternalRef),-    /// A reference to data located outside of `wasmtime`.-    Other(OtherRef),+pub struct ExternRef {+    pub(crate) inner: VMExternRef,+    pub(crate) store: Weak<crate::runtime::StoreInner>, }  impl ExternRef {-    /// Creates a new instance of `ExternRef` from `Box<dyn Any>`.-    pub fn new(data: Box<dyn Any>) -> Self {-        let info = AnyAndHostInfo {-            any: data,-            host_info: None,-        };-        ExternRef::Other(OtherRef(Rc::new(RefCell::new(info))))+    /// Creates a new instance of `ExternRef` wrapping the given value.+    pub fn new<T>(store: &crate::Store, value: T) -> ExternRef+    where+        T: 'static + Any,+    {+        let inner = VMExternRef::new(value);+        let store = store.weak();+        ExternRef { inner, store }     }      /// Creates a `Null` reference.-    pub fn null() -> Self {-        ExternRef::Null+    pub fn null() -> Option<Self> {+        None     } -    /// Returns the data stored in the reference if available.-    /// # Panics-    /// Panics if the variant isn't `ExternRef::Other`.-    pub fn data(&self) -> cell::Ref<Box<dyn Any>> {-        match self {-            ExternRef::Other(OtherRef(r)) => cell::Ref::map(r.borrow(), |r| &r.any),-            _ => panic!("expected ExternRef::Other"),-        }+    /// Get this reference's store.+    ///+    /// Returns `None` if this reference outlived its store.+    pub fn store(&self) -> Option<crate::runtime::Store> {+        crate::runtime::Store::upgrade(&self.store)     } -    /// Returns true if the two `ExternRef<T>`'s point to the same value (not just-    /// values that compare as equal).+    /// Get the underlying data for this `ExternRef`.+    pub fn data(&self) -> &dyn Any {+        &*self.inner+    }++    /// just point to two different values that compare as equal).     pub fn ptr_eq(&self, other: &ExternRef) -> bool {-        match (self, other) {-            (ExternRef::Null, ExternRef::Null) => true,-            (ExternRef::Ref(InternalRef(ref a)), ExternRef::Ref(InternalRef(ref b))) => {-                a.ptr_eq(b.as_ref())-            }-            (ExternRef::Other(OtherRef(ref a)), ExternRef::Other(OtherRef(ref b))) => {-                Rc::ptr_eq(a, b)-            }-            _ => false,-        }+        self.inner == other.inner     } -    /// Returns a mutable reference to the host information if available.-    /// # Panics-    /// Panics if `ExternRef` is already borrowed or `ExternRef` is `Null`.-    pub fn host_info(&self) -> Option<cell::RefMut<Box<dyn Any>>> {-        match self {-            ExternRef::Null => panic!("null"),-            ExternRef::Ref(r) => r.0.host_info(),-            ExternRef::Other(r) => {-                let info = cell::RefMut::map(r.0.borrow_mut(), |b| &mut b.host_info);-                if info.is_none() {-                    return None;-                }-                Some(cell::RefMut::map(info, |info| info.as_mut().unwrap()))-            }-        }+    /// Returns the host information for this `externref`, if previously created+    /// with `set_host_info`.+    pub fn host_info(&self) -> Option<Rc<RefCell<dyn Any>>> {+        let store = crate::Store::upgrade(&self.store)?;+        store.host_info(self)     } -    /// Sets the host information for an `ExternRef`.-    /// # Panics-    /// Panics if `ExternRef` is already borrowed or `ExternRef` is `Null`.-    pub fn set_host_info(&self, info: Option<Box<dyn Any>>) {-        match self {-            ExternRef::Null => panic!("null"),-            ExternRef::Ref(r) => r.0.set_host_info(info),-            ExternRef::Other(r) => {-                r.0.borrow_mut().host_info = info;-            }-        }+    /// Set the host information for this `externref`, returning the old host+    /// information if it was previously set.+    pub fn set_host_info<T>(&self, info: T) -> Option<Rc<RefCell<dyn Any>>>+    where+        T: 'static + Any,+    {+        let store = crate::Store::upgrade(&self.store)?;+        store.set_host_info(self, Some(Rc::new(RefCell::new(info))))     }-} -impl fmt::Debug for ExternRef {-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {-        match self {-            ExternRef::Null => write!(f, "null"),-            ExternRef::Ref(_) => write!(f, "externref"),-            ExternRef::Other(_) => write!(f, "other ref"),-        }+    /// Remove the host information for this `externref`, returning the old host+    /// information if it was previously set.+    pub fn remove_host_info(&self) -> Option<Rc<RefCell<dyn Any>>> {+        let store = crate::Store::upgrade(&self.store)?;+        store.set_host_info(self, None)     } } -struct ContentBox<T> {-    content: T,-    host_info: Option<Box<dyn Any>>,-    externref_data: Weak<dyn InternalRefBase>,+impl fmt::Debug for ExternRef {+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {+        let ExternRef { inner, store: _ } = self;+        f.debug_struct("ExternRef")+            .field("inner", &inner)+            .field("store", &"..")+            .finish()+    } }  /// Represents a piece of data located in the host environment.-pub struct HostRef<T>(Rc<RefCell<ContentBox<T>>>);+#[derive(Debug)]+pub struct HostRef<T>

Can this structure move to the C API?

fitzgen

comment created time in 2 days

Pull request review commentbytecodealliance/wasmtime

Initial, partial support for `externref`

  use std::any::Any; use std::cell::{self, RefCell};+use std::convert::TryFrom; use std::fmt;+use std::marker::PhantomData; use std::rc::{Rc, Weak};--trait InternalRefBase: Any {-    fn as_any(&self) -> &dyn Any;-    fn host_info(&self) -> Option<cell::RefMut<Box<dyn Any>>>;-    fn set_host_info(&self, info: Option<Box<dyn Any>>);-    fn ptr_eq(&self, other: &dyn InternalRefBase) -> bool;-}--#[derive(Clone)]-pub struct InternalRef(Rc<dyn InternalRefBase>);--impl InternalRef {-    pub fn is_ref<T: 'static>(&self) -> bool {-        let r = self.0.as_any();-        Any::is::<HostRef<T>>(r)-    }-    pub fn get_ref<T: 'static>(&self) -> HostRef<T> {-        let r = self.0.as_any();-        r.downcast_ref::<HostRef<T>>()-            .expect("reference is not T type")-            .clone()-    }-}--struct AnyAndHostInfo {-    any: Box<dyn Any>,-    host_info: Option<Box<dyn Any>>,-}--#[derive(Clone)]-pub struct OtherRef(Rc<RefCell<AnyAndHostInfo>>);+use wasmtime_runtime::VMExternRef;  /// Represents an opaque reference to any data within WebAssembly. #[derive(Clone)]-pub enum ExternRef {-    /// A reference to no data.-    Null,-    /// A reference to data stored internally in `wasmtime`.-    Ref(InternalRef),-    /// A reference to data located outside of `wasmtime`.-    Other(OtherRef),+pub struct ExternRef {+    pub(crate) inner: VMExternRef,+    pub(crate) store: Weak<crate::runtime::StoreInner>, }  impl ExternRef {-    /// Creates a new instance of `ExternRef` from `Box<dyn Any>`.-    pub fn new(data: Box<dyn Any>) -> Self {-        let info = AnyAndHostInfo {-            any: data,-            host_info: None,-        };-        ExternRef::Other(OtherRef(Rc::new(RefCell::new(info))))+    /// Creates a new instance of `ExternRef` wrapping the given value.+    pub fn new<T>(store: &crate::Store, value: T) -> ExternRef+    where+        T: 'static + Any,+    {+        let inner = VMExternRef::new(value);+        let store = store.weak();+        ExternRef { inner, store }     }      /// Creates a `Null` reference.-    pub fn null() -> Self {-        ExternRef::Null+    pub fn null() -> Option<Self> {

This can probably just be deleted now, it doesn't seem to serve too much purpose any more

fitzgen

comment created time in 2 days

Pull request review commentbytecodealliance/wasmtime

Initial, partial support for `externref`

  use std::any::Any; use std::cell::{self, RefCell};+use std::convert::TryFrom; use std::fmt;+use std::marker::PhantomData; use std::rc::{Rc, Weak};--trait InternalRefBase: Any {-    fn as_any(&self) -> &dyn Any;-    fn host_info(&self) -> Option<cell::RefMut<Box<dyn Any>>>;-    fn set_host_info(&self, info: Option<Box<dyn Any>>);-    fn ptr_eq(&self, other: &dyn InternalRefBase) -> bool;-}--#[derive(Clone)]-pub struct InternalRef(Rc<dyn InternalRefBase>);--impl InternalRef {-    pub fn is_ref<T: 'static>(&self) -> bool {-        let r = self.0.as_any();-        Any::is::<HostRef<T>>(r)-    }-    pub fn get_ref<T: 'static>(&self) -> HostRef<T> {-        let r = self.0.as_any();-        r.downcast_ref::<HostRef<T>>()-            .expect("reference is not T type")-            .clone()-    }-}--struct AnyAndHostInfo {-    any: Box<dyn Any>,-    host_info: Option<Box<dyn Any>>,-}--#[derive(Clone)]-pub struct OtherRef(Rc<RefCell<AnyAndHostInfo>>);+use wasmtime_runtime::VMExternRef;  /// Represents an opaque reference to any data within WebAssembly. #[derive(Clone)]-pub enum ExternRef {-    /// A reference to no data.-    Null,-    /// A reference to data stored internally in `wasmtime`.-    Ref(InternalRef),-    /// A reference to data located outside of `wasmtime`.-    Other(OtherRef),+pub struct ExternRef {+    pub(crate) inner: VMExternRef,+    pub(crate) store: Weak<crate::runtime::StoreInner>,

The only reason this is here is for the host_info APIs, right? If so I would personally prefer that we just jettison that support from the C API for now. I think we should find a better way for implementing that, if necessary, instead of altering the design of the wasmtime crate itself.

fitzgen

comment created time in 2 days

Pull request review commentbytecodealliance/wasmtime

Initial, partial support for `externref`

 

Looks like this file became executable?

fitzgen

comment created time in 2 days

Pull request review commentbytecodealliance/wasmtime

Initial, partial support for `externref`

+//! # `VMExternRef`+//!+//! `VMExternRef` is a reference-counted box for any kind of data that is+//! external and opaque to running Wasm. Sometimes it might hold a Wasmtime+//! thing, other times it might hold something from a Wasmtime embedder and is+//! opaque even to us. It is morally equivalent to `Rc<dyn Any>` in Rust, but+//! additionally always fits in a pointer-sized word. `VMExternRef` is+//! non-nullable, but `Option<VMExternRef>` is a null pointer.+//!+//! The one part of `VMExternRef` that can't ever be opaque to us is the+//! reference count. Even when we don't know what's inside an `VMExternRef`, we+//! need to be able to manipulate its reference count as we add and remove+//! references to it. And we need to do this from compiled Wasm code, so it must+//! be `repr(C)`!+//!+//! ## Memory Layout+//!+//! `VMExternRef` itself is just a pointer to an `VMExternData`, which holds the+//! opaque, boxed value, its reference count, and its vtable pointer.+//!+//! The `VMExternData` struct is *preceded* by the dynamically-sized value boxed+//! up and referenced by one or more `VMExternRef`s:+//!+//! ```ignore+//!      ,-------------------------------------------------------.+//!      |                                                       |+//!      V                                                       |+//!     +----------------------------+-----------+-----------+   |+//!     | dynamically-sized value... | ref_count | value_ptr |---'+//!     +----------------------------+-----------+-----------++//!                                  | VMExternData          |+//!                                  +-----------------------++//!                                   ^+//! +-------------+                   |+//! | VMExternRef |-------------------++//! +-------------+                   |+//!                                   |+//! +-------------+                   |+//! | VMExternRef |-------------------++//! +-------------+                   |+//!                                   |+//!   ...                            ===+//!                                   |+//! +-------------+                   |+//! | VMExternRef |-------------------'+//! +-------------++//! ```+//!+//! The `value_ptr` member always points backwards to the start of the+//! dynamically-sized value (which is also the start of the heap allocation for+//! this value-and-`VMExternData` pair). Because it is a `dyn` pointer, it is+//! fat, and also points to the value's `Any` vtable.+//!+//! The boxed value and the `VMExternRef` footer are held a single heap+//! allocation. The layout described above is used to make satisfying the+//! value's alignment easy: we just need to ensure that the heap allocation used+//! to hold everything satisfies its alignment. It also ensures that we don't+//! need a ton of excess padding between the `VMExternData` and the value for+//! values with large alignment.+//!+//! ## Reference Counting Protocol and Wasm Functions+//!+//! Currently, `VMExternRef`s passed into compiled Wasm functions have move+//! semantics: the host code gives up ownership and does not decrement the+//! reference count. Similarly, `VMExternRef`s returned from compiled Wasm+//! functions also have move semantics: host code takes ownership and the+//! reference count is not incremented.+//!+//! This works well when a reference is passed into Wasm and then passed back+//! out again. However, if a reference is passed into Wasm, but not passed back+//! out, then the reference is leaked. This is only a temporary state, and+//! follow up work will leverage stack maps to fix this issue. Follow+//! https://github.com/bytecodealliance/wasmtime/issues/929 to keep an eye on+//! this.++use std::alloc::Layout;+use std::any::Any;+use std::cell::UnsafeCell;+use std::cmp::Ordering;+use std::hash::{Hash, Hasher};+use std::mem;+use std::ops::Deref;+use std::ptr::{self, NonNull};++/// An external reference to some opaque data.+///+/// `VMExternRef`s dereference to their underlying opaque data as `dyn Any`.+///+/// Unlike the `externref` in the Wasm spec, `VMExternRef`s are non-nullable,+/// and always point to a valid value. You may use `Option<VMExternRef>` to+/// represent nullable references, and `Option<VMExternRef>` is guaranteed to+/// have the same size and alignment as a raw pointer, with `None` represented+/// with the null pointer.+///+/// `VMExternRef`s are reference counted, so cloning is a cheap, shallow+/// operation. It also means they are inherently shared, so you may not get a+/// mutable, exclusive reference to their inner contents, only a shared,+/// immutable reference. You may use interior mutability with `RefCell` or+/// `Mutex` to work around this restriction, if necessary.+///+/// `VMExternRef`s have pointer-equality semantics, not structural-equality+/// semantics. Given two `VMExternRef`s `a` and `b`, `a == b` only if `a` and+/// `b` point to the same allocation. `a` and `b` are considered not equal, even+/// if `a` and `b` are two different identical copies of the same data, if they+/// are in two different allocations. The hashing and ordering implementations+/// also only operate on the pointer.+///+/// # Example+///+/// ```+/// # fn foo() -> Result<(), Box<dyn std::error::Error>> {+/// use std::cell::RefCell;+/// use wasmtime_runtime::VMExternRef;+///+/// // Open a file. Wasm doesn't know about files, but we can let Wasm instances+/// // work with files via opaque `externref` handles.+/// let file = std::fs::File::create("some/file/path")?;+///+/// // Wrap the file up as an `VMExternRef` that can be passed to Wasm.+/// let extern_ref_to_file = VMExternRef::new(RefCell::new(file));+///+/// // `VMExternRef`s dereference to `dyn Any`, so you can use `Any` methods to+/// // perform runtime type checks and downcasts.+///+/// assert!(extern_ref_to_file.is::<RefCell<std::fs::File>>());+/// assert!(!extern_ref_to_file.is::<String>());+///+/// if let Some(file) = extern_ref_to_file.downcast_ref::<RefCell<std::fs::File>>() {+///     use std::io::Write;+///     let mut file = file.borrow_mut();+///     writeln!(&mut file, "Hello, `VMExternRef`!")?;+/// }+/// # Ok(())+/// # }+/// ```+#[derive(Debug)]+#[repr(transparent)]+pub struct VMExternRef(NonNull<VMExternData>);++#[repr(C)]+struct VMExternData {+    // Implicit, dynamically-sized member that always preceded an+    // `VMExternData`.+    //+    // value: [u8],+    //+    /// The reference count for this `VMExternData` and value. When it reaches+    /// zero, we can safely destroy the value and free this heap+    /// allocation. This is an `UnsafeCell`, rather than plain `Cell`, because+    /// it can be modified by compiled Wasm code.+    ///+    /// Note: this field's offset must be kept in sync with+    /// `wasmtime_environ::VMOffsets::vm_extern_data_ref_count()` which is+    /// currently always zero.+    ref_count: UnsafeCell<usize>,++    /// Always points to the implicit, dynamically-sized `value` member that+    /// precedes this `VMExternData`.+    value_ptr: NonNull<dyn Any>,+}++impl Clone for VMExternRef {+    #[inline]+    fn clone(&self) -> VMExternRef {+        self.extern_data().increment_ref_count();+        VMExternRef(self.0)+    }+}++impl Drop for VMExternRef {+    #[inline]+    fn drop(&mut self) {+        let data = self.extern_data();+        data.decrement_ref_count();+        if data.get_ref_count() == 0 {+            // Drop our live reference to `data` before we drop it itself.+            drop(data);

Since data isn't an owned value this doesn't actually do anything, right?

fitzgen

comment created time in 2 days

Pull request review commentbytecodealliance/wasmtime

Initial, partial support for `externref`

  use std::any::Any; use std::cell::{self, RefCell};+use std::convert::TryFrom; use std::fmt;+use std::marker::PhantomData; use std::rc::{Rc, Weak};--trait InternalRefBase: Any {-    fn as_any(&self) -> &dyn Any;-    fn host_info(&self) -> Option<cell::RefMut<Box<dyn Any>>>;-    fn set_host_info(&self, info: Option<Box<dyn Any>>);-    fn ptr_eq(&self, other: &dyn InternalRefBase) -> bool;-}--#[derive(Clone)]-pub struct InternalRef(Rc<dyn InternalRefBase>);--impl InternalRef {-    pub fn is_ref<T: 'static>(&self) -> bool {-        let r = self.0.as_any();-        Any::is::<HostRef<T>>(r)-    }-    pub fn get_ref<T: 'static>(&self) -> HostRef<T> {-        let r = self.0.as_any();-        r.downcast_ref::<HostRef<T>>()-            .expect("reference is not T type")-            .clone()-    }-}--struct AnyAndHostInfo {-    any: Box<dyn Any>,-    host_info: Option<Box<dyn Any>>,-}--#[derive(Clone)]-pub struct OtherRef(Rc<RefCell<AnyAndHostInfo>>);+use wasmtime_runtime::VMExternRef;  /// Represents an opaque reference to any data within WebAssembly. #[derive(Clone)]-pub enum ExternRef {-    /// A reference to no data.-    Null,-    /// A reference to data stored internally in `wasmtime`.-    Ref(InternalRef),-    /// A reference to data located outside of `wasmtime`.-    Other(OtherRef),+pub struct ExternRef {+    pub(crate) inner: VMExternRef,+    pub(crate) store: Weak<crate::runtime::StoreInner>, }  impl ExternRef {-    /// Creates a new instance of `ExternRef` from `Box<dyn Any>`.-    pub fn new(data: Box<dyn Any>) -> Self {-        let info = AnyAndHostInfo {-            any: data,-            host_info: None,-        };-        ExternRef::Other(OtherRef(Rc::new(RefCell::new(info))))+    /// Creates a new instance of `ExternRef` wrapping the given value.+    pub fn new<T>(store: &crate::Store, value: T) -> ExternRef+    where+        T: 'static + Any,+    {+        let inner = VMExternRef::new(value);+        let store = store.weak();+        ExternRef { inner, store }     }      /// Creates a `Null` reference.-    pub fn null() -> Self {-        ExternRef::Null+    pub fn null() -> Option<Self> {+        None     } -    /// Returns the data stored in the reference if available.-    /// # Panics-    /// Panics if the variant isn't `ExternRef::Other`.-    pub fn data(&self) -> cell::Ref<Box<dyn Any>> {-        match self {-            ExternRef::Other(OtherRef(r)) => cell::Ref::map(r.borrow(), |r| &r.any),-            _ => panic!("expected ExternRef::Other"),-        }+    /// Get this reference's store.+    ///+    /// Returns `None` if this reference outlived its store.+    pub fn store(&self) -> Option<crate::runtime::Store> {+        crate::runtime::Store::upgrade(&self.store)     } -    /// Returns true if the two `ExternRef<T>`'s point to the same value (not just-    /// values that compare as equal).+    /// Get the underlying data for this `ExternRef`.+    pub fn data(&self) -> &dyn Any {+        &*self.inner+    }++    /// just point to two different values that compare as equal).     pub fn ptr_eq(&self, other: &ExternRef) -> bool {-        match (self, other) {-            (ExternRef::Null, ExternRef::Null) => true,-            (ExternRef::Ref(InternalRef(ref a)), ExternRef::Ref(InternalRef(ref b))) => {-                a.ptr_eq(b.as_ref())-            }-            (ExternRef::Other(OtherRef(ref a)), ExternRef::Other(OtherRef(ref b))) => {-                Rc::ptr_eq(a, b)-            }-            _ => false,-        }+        self.inner == other.inner     } -    /// Returns a mutable reference to the host information if available.-    /// # Panics-    /// Panics if `ExternRef` is already borrowed or `ExternRef` is `Null`.-    pub fn host_info(&self) -> Option<cell::RefMut<Box<dyn Any>>> {-        match self {-            ExternRef::Null => panic!("null"),-            ExternRef::Ref(r) => r.0.host_info(),-            ExternRef::Other(r) => {-                let info = cell::RefMut::map(r.0.borrow_mut(), |b| &mut b.host_info);-                if info.is_none() {-                    return None;-                }-                Some(cell::RefMut::map(info, |info| info.as_mut().unwrap()))-            }-        }+    /// Returns the host information for this `externref`, if previously created+    /// with `set_host_info`.+    pub fn host_info(&self) -> Option<Rc<RefCell<dyn Any>>> {+        let store = crate::Store::upgrade(&self.store)?;+        store.host_info(self)     } -    /// Sets the host information for an `ExternRef`.-    /// # Panics-    /// Panics if `ExternRef` is already borrowed or `ExternRef` is `Null`.-    pub fn set_host_info(&self, info: Option<Box<dyn Any>>) {-        match self {-            ExternRef::Null => panic!("null"),-            ExternRef::Ref(r) => r.0.set_host_info(info),-            ExternRef::Other(r) => {-                r.0.borrow_mut().host_info = info;-            }-        }+    /// Set the host information for this `externref`, returning the old host+    /// information if it was previously set.+    pub fn set_host_info<T>(&self, info: T) -> Option<Rc<RefCell<dyn Any>>>+    where+        T: 'static + Any,+    {+        let store = crate::Store::upgrade(&self.store)?;+        store.set_host_info(self, Some(Rc::new(RefCell::new(info))))     }-} -impl fmt::Debug for ExternRef {-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {-        match self {-            ExternRef::Null => write!(f, "null"),-            ExternRef::Ref(_) => write!(f, "externref"),-            ExternRef::Other(_) => write!(f, "other ref"),-        }+    /// Remove the host information for this `externref`, returning the old host+    /// information if it was previously set.+    pub fn remove_host_info(&self) -> Option<Rc<RefCell<dyn Any>>> {+        let store = crate::Store::upgrade(&self.store)?;+        store.set_host_info(self, None)     } } -struct ContentBox<T> {-    content: T,-    host_info: Option<Box<dyn Any>>,-    externref_data: Weak<dyn InternalRefBase>,+impl fmt::Debug for ExternRef {+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {+        let ExternRef { inner, store: _ } = self;+        f.debug_struct("ExternRef")+            .field("inner", &inner)+            .field("store", &"..")

Could this pass in self.store() and defer to Debug for Store?

fitzgen

comment created time in 2 days

Pull request review commentbytecodealliance/wasmtime

Initial, partial support for `externref`

 impl Func {         (get15, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15)     } -    pub(crate) fn store(&self) -> &Store {+    // This should be `pub(crate)` but the C API happens to need it in one+    // place.+    #[doc(hidden)]+    pub fn store(&self) -> &Store {

This seems like a fine API to expose though, especially because Caller below exposes it too

fitzgen

comment created time in 2 days

Pull request review commentbytecodealliance/wasmtime

Initial, partial support for `externref`

  use std::any::Any; use std::cell::{self, RefCell};+use std::convert::TryFrom; use std::fmt;+use std::marker::PhantomData; use std::rc::{Rc, Weak};--trait InternalRefBase: Any {-    fn as_any(&self) -> &dyn Any;-    fn host_info(&self) -> Option<cell::RefMut<Box<dyn Any>>>;-    fn set_host_info(&self, info: Option<Box<dyn Any>>);-    fn ptr_eq(&self, other: &dyn InternalRefBase) -> bool;-}--#[derive(Clone)]-pub struct InternalRef(Rc<dyn InternalRefBase>);--impl InternalRef {-    pub fn is_ref<T: 'static>(&self) -> bool {-        let r = self.0.as_any();-        Any::is::<HostRef<T>>(r)-    }-    pub fn get_ref<T: 'static>(&self) -> HostRef<T> {-        let r = self.0.as_any();-        r.downcast_ref::<HostRef<T>>()-            .expect("reference is not T type")-            .clone()-    }-}--struct AnyAndHostInfo {-    any: Box<dyn Any>,-    host_info: Option<Box<dyn Any>>,-}--#[derive(Clone)]-pub struct OtherRef(Rc<RefCell<AnyAndHostInfo>>);+use wasmtime_runtime::VMExternRef;  /// Represents an opaque reference to any data within WebAssembly. #[derive(Clone)]-pub enum ExternRef {-    /// A reference to no data.-    Null,-    /// A reference to data stored internally in `wasmtime`.-    Ref(InternalRef),-    /// A reference to data located outside of `wasmtime`.-    Other(OtherRef),+pub struct ExternRef {+    pub(crate) inner: VMExternRef,+    pub(crate) store: Weak<crate::runtime::StoreInner>, }  impl ExternRef {-    /// Creates a new instance of `ExternRef` from `Box<dyn Any>`.-    pub fn new(data: Box<dyn Any>) -> Self {-        let info = AnyAndHostInfo {-            any: data,-            host_info: None,-        };-        ExternRef::Other(OtherRef(Rc::new(RefCell::new(info))))+    /// Creates a new instance of `ExternRef` wrapping the given value.+    pub fn new<T>(store: &crate::Store, value: T) -> ExternRef+    where+        T: 'static + Any,+    {+        let inner = VMExternRef::new(value);+        let store = store.weak();+        ExternRef { inner, store }     }      /// Creates a `Null` reference.-    pub fn null() -> Self {-        ExternRef::Null+    pub fn null() -> Option<Self> {+        None     } -    /// Returns the data stored in the reference if available.-    /// # Panics-    /// Panics if the variant isn't `ExternRef::Other`.-    pub fn data(&self) -> cell::Ref<Box<dyn Any>> {-        match self {-            ExternRef::Other(OtherRef(r)) => cell::Ref::map(r.borrow(), |r| &r.any),-            _ => panic!("expected ExternRef::Other"),-        }+    /// Get this reference's store.+    ///+    /// Returns `None` if this reference outlived its store.+    pub fn store(&self) -> Option<crate::runtime::Store> {+        crate::runtime::Store::upgrade(&self.store)     } -    /// Returns true if the two `ExternRef<T>`'s point to the same value (not just-    /// values that compare as equal).+    /// Get the underlying data for this `ExternRef`.+    pub fn data(&self) -> &dyn Any {+        &*self.inner+    }++    /// just point to two different values that compare as equal).     pub fn ptr_eq(&self, other: &ExternRef) -> bool {-        match (self, other) {-            (ExternRef::Null, ExternRef::Null) => true,-            (ExternRef::Ref(InternalRef(ref a)), ExternRef::Ref(InternalRef(ref b))) => {-                a.ptr_eq(b.as_ref())-            }-            (ExternRef::Other(OtherRef(ref a)), ExternRef::Other(OtherRef(ref b))) => {-                Rc::ptr_eq(a, b)-            }-            _ => false,-        }+        self.inner == other.inner     } -    /// Returns a mutable reference to the host information if available.-    /// # Panics-    /// Panics if `ExternRef` is already borrowed or `ExternRef` is `Null`.-    pub fn host_info(&self) -> Option<cell::RefMut<Box<dyn Any>>> {-        match self {-            ExternRef::Null => panic!("null"),-            ExternRef::Ref(r) => r.0.host_info(),-            ExternRef::Other(r) => {-                let info = cell::RefMut::map(r.0.borrow_mut(), |b| &mut b.host_info);-                if info.is_none() {-                    return None;-                }-                Some(cell::RefMut::map(info, |info| info.as_mut().unwrap()))-            }-        }+    /// Returns the host information for this `externref`, if previously created+    /// with `set_host_info`.+    pub fn host_info(&self) -> Option<Rc<RefCell<dyn Any>>> {

Ideally we'd move this out of the wasmtime crate entirely. Can all the host_info stuff move to wasm_store_t in the C API? I think the C API could store a Rc<wasm_store_t> on all the exported types, right?

fitzgen

comment created time in 2 days

Pull request review commentbytecodealliance/wasmtime

Initial, partial support for `externref`

+//! # `VMExternRef`+//!+//! `VMExternRef` is a reference-counted box for any kind of data that is+//! external and opaque to running Wasm. Sometimes it might hold a Wasmtime+//! thing, other times it might hold something from a Wasmtime embedder and is+//! opaque even to us. It is morally equivalent to `Rc<dyn Any>` in Rust, but+//! additionally always fits in a pointer-sized word. `VMExternRef` is+//! non-nullable, but `Option<VMExternRef>` is a null pointer.+//!+//! The one part of `VMExternRef` that can't ever be opaque to us is the+//! reference count. Even when we don't know what's inside an `VMExternRef`, we+//! need to be able to manipulate its reference count as we add and remove+//! references to it. And we need to do this from compiled Wasm code, so it must+//! be `repr(C)`!+//!+//! ## Memory Layout+//!+//! `VMExternRef` itself is just a pointer to an `VMExternData`, which holds the+//! opaque, boxed value, its reference count, and its vtable pointer.+//!+//! The `VMExternData` struct is *preceded* by the dynamically-sized value boxed+//! up and referenced by one or more `VMExternRef`s:+//!+//! ```ignore+//!      ,-------------------------------------------------------.+//!      |                                                       |+//!      V                                                       |+//!     +----------------------------+-----------+-----------+   |+//!     | dynamically-sized value... | ref_count | value_ptr |---'+//!     +----------------------------+-----------+-----------++//!                                  | VMExternData          |+//!                                  +-----------------------++//!                                   ^+//! +-------------+                   |+//! | VMExternRef |-------------------++//! +-------------+                   |+//!                                   |+//! +-------------+                   |+//! | VMExternRef |-------------------++//! +-------------+                   |+//!                                   |+//!   ...                            ===+//!                                   |+//! +-------------+                   |+//! | VMExternRef |-------------------'+//! +-------------++//! ```+//!+//! The `value_ptr` member always points backwards to the start of the+//! dynamically-sized value (which is also the start of the heap allocation for+//! this value-and-`VMExternData` pair). Because it is a `dyn` pointer, it is+//! fat, and also points to the value's `Any` vtable.+//!+//! The boxed value and the `VMExternRef` footer are held a single heap+//! allocation. The layout described above is used to make satisfying the+//! value's alignment easy: we just need to ensure that the heap allocation used+//! to hold everything satisfies its alignment. It also ensures that we don't+//! need a ton of excess padding between the `VMExternData` and the value for+//! values with large alignment.+//!+//! ## Reference Counting Protocol and Wasm Functions+//!+//! Currently, `VMExternRef`s passed into compiled Wasm functions have move+//! semantics: the host code gives up ownership and does not decrement the+//! reference count. Similarly, `VMExternRef`s returned from compiled Wasm+//! functions also have move semantics: host code takes ownership and the+//! reference count is not incremented.+//!+//! This works well when a reference is passed into Wasm and then passed back+//! out again. However, if a reference is passed into Wasm, but not passed back+//! out, then the reference is leaked. This is only a temporary state, and+//! follow up work will leverage stack maps to fix this issue. Follow+//! https://github.com/bytecodealliance/wasmtime/issues/929 to keep an eye on+//! this.++use std::alloc::Layout;+use std::any::Any;+use std::cell::UnsafeCell;+use std::cmp::Ordering;+use std::hash::{Hash, Hasher};+use std::mem;+use std::ops::Deref;+use std::ptr::{self, NonNull};++/// An external reference to some opaque data.+///+/// `VMExternRef`s dereference to their underlying opaque data as `dyn Any`.+///+/// Unlike the `externref` in the Wasm spec, `VMExternRef`s are non-nullable,+/// and always point to a valid value. You may use `Option<VMExternRef>` to+/// represent nullable references, and `Option<VMExternRef>` is guaranteed to+/// have the same size and alignment as a raw pointer, with `None` represented+/// with the null pointer.+///+/// `VMExternRef`s are reference counted, so cloning is a cheap, shallow+/// operation. It also means they are inherently shared, so you may not get a+/// mutable, exclusive reference to their inner contents, only a shared,+/// immutable reference. You may use interior mutability with `RefCell` or+/// `Mutex` to work around this restriction, if necessary.+///+/// `VMExternRef`s have pointer-equality semantics, not structural-equality+/// semantics. Given two `VMExternRef`s `a` and `b`, `a == b` only if `a` and+/// `b` point to the same allocation. `a` and `b` are considered not equal, even+/// if `a` and `b` are two different identical copies of the same data, if they+/// are in two different allocations. The hashing and ordering implementations+/// also only operate on the pointer.+///+/// # Example+///+/// ```+/// # fn foo() -> Result<(), Box<dyn std::error::Error>> {+/// use std::cell::RefCell;+/// use wasmtime_runtime::VMExternRef;+///+/// // Open a file. Wasm doesn't know about files, but we can let Wasm instances+/// // work with files via opaque `externref` handles.+/// let file = std::fs::File::create("some/file/path")?;+///+/// // Wrap the file up as an `VMExternRef` that can be passed to Wasm.+/// let extern_ref_to_file = VMExternRef::new(RefCell::new(file));+///+/// // `VMExternRef`s dereference to `dyn Any`, so you can use `Any` methods to+/// // perform runtime type checks and downcasts.+///+/// assert!(extern_ref_to_file.is::<RefCell<std::fs::File>>());+/// assert!(!extern_ref_to_file.is::<String>());+///+/// if let Some(file) = extern_ref_to_file.downcast_ref::<RefCell<std::fs::File>>() {+///     use std::io::Write;+///     let mut file = file.borrow_mut();+///     writeln!(&mut file, "Hello, `VMExternRef`!")?;+/// }+/// # Ok(())+/// # }+/// ```+#[derive(Debug)]+#[repr(transparent)]+pub struct VMExternRef(NonNull<VMExternData>);++#[repr(C)]+struct VMExternData {+    // Implicit, dynamically-sized member that always preceded an+    // `VMExternData`.+    //+    // value: [u8],+    //+    /// The reference count for this `VMExternData` and value. When it reaches+    /// zero, we can safely destroy the value and free this heap+    /// allocation. This is an `UnsafeCell`, rather than plain `Cell`, because+    /// it can be modified by compiled Wasm code.+    ///+    /// Note: this field's offset must be kept in sync with+    /// `wasmtime_environ::VMOffsets::vm_extern_data_ref_count()` which is+    /// currently always zero.+    ref_count: UnsafeCell<usize>,++    /// Always points to the implicit, dynamically-sized `value` member that+    /// precedes this `VMExternData`.+    value_ptr: NonNull<dyn Any>,+}++impl Clone for VMExternRef {+    #[inline]+    fn clone(&self) -> VMExternRef {+        self.extern_data().increment_ref_count();+        VMExternRef(self.0)+    }+}++impl Drop for VMExternRef {+    #[inline]+    fn drop(&mut self) {+        let data = self.extern_data();+        data.decrement_ref_count();+        if data.get_ref_count() == 0 {+            // Drop our live reference to `data` before we drop it itself.+            drop(data);+            unsafe {+                VMExternData::drop_and_dealloc(self.0);+            }+        }+    }+}++impl VMExternData {+    unsafe fn drop_and_dealloc(mut data: NonNull<VMExternData>) {+        // Note: we introduce a block scope so that we drop the live+        // reference to the data before we free the heap allocation it+        // resides within after this block.+        let (alloc_ptr, layout) = {+            let data = data.as_mut();+            debug_assert_eq!(data.get_ref_count(), 0);++            // Same thing, but for the dropping the reference to `value` before+            // we drop it itself.+            let layout = {+                let value = data.value_ptr.as_ref();++                let value_size = mem::size_of_val(value);+                let value_align = mem::align_of_val(value);++                let extern_data_size = mem::size_of::<VMExternData>();+                let extern_data_align = mem::align_of::<VMExternData>();++                let value_and_padding_size = round_up_to_align(value_size, extern_data_align)+                    .unwrap_or_else(|| unreachable!());++                let alloc_align = std::cmp::max(value_align, extern_data_align);+                let alloc_size = value_and_padding_size + extern_data_size;++                debug_assert!(Layout::from_size_align(alloc_size, alloc_align).is_ok());+                Layout::from_size_align_unchecked(alloc_size, alloc_align)+            };++            ptr::drop_in_place(data.value_ptr.as_ptr());+            let alloc_ptr = data.value_ptr.cast::<u8>();++            (alloc_ptr, layout)+        };++        ptr::drop_in_place(data.as_ptr());+        std::alloc::dealloc(alloc_ptr.as_ptr(), layout);+    }++    #[inline]+    fn get_ref_count(&self) -> usize {+        unsafe { *self.ref_count.get() }+    }++    #[inline]+    fn increment_ref_count(&self) {+        unsafe {+            let count = self.ref_count.get();+            *count += 1;+        }+    }++    #[inline]+    fn decrement_ref_count(&self) {+        unsafe {+            let count = self.ref_count.get();+            *count -= 1;+        }+    }+}++#[inline]+fn round_up_to_align(n: usize, align: usize) -> Option<usize> {+    debug_assert!(align.is_power_of_two());+    let align_minus_one = align - 1;+    Some(n.checked_add(align_minus_one)? & !align_minus_one)+}++impl VMExternRef {+    /// Wrap the given value inside an `VMExternRef`.+    pub fn new<T>(value: T) -> VMExternRef+    where+        T: 'static + Any,+    {+        VMExternRef::new_with(|| value)+    }++    /// Construct a new `VMExternRef` in place by invoking `make_value`.+    pub fn new_with<T>(make_value: impl FnOnce() -> T) -> VMExternRef+    where+        T: 'static + Any,+    {+        let value_size = mem::size_of::<T>();+        let value_align = mem::align_of::<T>();++        let extern_data_align = mem::align_of::<VMExternData>();+        let extern_data_size = mem::size_of::<VMExternData>();++        let value_and_padding_size = round_up_to_align(value_size, extern_data_align)+            .unwrap_or_else(|| {+                Self::alloc_failure();+            });++        let alloc_align = std::cmp::max(value_align, extern_data_align);+        let alloc_size = value_and_padding_size+            .checked_add(extern_data_size)+            .unwrap_or_else(|| Self::alloc_failure());++        unsafe {+            debug_assert!(Layout::from_size_align(alloc_size, alloc_align).is_ok());+            let layout = Layout::from_size_align_unchecked(alloc_size, alloc_align);++            let alloc_ptr = std::alloc::alloc(layout);+            let alloc_ptr = NonNull::new(alloc_ptr).unwrap_or_else(|| {+                Self::alloc_failure();+            });++            let value_ptr = alloc_ptr.cast::<T>();+            ptr::write(value_ptr.as_ptr(), make_value());++            let value_ref: &T = value_ptr.as_ref();+            let value_ref: &dyn Any = value_ref as _;+            let value_ptr: *const dyn Any = value_ref as _;+            let value_ptr: *mut dyn Any = value_ptr as _;+            let value_ptr = NonNull::new_unchecked(value_ptr);++            let extern_data_ptr =+                alloc_ptr.cast::<u8>().as_ptr().add(value_and_padding_size) as *mut VMExternData;+            ptr::write(+                extern_data_ptr,+                VMExternData {+                    ref_count: UnsafeCell::new(1),+                    value_ptr,+                },+            );++            VMExternRef(NonNull::new_unchecked(extern_data_ptr))+        }+    }++    /// Turn this `VMExternRef` into a raw, untyped pointer.+    ///+    /// This forgets `self` and does *not* decrement the reference count on the+    /// pointed-to data.+    ///+    /// This `VMExternRef` may be recovered with `VMExternRef::from_raw`.+    pub fn into_raw(self) -> *mut u8 {+        let ptr = self.0.cast::<u8>().as_ptr();+        mem::forget(self);+        ptr+    }++    /// Create a `VMExternRef` from a pointer returned from a previous call to+    /// `VMExternRef::into_raw`.+    ///+    /// # Safety+    ///+    /// Wildly unsafe to use with anything other than the result of a previous+    /// `into_raw` call!+    ///+    /// This method does *not* increment the reference count on the pointed-to+    /// data, so `from_raw` must be called at most *once* on the result of a+    /// previous `into_raw` call. (Ideally, every `into_raw` is later followed+    /// by a `from_raw`, but it is technically memory safe to never call+    /// `from_raw` after `into_raw`: it will leak the pointed-to value, which is+    /// memory safe).+    pub unsafe fn from_raw(ptr: *mut u8) -> Self {+        debug_assert!(!ptr.is_null());+        VMExternRef(NonNull::new_unchecked(ptr).cast())+    }++    #[inline(never)]+    #[cold]+    fn alloc_failure() -> ! {+        panic!("VMExternRef allocation failure")

Perhaps defer to https://doc.rust-lang.org/std/alloc/fn.handle_alloc_error.html?

fitzgen

comment created time in 2 days

Pull request review commentbytecodealliance/wasmtime

Initial, partial support for `externref`

  use std::any::Any; use std::cell::{self, RefCell};+use std::convert::TryFrom; use std::fmt;+use std::marker::PhantomData; use std::rc::{Rc, Weak};--trait InternalRefBase: Any {-    fn as_any(&self) -> &dyn Any;-    fn host_info(&self) -> Option<cell::RefMut<Box<dyn Any>>>;-    fn set_host_info(&self, info: Option<Box<dyn Any>>);-    fn ptr_eq(&self, other: &dyn InternalRefBase) -> bool;-}--#[derive(Clone)]-pub struct InternalRef(Rc<dyn InternalRefBase>);--impl InternalRef {-    pub fn is_ref<T: 'static>(&self) -> bool {-        let r = self.0.as_any();-        Any::is::<HostRef<T>>(r)-    }-    pub fn get_ref<T: 'static>(&self) -> HostRef<T> {-        let r = self.0.as_any();-        r.downcast_ref::<HostRef<T>>()-            .expect("reference is not T type")-            .clone()-    }-}--struct AnyAndHostInfo {-    any: Box<dyn Any>,-    host_info: Option<Box<dyn Any>>,-}--#[derive(Clone)]-pub struct OtherRef(Rc<RefCell<AnyAndHostInfo>>);+use wasmtime_runtime::VMExternRef;  /// Represents an opaque reference to any data within WebAssembly. #[derive(Clone)]-pub enum ExternRef {-    /// A reference to no data.-    Null,-    /// A reference to data stored internally in `wasmtime`.-    Ref(InternalRef),-    /// A reference to data located outside of `wasmtime`.-    Other(OtherRef),+pub struct ExternRef {+    pub(crate) inner: VMExternRef,+    pub(crate) store: Weak<crate::runtime::StoreInner>, }  impl ExternRef {-    /// Creates a new instance of `ExternRef` from `Box<dyn Any>`.-    pub fn new(data: Box<dyn Any>) -> Self {-        let info = AnyAndHostInfo {-            any: data,-            host_info: None,-        };-        ExternRef::Other(OtherRef(Rc::new(RefCell::new(info))))+    /// Creates a new instance of `ExternRef` wrapping the given value.+    pub fn new<T>(store: &crate::Store, value: T) -> ExternRef+    where+        T: 'static + Any,+    {+        let inner = VMExternRef::new(value);+        let store = store.weak();+        ExternRef { inner, store }     }      /// Creates a `Null` reference.-    pub fn null() -> Self {-        ExternRef::Null+    pub fn null() -> Option<Self> {+        None     } -    /// Returns the data stored in the reference if available.-    /// # Panics-    /// Panics if the variant isn't `ExternRef::Other`.-    pub fn data(&self) -> cell::Ref<Box<dyn Any>> {-        match self {-            ExternRef::Other(OtherRef(r)) => cell::Ref::map(r.borrow(), |r| &r.any),-            _ => panic!("expected ExternRef::Other"),-        }+    /// Get this reference's store.+    ///+    /// Returns `None` if this reference outlived its store.+    pub fn store(&self) -> Option<crate::runtime::Store> {+        crate::runtime::Store::upgrade(&self.store)     } -    /// Returns true if the two `ExternRef<T>`'s point to the same value (not just-    /// values that compare as equal).+    /// Get the underlying data for this `ExternRef`.+    pub fn data(&self) -> &dyn Any {+        &*self.inner+    }++    /// just point to two different values that compare as equal).

out of date comment?

fitzgen

comment created time in 2 days

Pull request review commentbytecodealliance/wasmtime

Initial, partial support for `externref`

+//! # `VMExternRef`+//!+//! `VMExternRef` is a reference-counted box for any kind of data that is+//! external and opaque to running Wasm. Sometimes it might hold a Wasmtime+//! thing, other times it might hold something from a Wasmtime embedder and is+//! opaque even to us. It is morally equivalent to `Rc<dyn Any>` in Rust, but+//! additionally always fits in a pointer-sized word. `VMExternRef` is+//! non-nullable, but `Option<VMExternRef>` is a null pointer.+//!+//! The one part of `VMExternRef` that can't ever be opaque to us is the+//! reference count. Even when we don't know what's inside an `VMExternRef`, we+//! need to be able to manipulate its reference count as we add and remove+//! references to it. And we need to do this from compiled Wasm code, so it must+//! be `repr(C)`!+//!+//! ## Memory Layout+//!+//! `VMExternRef` itself is just a pointer to an `VMExternData`, which holds the+//! opaque, boxed value, its reference count, and its vtable pointer.+//!+//! The `VMExternData` struct is *preceded* by the dynamically-sized value boxed+//! up and referenced by one or more `VMExternRef`s:+//!+//! ```ignore+//!      ,-------------------------------------------------------.+//!      |                                                       |+//!      V                                                       |+//!     +----------------------------+-----------+-----------+   |+//!     | dynamically-sized value... | ref_count | value_ptr |---'+//!     +----------------------------+-----------+-----------++//!                                  | VMExternData          |+//!                                  +-----------------------++//!                                   ^+//! +-------------+                   |+//! | VMExternRef |-------------------++//! +-------------+                   |+//!                                   |+//! +-------------+                   |+//! | VMExternRef |-------------------++//! +-------------+                   |+//!                                   |+//!   ...                            ===+//!                                   |+//! +-------------+                   |+//! | VMExternRef |-------------------'+//! +-------------++//! ```+//!+//! The `value_ptr` member always points backwards to the start of the+//! dynamically-sized value (which is also the start of the heap allocation for+//! this value-and-`VMExternData` pair). Because it is a `dyn` pointer, it is+//! fat, and also points to the value's `Any` vtable.+//!+//! The boxed value and the `VMExternRef` footer are held a single heap+//! allocation. The layout described above is used to make satisfying the+//! value's alignment easy: we just need to ensure that the heap allocation used+//! to hold everything satisfies its alignment. It also ensures that we don't+//! need a ton of excess padding between the `VMExternData` and the value for+//! values with large alignment.+//!+//! ## Reference Counting Protocol and Wasm Functions+//!+//! Currently, `VMExternRef`s passed into compiled Wasm functions have move+//! semantics: the host code gives up ownership and does not decrement the+//! reference count. Similarly, `VMExternRef`s returned from compiled Wasm+//! functions also have move semantics: host code takes ownership and the+//! reference count is not incremented.+//!+//! This works well when a reference is passed into Wasm and then passed back+//! out again. However, if a reference is passed into Wasm, but not passed back+//! out, then the reference is leaked. This is only a temporary state, and+//! follow up work will leverage stack maps to fix this issue. Follow+//! https://github.com/bytecodealliance/wasmtime/issues/929 to keep an eye on+//! this.++use std::alloc::Layout;+use std::any::Any;+use std::cell::UnsafeCell;+use std::cmp::Ordering;+use std::hash::{Hash, Hasher};+use std::mem;+use std::ops::Deref;+use std::ptr::{self, NonNull};++/// An external reference to some opaque data.+///+/// `VMExternRef`s dereference to their underlying opaque data as `dyn Any`.+///+/// Unlike the `externref` in the Wasm spec, `VMExternRef`s are non-nullable,+/// and always point to a valid value. You may use `Option<VMExternRef>` to+/// represent nullable references, and `Option<VMExternRef>` is guaranteed to+/// have the same size and alignment as a raw pointer, with `None` represented+/// with the null pointer.+///+/// `VMExternRef`s are reference counted, so cloning is a cheap, shallow+/// operation. It also means they are inherently shared, so you may not get a+/// mutable, exclusive reference to their inner contents, only a shared,+/// immutable reference. You may use interior mutability with `RefCell` or+/// `Mutex` to work around this restriction, if necessary.+///+/// `VMExternRef`s have pointer-equality semantics, not structural-equality+/// semantics. Given two `VMExternRef`s `a` and `b`, `a == b` only if `a` and+/// `b` point to the same allocation. `a` and `b` are considered not equal, even+/// if `a` and `b` are two different identical copies of the same data, if they+/// are in two different allocations. The hashing and ordering implementations+/// also only operate on the pointer.+///+/// # Example+///+/// ```+/// # fn foo() -> Result<(), Box<dyn std::error::Error>> {+/// use std::cell::RefCell;+/// use wasmtime_runtime::VMExternRef;+///+/// // Open a file. Wasm doesn't know about files, but we can let Wasm instances+/// // work with files via opaque `externref` handles.+/// let file = std::fs::File::create("some/file/path")?;+///+/// // Wrap the file up as an `VMExternRef` that can be passed to Wasm.+/// let extern_ref_to_file = VMExternRef::new(RefCell::new(file));+///+/// // `VMExternRef`s dereference to `dyn Any`, so you can use `Any` methods to+/// // perform runtime type checks and downcasts.+///+/// assert!(extern_ref_to_file.is::<RefCell<std::fs::File>>());+/// assert!(!extern_ref_to_file.is::<String>());+///+/// if let Some(file) = extern_ref_to_file.downcast_ref::<RefCell<std::fs::File>>() {+///     use std::io::Write;+///     let mut file = file.borrow_mut();+///     writeln!(&mut file, "Hello, `VMExternRef`!")?;+/// }+/// # Ok(())+/// # }+/// ```+#[derive(Debug)]+#[repr(transparent)]+pub struct VMExternRef(NonNull<VMExternData>);++#[repr(C)]+struct VMExternData {+    // Implicit, dynamically-sized member that always preceded an+    // `VMExternData`.+    //+    // value: [u8],+    //+    /// The reference count for this `VMExternData` and value. When it reaches+    /// zero, we can safely destroy the value and free this heap+    /// allocation. This is an `UnsafeCell`, rather than plain `Cell`, because+    /// it can be modified by compiled Wasm code.+    ///+    /// Note: this field's offset must be kept in sync with+    /// `wasmtime_environ::VMOffsets::vm_extern_data_ref_count()` which is+    /// currently always zero.+    ref_count: UnsafeCell<usize>,++    /// Always points to the implicit, dynamically-sized `value` member that+    /// precedes this `VMExternData`.+    value_ptr: NonNull<dyn Any>,+}++impl Clone for VMExternRef {+    #[inline]+    fn clone(&self) -> VMExternRef {+        self.extern_data().increment_ref_count();+        VMExternRef(self.0)+    }+}++impl Drop for VMExternRef {+    #[inline]+    fn drop(&mut self) {+        let data = self.extern_data();+        data.decrement_ref_count();+        if data.get_ref_count() == 0 {+            // Drop our live reference to `data` before we drop it itself.+            drop(data);+            unsafe {+                VMExternData::drop_and_dealloc(self.0);+            }+        }+    }+}++impl VMExternData {+    unsafe fn drop_and_dealloc(mut data: NonNull<VMExternData>) {+        // Note: we introduce a block scope so that we drop the live+        // reference to the data before we free the heap allocation it+        // resides within after this block.+        let (alloc_ptr, layout) = {+            let data = data.as_mut();+            debug_assert_eq!(data.get_ref_count(), 0);++            // Same thing, but for the dropping the reference to `value` before+            // we drop it itself.+            let layout = {+                let value = data.value_ptr.as_ref();++                let value_size = mem::size_of_val(value);+                let value_align = mem::align_of_val(value);++                let extern_data_size = mem::size_of::<VMExternData>();+                let extern_data_align = mem::align_of::<VMExternData>();++                let value_and_padding_size = round_up_to_align(value_size, extern_data_align)+                    .unwrap_or_else(|| unreachable!());++                let alloc_align = std::cmp::max(value_align, extern_data_align);+                let alloc_size = value_and_padding_size + extern_data_size;++                debug_assert!(Layout::from_size_align(alloc_size, alloc_align).is_ok());+                Layout::from_size_align_unchecked(alloc_size, alloc_align)+            };++            ptr::drop_in_place(data.value_ptr.as_ptr());+            let alloc_ptr = data.value_ptr.cast::<u8>();++            (alloc_ptr, layout)+        };++        ptr::drop_in_place(data.as_ptr());+        std::alloc::dealloc(alloc_ptr.as_ptr(), layout);+    }++    #[inline]+    fn get_ref_count(&self) -> usize {+        unsafe { *self.ref_count.get() }+    }++    #[inline]+    fn increment_ref_count(&self) {+        unsafe {+            let count = self.ref_count.get();+            *count += 1;+        }+    }++    #[inline]+    fn decrement_ref_count(&self) {+        unsafe {+            let count = self.ref_count.get();+            *count -= 1;+        }+    }+}++#[inline]+fn round_up_to_align(n: usize, align: usize) -> Option<usize> {+    debug_assert!(align.is_power_of_two());+    let align_minus_one = align - 1;+    Some(n.checked_add(align_minus_one)? & !align_minus_one)+}++impl VMExternRef {+    /// Wrap the given value inside an `VMExternRef`.+    pub fn new<T>(value: T) -> VMExternRef+    where+        T: 'static + Any,+    {+        VMExternRef::new_with(|| value)+    }++    /// Construct a new `VMExternRef` in place by invoking `make_value`.+    pub fn new_with<T>(make_value: impl FnOnce() -> T) -> VMExternRef+    where+        T: 'static + Any,+    {+        let value_size = mem::size_of::<T>();+        let value_align = mem::align_of::<T>();++        let extern_data_align = mem::align_of::<VMExternData>();+        let extern_data_size = mem::size_of::<VMExternData>();++        let value_and_padding_size = round_up_to_align(value_size, extern_data_align)+            .unwrap_or_else(|| {+                Self::alloc_failure();+            });++        let alloc_align = std::cmp::max(value_align, extern_data_align);+        let alloc_size = value_and_padding_size+            .checked_add(extern_data_size)+            .unwrap_or_else(|| Self::alloc_failure());++        unsafe {+            debug_assert!(Layout::from_size_align(alloc_size, alloc_align).is_ok());+            let layout = Layout::from_size_align_unchecked(alloc_size, alloc_align);++            let alloc_ptr = std::alloc::alloc(layout);+            let alloc_ptr = NonNull::new(alloc_ptr).unwrap_or_else(|| {+                Self::alloc_failure();+            });++            let value_ptr = alloc_ptr.cast::<T>();+            ptr::write(value_ptr.as_ptr(), make_value());++            let value_ref: &T = value_ptr.as_ref();+            let value_ref: &dyn Any = value_ref as _;+            let value_ptr: *const dyn Any = value_ref as _;+            let value_ptr: *mut dyn Any = value_ptr as _;+            let value_ptr = NonNull::new_unchecked(value_ptr);++            let extern_data_ptr =+                alloc_ptr.cast::<u8>().as_ptr().add(value_and_padding_size) as *mut VMExternData;+            ptr::write(+                extern_data_ptr,+                VMExternData {+                    ref_count: UnsafeCell::new(1),+                    value_ptr,+                },+            );++            VMExternRef(NonNull::new_unchecked(extern_data_ptr))+        }+    }++    /// Turn this `VMExternRef` into a raw, untyped pointer.+    ///+    /// This forgets `self` and does *not* decrement the reference count on the+    /// pointed-to data.+    ///+    /// This `VMExternRef` may be recovered with `VMExternRef::from_raw`.+    pub fn into_raw(self) -> *mut u8 {+        let ptr = self.0.cast::<u8>().as_ptr();+        mem::forget(self);+        ptr+    }++    /// Create a `VMExternRef` from a pointer returned from a previous call to+    /// `VMExternRef::into_raw`.+    ///+    /// # Safety+    ///+    /// Wildly unsafe to use with anything other than the result of a previous+    /// `into_raw` call!+    ///+    /// This method does *not* increment the reference count on the pointed-to+    /// data, so `from_raw` must be called at most *once* on the result of a+    /// previous `into_raw` call. (Ideally, every `into_raw` is later followed+    /// by a `from_raw`, but it is technically memory safe to never call+    /// `from_raw` after `into_raw`: it will leak the pointed-to value, which is+    /// memory safe).+    pub unsafe fn from_raw(ptr: *mut u8) -> Self {+        debug_assert!(!ptr.is_null());+        VMExternRef(NonNull::new_unchecked(ptr).cast())+    }++    #[inline(never)]+    #[cold]+    fn alloc_failure() -> ! {+        panic!("VMExternRef allocation failure")+    }++    #[inline]+    fn extern_data(&self) -> &VMExternData {+        unsafe { self.0.as_ref() }+    }+}++impl PartialEq for VMExternRef {

This and traits below seem dangerous since I'd naively expect it to defer to the underlying type (which can't be done here b/c it's not generic). Could this be exposed as a free-function?

fitzgen

comment created time in 2 days

Pull request review commentbytecodealliance/wasmtime

Initial, partial support for `externref`

 impl Func {         (get15, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15)     } -    pub(crate) fn store(&self) -> &Store {+    // This should be `pub(crate)` but the C API happens to need it in one+    // place.+    #[doc(hidden)]+    pub fn store(&self) -> &Store {

Is this a place in the C API where the store should be passed in?

fitzgen

comment created time in 2 days

Pull request review commentbytecodealliance/wasmtime

Initial, partial support for `externref`

+//! # `VMExternRef`+//!+//! `VMExternRef` is a reference-counted box for any kind of data that is+//! external and opaque to running Wasm. Sometimes it might hold a Wasmtime+//! thing, other times it might hold something from a Wasmtime embedder and is+//! opaque even to us. It is morally equivalent to `Rc<dyn Any>` in Rust, but+//! additionally always fits in a pointer-sized word. `VMExternRef` is+//! non-nullable, but `Option<VMExternRef>` is a null pointer.+//!+//! The one part of `VMExternRef` that can't ever be opaque to us is the+//! reference count. Even when we don't know what's inside an `VMExternRef`, we+//! need to be able to manipulate its reference count as we add and remove+//! references to it. And we need to do this from compiled Wasm code, so it must+//! be `repr(C)`!+//!+//! ## Memory Layout+//!+//! `VMExternRef` itself is just a pointer to an `VMExternData`, which holds the+//! opaque, boxed value, its reference count, and its vtable pointer.+//!+//! The `VMExternData` struct is *preceded* by the dynamically-sized value boxed+//! up and referenced by one or more `VMExternRef`s:+//!+//! ```ignore+//!      ,-------------------------------------------------------.+//!      |                                                       |+//!      V                                                       |+//!     +----------------------------+-----------+-----------+   |+//!     | dynamically-sized value... | ref_count | value_ptr |---'+//!     +----------------------------+-----------+-----------++//!                                  | VMExternData          |+//!                                  +-----------------------++//!                                   ^+//! +-------------+                   |+//! | VMExternRef |-------------------++//! +-------------+                   |+//!                                   |+//! +-------------+                   |+//! | VMExternRef |-------------------++//! +-------------+                   |+//!                                   |+//!   ...                            ===+//!                                   |+//! +-------------+                   |+//! | VMExternRef |-------------------'+//! +-------------++//! ```+//!+//! The `value_ptr` member always points backwards to the start of the+//! dynamically-sized value (which is also the start of the heap allocation for+//! this value-and-`VMExternData` pair). Because it is a `dyn` pointer, it is+//! fat, and also points to the value's `Any` vtable.+//!+//! The boxed value and the `VMExternRef` footer are held a single heap+//! allocation. The layout described above is used to make satisfying the+//! value's alignment easy: we just need to ensure that the heap allocation used+//! to hold everything satisfies its alignment. It also ensures that we don't+//! need a ton of excess padding between the `VMExternData` and the value for+//! values with large alignment.+//!+//! ## Reference Counting Protocol and Wasm Functions+//!+//! Currently, `VMExternRef`s passed into compiled Wasm functions have move+//! semantics: the host code gives up ownership and does not decrement the+//! reference count. Similarly, `VMExternRef`s returned from compiled Wasm+//! functions also have move semantics: host code takes ownership and the+//! reference count is not incremented.+//!+//! This works well when a reference is passed into Wasm and then passed back+//! out again. However, if a reference is passed into Wasm, but not passed back+//! out, then the reference is leaked. This is only a temporary state, and+//! follow up work will leverage stack maps to fix this issue. Follow+//! https://github.com/bytecodealliance/wasmtime/issues/929 to keep an eye on+//! this.++use std::alloc::Layout;+use std::any::Any;+use std::cell::UnsafeCell;+use std::cmp::Ordering;+use std::hash::{Hash, Hasher};+use std::mem;+use std::ops::Deref;+use std::ptr::{self, NonNull};++/// An external reference to some opaque data.+///+/// `VMExternRef`s dereference to their underlying opaque data as `dyn Any`.+///+/// Unlike the `externref` in the Wasm spec, `VMExternRef`s are non-nullable,+/// and always point to a valid value. You may use `Option<VMExternRef>` to+/// represent nullable references, and `Option<VMExternRef>` is guaranteed to+/// have the same size and alignment as a raw pointer, with `None` represented+/// with the null pointer.+///+/// `VMExternRef`s are reference counted, so cloning is a cheap, shallow+/// operation. It also means they are inherently shared, so you may not get a+/// mutable, exclusive reference to their inner contents, only a shared,+/// immutable reference. You may use interior mutability with `RefCell` or+/// `Mutex` to work around this restriction, if necessary.+///+/// `VMExternRef`s have pointer-equality semantics, not structural-equality+/// semantics. Given two `VMExternRef`s `a` and `b`, `a == b` only if `a` and+/// `b` point to the same allocation. `a` and `b` are considered not equal, even+/// if `a` and `b` are two different identical copies of the same data, if they+/// are in two different allocations. The hashing and ordering implementations+/// also only operate on the pointer.+///+/// # Example+///+/// ```+/// # fn foo() -> Result<(), Box<dyn std::error::Error>> {+/// use std::cell::RefCell;+/// use wasmtime_runtime::VMExternRef;+///+/// // Open a file. Wasm doesn't know about files, but we can let Wasm instances+/// // work with files via opaque `externref` handles.+/// let file = std::fs::File::create("some/file/path")?;+///+/// // Wrap the file up as an `VMExternRef` that can be passed to Wasm.+/// let extern_ref_to_file = VMExternRef::new(RefCell::new(file));+///+/// // `VMExternRef`s dereference to `dyn Any`, so you can use `Any` methods to+/// // perform runtime type checks and downcasts.+///+/// assert!(extern_ref_to_file.is::<RefCell<std::fs::File>>());+/// assert!(!extern_ref_to_file.is::<String>());+///+/// if let Some(file) = extern_ref_to_file.downcast_ref::<RefCell<std::fs::File>>() {+///     use std::io::Write;+///     let mut file = file.borrow_mut();+///     writeln!(&mut file, "Hello, `VMExternRef`!")?;+/// }+/// # Ok(())+/// # }+/// ```+#[derive(Debug)]+#[repr(transparent)]+pub struct VMExternRef(NonNull<VMExternData>);++#[repr(C)]+struct VMExternData {+    // Implicit, dynamically-sized member that always preceded an+    // `VMExternData`.+    //+    // value: [u8],+    //+    /// The reference count for this `VMExternData` and value. When it reaches+    /// zero, we can safely destroy the value and free this heap+    /// allocation. This is an `UnsafeCell`, rather than plain `Cell`, because+    /// it can be modified by compiled Wasm code.+    ///+    /// Note: this field's offset must be kept in sync with+    /// `wasmtime_environ::VMOffsets::vm_extern_data_ref_count()` which is+    /// currently always zero.+    ref_count: UnsafeCell<usize>,++    /// Always points to the implicit, dynamically-sized `value` member that+    /// precedes this `VMExternData`.+    value_ptr: NonNull<dyn Any>,+}++impl Clone for VMExternRef {+    #[inline]+    fn clone(&self) -> VMExternRef {+        self.extern_data().increment_ref_count();+        VMExternRef(self.0)+    }+}++impl Drop for VMExternRef {+    #[inline]+    fn drop(&mut self) {+        let data = self.extern_data();+        data.decrement_ref_count();+        if data.get_ref_count() == 0 {+            // Drop our live reference to `data` before we drop it itself.+            drop(data);+            unsafe {+                VMExternData::drop_and_dealloc(self.0);+            }+        }+    }+}++impl VMExternData {+    unsafe fn drop_and_dealloc(mut data: NonNull<VMExternData>) {+        // Note: we introduce a block scope so that we drop the live+        // reference to the data before we free the heap allocation it+        // resides within after this block.+        let (alloc_ptr, layout) = {+            let data = data.as_mut();+            debug_assert_eq!(data.get_ref_count(), 0);++            // Same thing, but for the dropping the reference to `value` before+            // we drop it itself.+            let layout = {+                let value = data.value_ptr.as_ref();++                let value_size = mem::size_of_val(value);+                let value_align = mem::align_of_val(value);++                let extern_data_size = mem::size_of::<VMExternData>();+                let extern_data_align = mem::align_of::<VMExternData>();++                let value_and_padding_size = round_up_to_align(value_size, extern_data_align)+                    .unwrap_or_else(|| unreachable!());

.unwrap()?

fitzgen

comment created time in 2 days

Pull request review commentbytecodealliance/wasmtime

Initial, partial support for `externref`

+//! # `VMExternRef`+//!+//! `VMExternRef` is a reference-counted box for any kind of data that is+//! external and opaque to running Wasm. Sometimes it might hold a Wasmtime+//! thing, other times it might hold something from a Wasmtime embedder and is+//! opaque even to us. It is morally equivalent to `Rc<dyn Any>` in Rust, but+//! additionally always fits in a pointer-sized word. `VMExternRef` is+//! non-nullable, but `Option<VMExternRef>` is a null pointer.+//!+//! The one part of `VMExternRef` that can't ever be opaque to us is the+//! reference count. Even when we don't know what's inside an `VMExternRef`, we+//! need to be able to manipulate its reference count as we add and remove+//! references to it. And we need to do this from compiled Wasm code, so it must+//! be `repr(C)`!+//!+//! ## Memory Layout+//!+//! `VMExternRef` itself is just a pointer to an `VMExternData`, which holds the+//! opaque, boxed value, its reference count, and its vtable pointer.+//!+//! The `VMExternData` struct is *preceded* by the dynamically-sized value boxed+//! up and referenced by one or more `VMExternRef`s:+//!+//! ```ignore+//!      ,-------------------------------------------------------.+//!      |                                                       |+//!      V                                                       |+//!     +----------------------------+-----------+-----------+   |+//!     | dynamically-sized value... | ref_count | value_ptr |---'+//!     +----------------------------+-----------+-----------++//!                                  | VMExternData          |+//!                                  +-----------------------++//!                                   ^+//! +-------------+                   |+//! | VMExternRef |-------------------++//! +-------------+                   |+//!                                   |+//! +-------------+                   |+//! | VMExternRef |-------------------++//! +-------------+                   |+//!                                   |+//!   ...                            ===+//!                                   |+//! +-------------+                   |+//! | VMExternRef |-------------------'+//! +-------------++//! ```+//!+//! The `value_ptr` member always points backwards to the start of the+//! dynamically-sized value (which is also the start of the heap allocation for+//! this value-and-`VMExternData` pair). Because it is a `dyn` pointer, it is+//! fat, and also points to the value's `Any` vtable.+//!+//! The boxed value and the `VMExternRef` footer are held a single heap+//! allocation. The layout described above is used to make satisfying the+//! value's alignment easy: we just need to ensure that the heap allocation used+//! to hold everything satisfies its alignment. It also ensures that we don't+//! need a ton of excess padding between the `VMExternData` and the value for+//! values with large alignment.+//!+//! ## Reference Counting Protocol and Wasm Functions+//!+//! Currently, `VMExternRef`s passed into compiled Wasm functions have move+//! semantics: the host code gives up ownership and does not decrement the+//! reference count. Similarly, `VMExternRef`s returned from compiled Wasm+//! functions also have move semantics: host code takes ownership and the+//! reference count is not incremented.+//!+//! This works well when a reference is passed into Wasm and then passed back+//! out again. However, if a reference is passed into Wasm, but not passed back+//! out, then the reference is leaked. This is only a temporary state, and+//! follow up work will leverage stack maps to fix this issue. Follow+//! https://github.com/bytecodealliance/wasmtime/issues/929 to keep an eye on+//! this.++use std::alloc::Layout;+use std::any::Any;+use std::cell::UnsafeCell;+use std::cmp::Ordering;+use std::hash::{Hash, Hasher};+use std::mem;+use std::ops::Deref;+use std::ptr::{self, NonNull};++/// An external reference to some opaque data.+///+/// `VMExternRef`s dereference to their underlying opaque data as `dyn Any`.+///+/// Unlike the `externref` in the Wasm spec, `VMExternRef`s are non-nullable,+/// and always point to a valid value. You may use `Option<VMExternRef>` to+/// represent nullable references, and `Option<VMExternRef>` is guaranteed to+/// have the same size and alignment as a raw pointer, with `None` represented+/// with the null pointer.+///+/// `VMExternRef`s are reference counted, so cloning is a cheap, shallow+/// operation. It also means they are inherently shared, so you may not get a+/// mutable, exclusive reference to their inner contents, only a shared,+/// immutable reference. You may use interior mutability with `RefCell` or+/// `Mutex` to work around this restriction, if necessary.+///+/// `VMExternRef`s have pointer-equality semantics, not structural-equality+/// semantics. Given two `VMExternRef`s `a` and `b`, `a == b` only if `a` and+/// `b` point to the same allocation. `a` and `b` are considered not equal, even+/// if `a` and `b` are two different identical copies of the same data, if they+/// are in two different allocations. The hashing and ordering implementations+/// also only operate on the pointer.+///+/// # Example+///+/// ```+/// # fn foo() -> Result<(), Box<dyn std::error::Error>> {+/// use std::cell::RefCell;+/// use wasmtime_runtime::VMExternRef;+///+/// // Open a file. Wasm doesn't know about files, but we can let Wasm instances+/// // work with files via opaque `externref` handles.+/// let file = std::fs::File::create("some/file/path")?;+///+/// // Wrap the file up as an `VMExternRef` that can be passed to Wasm.+/// let extern_ref_to_file = VMExternRef::new(RefCell::new(file));+///+/// // `VMExternRef`s dereference to `dyn Any`, so you can use `Any` methods to+/// // perform runtime type checks and downcasts.+///+/// assert!(extern_ref_to_file.is::<RefCell<std::fs::File>>());+/// assert!(!extern_ref_to_file.is::<String>());+///+/// if let Some(file) = extern_ref_to_file.downcast_ref::<RefCell<std::fs::File>>() {+///     use std::io::Write;+///     let mut file = file.borrow_mut();+///     writeln!(&mut file, "Hello, `VMExternRef`!")?;+/// }+/// # Ok(())+/// # }+/// ```+#[derive(Debug)]+#[repr(transparent)]+pub struct VMExternRef(NonNull<VMExternData>);++#[repr(C)]+struct VMExternData {+    // Implicit, dynamically-sized member that always preceded an+    // `VMExternData`.+    //+    // value: [u8],+    //+    /// The reference count for this `VMExternData` and value. When it reaches+    /// zero, we can safely destroy the value and free this heap+    /// allocation. This is an `UnsafeCell`, rather than plain `Cell`, because+    /// it can be modified by compiled Wasm code.+    ///+    /// Note: this field's offset must be kept in sync with+    /// `wasmtime_environ::VMOffsets::vm_extern_data_ref_count()` which is+    /// currently always zero.+    ref_count: UnsafeCell<usize>,++    /// Always points to the implicit, dynamically-sized `value` member that+    /// precedes this `VMExternData`.+    value_ptr: NonNull<dyn Any>,+}++impl Clone for VMExternRef {+    #[inline]+    fn clone(&self) -> VMExternRef {+        self.extern_data().increment_ref_count();+        VMExternRef(self.0)+    }+}++impl Drop for VMExternRef {+    #[inline]+    fn drop(&mut self) {+        let data = self.extern_data();+        data.decrement_ref_count();+        if data.get_ref_count() == 0 {+            // Drop our live reference to `data` before we drop it itself.+            drop(data);+            unsafe {+                VMExternData::drop_and_dealloc(self.0);+            }+        }+    }+}++impl VMExternData {+    unsafe fn drop_and_dealloc(mut data: NonNull<VMExternData>) {+        // Note: we introduce a block scope so that we drop the live+        // reference to the data before we free the heap allocation it+        // resides within after this block.+        let (alloc_ptr, layout) = {+            let data = data.as_mut();+            debug_assert_eq!(data.get_ref_count(), 0);++            // Same thing, but for the dropping the reference to `value` before+            // we drop it itself.+            let layout = {+                let value = data.value_ptr.as_ref();++                let value_size = mem::size_of_val(value);+                let value_align = mem::align_of_val(value);++                let extern_data_size = mem::size_of::<VMExternData>();+                let extern_data_align = mem::align_of::<VMExternData>();++                let value_and_padding_size = round_up_to_align(value_size, extern_data_align)+                    .unwrap_or_else(|| unreachable!());++                let alloc_align = std::cmp::max(value_align, extern_data_align);+                let alloc_size = value_and_padding_size + extern_data_size;++                debug_assert!(Layout::from_size_align(alloc_size, alloc_align).is_ok());+                Layout::from_size_align_unchecked(alloc_size, alloc_align)

This looks like it's sharing a lot of code with allocation below, could that be refactored out to a shared function?

fitzgen

comment created time in 2 days

push eventalexcrichton/compiler-builtins

Alex Crichton

commit sha 6bcb1abe2bae699ee5b8f11f5c6a9104c028c2b4

Update libm

view details

push time in 2 days

push eventrust-lang/libm

Alex Crichton

commit sha fe396e00b7e47821a81c4c87a481ddc6af1d2cdf

Use macros for more division/array checks (#244) * Use macros for more division/array checks This commit moves over more array accesses to the `i!` macro to avoid bounds checks when debug assertions are disabled. This is surfaced from rust-lang/compiler-builtins#360 where recent changes in codegen units has caused some bounds checks to not get elided in release mode. This also adds a `div!` macro to work around rust-lang/rust#72751. * Don't test/bench our shim crate It's not intended to run all our tests

view details

push time in 2 days

delete branch alexcrichton/libm

delete branch : less-panics

delete time in 2 days

PR merged rust-lang/libm

Use macros for more division/array checks

This commit moves over more array accesses to the i! macro to avoid bounds checks when debug assertions are disabled. This is surfaced from rust-lang/compiler-builtins#360 where recent changes in codegen units has caused some bounds checks to not get elided in release mode. This also adds a div! macro to work around rust-lang/rust#72751.

+53 -35

2 comments

12 changed files

alexcrichton

pr closed time in 2 days

Pull request review commentalexcrichton/curl-rust

Experimental use of constructor functions to call init

 mod panic; /// /// It's not required to call this before the library is used, but it's /// recommended to do so as soon as the program starts.+#[inline] pub fn init() {+    /// An exported constructor function. On supported platforms, this will be+    /// invoked automatically before the program's `main` is called.+    #[cfg_attr(any(target_os = "linux", target_os = "freebsd", target_os = "android"), link_section = ".init_array")]+    #[cfg_attr(target_os = "macos", link_section = "__DATA,__mod_init_func")]+    #[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")]+    static INIT_CTOR: extern "C" fn() = init_inner;++    /// Used to prevent concurrent calls when initializing after `main` starts.     static INIT: Once = Once::new();-    INIT.call_once(|| {-        platform_init();-        unsafe {-            assert_eq!(curl_sys::curl_global_init(curl_sys::CURL_GLOBAL_ALL), 0);-        } -        // Note that we explicitly don't schedule a call to-        // `curl_global_cleanup`. The documentation for that function says-        //-        // > You must not call it when any other thread in the program (i.e. a-        // > thread sharing the same memory) is running. This doesn't just mean-        // > no other thread that is using libcurl.+    // We invoke our init function through our static to ensure the symbol isn't+    // optimized away by a bug: https://github.com/rust-lang/rust/issues/47384+    INIT.call_once(|| INIT_CTOR());

Ah I was also seeing rustfmt issues. The issue looks like it's the extprim crate, which I don't really have time to dig into where it's coming from right now.

I think ctest is largely unmaintained by gnzlbg now. I started a fork of it at https://github.com/alexcrichton/ctest/tree/use-maybe-unini and we can add more patches there to use here if necessary.

sagebind

comment created time in 2 days

push eventalexcrichton/libm

Alex Crichton

commit sha 703d46e4969b0f42b8c4c347e0c8edeb64bf6db5

Use macros for more division/array checks This commit moves over more array accesses to the `i!` macro to avoid bounds checks when debug assertions are disabled. This is surfaced from rust-lang/compiler-builtins#360 where recent changes in codegen units has caused some bounds checks to not get elided in release mode. This also adds a `div!` macro to work around rust-lang/rust#72751.

view details

Alex Crichton

commit sha 41e959ad7173fa6e071a0f27e495a0290437c92d

Don't test/bench our shim crate It's not intended to run all our tests

view details

push time in 2 days

pull request commentrust-lang/libm

Use macros for more division/array checks

That's not really how this is degined to be used. Like the i! macro definition the unsafety is encapsulated in the macro, rightfully so because it's supposed to be a safe macro. The only purpose is to cater to compiler-builtins which can't panic in release mode, so we're relying on testing elsewhere to weed out possible cases where it does indeed panic or cause unsafety.

alexcrichton

comment created time in 2 days

pull request commentrust-lang/compiler-builtins

Expand wasm32 testing on CI

Ok I think https://github.com/rust-lang/libm/pull/244 will fix CI here, so after that's merged I'll land this here.

alexcrichton

comment created time in 2 days

push eventalexcrichton/libm

Alex Crichton

commit sha b25021193be1a50a160e8a5408942f7dd74605ff

Use macros for more division/array checks This commit moves over more array accesses to the `i!` macro to avoid bounds checks when debug assertions are disabled. This is surfaced from rust-lang/compiler-builtins#360 where recent changes in codegen units has caused some bounds checks to not get elided in release mode. This also adds a `div!` macro to work around rust-lang/rust#72751.

view details

Alex Crichton

commit sha 07ec9c1775ea997e1106d9c166be4232276bdd20

Don't test/bench our shim crate It's not intended to run all our tests

view details

push time in 2 days

push eventalexcrichton/libm

Alex Crichton

commit sha 04a04ff919eda53926650fc7eccfa295795695b7

Don't test/bench our shim crate It's not intended to run all our tests

view details

push time in 2 days

push eventalexcrichton/libm

Alex Crichton

commit sha 2267138238a1fad6f7228ee53603ab861a98a93d

Only benchmark one crate

view details

push time in 2 days

PullRequestEvent

PR closed rust-lang/libm

Use macros for more division/array checks

This commit moves over more array accesses to the i! macro to avoid bounds checks when debug assertions are disabled. This is surfaced from rust-lang/compiler-builtins#360 where recent changes in codegen units has caused some bounds checks to not get elided in release mode. This also adds a div! macro to work around rust-lang/rust#72751.

+44 -30

0 comment

10 changed files

alexcrichton

pr closed time in 2 days

push eventalexcrichton/compiler-builtins

Alex Crichton

commit sha 57d1050a1ebbbac25096c805a16e85ce08f8dc35

Update libm

view details

push time in 2 days

create barnchalexcrichton/libm

branch : less-panics

created branch time in 2 days

PR opened rust-lang/libm

Use macros for more division/array checks

This commit moves over more array accesses to the i! macro to avoid bounds checks when debug assertions are disabled. This is surfaced from rust-lang/compiler-builtins#360 where recent changes in codegen units has caused some bounds checks to not get elided in release mode. This also adds a div! macro to work around rust-lang/rust#72751.

+44 -30

0 comment

10 changed files

pr created time in 2 days

issue openedrust-lang/rust

Suboptimal debug codegen for integer division with a constant rhs

Codegen for division in general needs to

created time in 2 days

issue commentalexcrichton/curl-rust

curl_global_init footgun

Bah unfortunately not as illuminating as I thought it would be. Thanks though!

sagebind

comment created time in 2 days

push eventalexcrichton/compiler-builtins

Alex Crichton

commit sha 3c85e4f3de088e492fd49dbfcaba23be275e09c4

Expand wasm32 testing on CI Run the full `run.sh` test script to get full assertions, including that nothing in the wasm compiler-builtins is panicking. Unfortunately it's currently panicking, so this is good to weed out!

view details

push time in 2 days

push eventbytecodealliance/wasmtime

Deploy from CI

commit sha d136980c4990de336f2421568b8bfb613ec7fac2

Deploy 94380bf2b7f69e353df03661a618a579dfb6cdc7 to gh-pages

view details

push time in 2 days

more