Saltar al contenido

¿Cómo puedo devolver un JavaScript? string desde una función WebAssembly

Entiende el código de forma correcta antes de aplicarlo a tu trabajo y si ttienes algo que aportar puedes dejarlo en la sección de comentarios.

Solución:

WebAssembly no admite de forma nativa un string tipo, más bien admite i32 / i64 / f32 / f64 tipos de valor, así como i8 / i16 para almacenamiento.

Puede interactuar con una instancia de WebAssembly usando:

  • exports, donde desde JavaScript se llama a WebAssembly, y WebAssembly devuelve un tipo de valor único.
  • imports donde WebAssembly llama a JavaScript, con tantos tipos de valores como desee (nota: el recuento debe conocerse en el momento de la compilación del módulo, esto no es un array y no es variadic).
  • Memory.buffer, que es un ArrayBuffer que se puede indexar usando (entre otros) Uint8Array.

Depende de lo que quieras hacer, pero parece que acceder al búfer directamente es lo más fácil:

const bin = ...; // WebAssembly binary, I assume below that it imports a memory from module "imports", field "memory".
const module = new WebAssembly.Module(bin);
const memory = new WebAssembly.Memory( initial: 2 ); // Size is in pages.
const instance = new WebAssembly.Instance(module,  imports:  memory: memory  );
const arrayBuffer = memory.buffer;
const buffer = new Uint8Array(arrayBuffer);

Si su módulo tuviera un start función, luego se ejecutó en el momento de la instanciación. De lo contrario, es probable que tenga una exportación a la que llame, p. Ej. instance.exports.doIt().

Una vez hecho esto, debe obtener string tamaño + índice en la memoria, que también expondría a través de una exportación:

const size = instance.exports.myStringSize();
const index = instance.exports.myStringIndex();

Luego lo leerías del búfer:

let s = "";
for (let i = index; i < index + size; ++i)
  s += String.fromCharCode(buffer[i]);

Tenga en cuenta que estoy leyendo valores de 8 bits del búfer, por lo que supongo que las cadenas eran ASCII. Eso es lo que std::string le daría (el índice en la memoria sería lo que .c_str() devuelve), pero para exponer algo más, como UTF-8, necesitaría usar una biblioteca C ++ compatible con UTF-8, y luego leer UTF-8 usted mismo desde JavaScript, obtener los puntos de código y usar String.fromCodePoint.

También puede confiar en el string ser null-terminó, lo cual no hice aquí.

También puede utilizar el TextDecoder API una vez que esté disponible más ampliamente en los navegadores mediante la creación de una ArrayBufferView en el WebAssembly.Memory's buffer (que es un ArrayBuffer).


Si, en cambio, está haciendo algo como iniciar sesión desde WebAssembly a JavaScript, entonces puede exponer el Memory como arriba, y luego desde WebAssembly declare una importación que llame a JavaScript con tamaño + posición. Puede crear una instancia de su módulo como:

const memory = new WebAssembly.Memory( initial: 2 );
const arrayBuffer = memory.buffer;
const buffer = new Uint8Array(arrayBuffer);
const instance = new WebAssembly.Instance(module, 
    imports: 
        memory: memory,
        logString: (size, index) => 
            let s = "";
            for (let i = index; i < index + size; ++i)
                s += String.fromCharCode(buffer[i]);
            console.log(s);
        
    
);

Esto tiene la salvedad de que si alguna vez aumenta la memoria (ya sea a través de JavaScript usando Memory.prototype.grow, o usando el grow_memory opcode) entonces el ArrayBuffer se castra y necesitas crearlo de nuevo.


Sobre la recolección de basura: WebAssembly.Module / WebAssembly.Instance / WebAssembly.Memory son basura recolectada por el motor de JavaScript, pero eso es un martillo bastante grande. Es probable que desee GC cadenas, y eso no es posible actualmente para los objetos que viven dentro de un WebAssembly.Memory. Hemos hablado de agregar compatibilidad con GC en el futuro.

Actualización 2020

Las cosas han cambiado desde que se publicaron las otras respuestas.

Hoy apostaría por los tipos de interfaz de WebAssembly, ver más abajo.

Como preguntaste específicamente sobre C ++, consulta:

  • Nbind:

nbind: encabezados mágicos que hacen que su biblioteca C ++ sea accesible desde JavaScript

nbind es un conjunto de encabezados que hacen que su biblioteca de C ++ 11 sea accesible desde JavaScript. Con una sola instrucción #include, su compilador de C ++ genera los enlaces necesarios sin herramientas adicionales. Su biblioteca se puede utilizar como un complemento de Node.js o, si se compila en asm.js con Emscripten, directamente en páginas web sin ningún complemento.

  • Embind:

Embind se utiliza para vincular funciones y clases de C ++ a JavaScript, de modo que el código compilado pueda ser utilizado de forma natural por JavaScript "normal". Embind también admite la llamada de clases de JavaScript desde C ++.

Vea las siguientes propuestas de WebAssembly:

  • Tipos de JS
  • Tipos de referencia
  • Tipos de interfaz

La propuesta agrega un nuevo conjunto de tipos de interfaz a WebAssembly que describen valores de alto nivel (como cadenas, secuencias, registros y variantes) sin comprometerse con una única representación de memoria o esquema de uso compartido. Los tipos de interfaz solo se pueden usar en las interfaces de los módulos y solo se pueden producir o consumir mediante adaptadores de interfaz declarativos.

Para obtener más información y una gran explicación, consulte:

  • Tipos de interfaz de WebAssembly: ¡Interopere con todas las cosas! por Lin Clark

Ya puede usarlo con algunas funciones experimentales, consulte:

  • https://www.youtube.com/watch?v=Qn_4F3foB3Q

Para ver un buen ejemplo del mundo real utilizando otro enfoque, consulte:

  • libsodium.js

libsodium.js: la biblioteca de criptografía de sodio compilada en WebAssembly y JavaScript puro mediante Emscripten, con envoltorios generados automáticamente para facilitar su uso en aplicaciones web.

Ver también:

  • Wasmer:

Wasmer es un tiempo de ejecución de código abierto para ejecutar WebAssembly en el servidor. Nuestra misión es hacer que todo el software esté disponible universalmente. Admitimos la ejecución de módulos Wasm de forma independiente en nuestro tiempo de ejecución, pero también se pueden incrustar en varios idiomas utilizando nuestras integraciones de idiomas.

y específicamente Wasmer-JS:

Wasmer-JS permite el uso de módulos WebAssembly compilados del lado del servidor en Node.js y el navegador. El proyecto está configurado como un repositorio único de varios paquetes de JavaScript.

También hay buena información en este artículo sobre Hacker News.

Dado:

  • mem, los WebAssembly.Memory objeto (de las exportaciones del módulo)
  • p, la dirección del primer carácter del string
  • len, la longitud del string (en bytes),

puedes leer el string utilizando:

let str = (new TextDecoder()).decode(new Uint8Array(mem.buffer, p, len));

Esto asume el string está codificado en UTF-8.

Comentarios y puntuaciones

Ten en cuenta recomendar esta noticia si te fue útil.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *