1. 程式人生 > >[WASM] Access WebAssembly Memory Directly from JavaScript

[WASM] Access WebAssembly Memory Directly from JavaScript

While JavaScript has a garbage-collected heap, WebAssembly has a linear memory space. Nevertheless using a JavaScript ArrayBuffer, we can read and write to WebAssembly’s memory space.

 

lib.rs:

#[macro_use]
extern crate cfg_if;

extern crate wasm_bindgen;
use wasm_bindgen::prelude::
*; cfg_if! { // When the `console_error_panic_hook` feature is enabled, we can call the // `set_panic_hook` function to get better error messages if we ever panic. if #[cfg(feature = "console_error_panic_hook")] { extern crate console_error_panic_hook; use console_error_panic_hook::set_once as set_panic_hook; } } cfg_if
! { // When the `wee_alloc` feature is enabled, use `wee_alloc` as the global // allocator. if #[cfg(feature = "wee_alloc")] { extern crate wee_alloc; #[global_allocator] static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; } } // Definitions of the functionality available in JS, which wasm-bindgen will
// generate shims for today (and eventually these should be near-0 cost!) // // These definitions need to be hand-written today but the current vision is // that we'll use WebIDL to generate this `extern` block into a crate which you // can link and import. There's a tracking issue for this at // https://github.com/rustwasm/wasm-bindgen/issues/42 // // In the meantime these are written out by hand and correspond to the names and // signatures documented on MDN, for example #[wasm_bindgen] extern "C" { type HTMLDocument; static document: HTMLDocument; #[wasm_bindgen(method)] fn createElement(this: &HTMLDocument, tagName: &str) -> Element; #[wasm_bindgen(method, getter)] fn body(this: &HTMLDocument) -> Element; type Element; #[wasm_bindgen(method, setter = innerHTML)] fn set_inner_html(this: &Element, html: &str); #[wasm_bindgen(method, js_name = appendChild)] fn append_child(this: &Element, other: Element); #[wasm_bindgen(js_namespace = console)] fn log(msg: &str); } macro_rules! log { ($($t:tt)*) => (log(&format!($($t)*))) }
View Code
#[wasm_bindgen]
pub struct Color {
    red: u8,
    green: u8,
    blue: u8,
}

#[wasm_bindgen]
pub struct Image {
    pixels: Vec<Color>,
}

#[wasm_bindgen]
impl Image {
    pub fn new() -> Image {
        let color1 = Color {
            red: 255,
            green: 0,
            blue: 0,
        };
        let color2 = Color {
            red: 60,
            green: 70,
            blue: 90,
        };
        let pixels = vec![color1, color2];
        Image {
            pixels
        }
    }

    pub fn pixels_ptr(&self) -> *const Color {
        self.pixels.as_ptr()
    }
}

We create a Image object, which has two functions, new() and pixels_ptr() which can be called by javascript. new() is a constructor function, pixels_ptr() is a instant method.

 

app.js:

import { memory } from "../crate/pkg/rust_webpack_bg";
import { Image } from "../crate/pkg/rust_webpack";

const image = Image.new();
const pixelsPointer = image.pixels_ptr();
const pixels = new Uint8Array(memory.buffer, pixelsPointer, 6);
console.log(pixels);

function numToHex(value) {
    const hex = value.toString(16);
    return hex.length === 1 ? `0${hex}` : hex;
  }
  
  function drawPixel(x, y, color) {
    const ctx = canvas.getContext("2d");
    ctx.fillStyle = `#${numToHex(color[0])}${numToHex(color[1])}${numToHex(
      color[2]
    )}`;
    ctx.fillRect(x, y, 100, 100);
  }
  
  const canvas = document.createElement("canvas");
  document.body.appendChild(canvas);
  drawPixel(0, 0, pixels.slice(0, 3));
  drawPixel(100, 0, pixels.slice(3, 6));