Bug 1612534 - Use 'wat' crate for wasmTextToBinary. r=lth

This commit removes the old WasmTextToBinary implementation and replaces
it with a stub that calls into the rust wat implementation.

The old wasmTextToBinary function took an optional boolean parameter to
indicate that additional metadata was desired. This extra metadata was
a list of offsets of instructions in the code section. I'm not sure if
this was intentional or not, but it looks like it only includes root
expressions of nested s-exprs.

These offsets were used in two tests, debug/wasm-breakpoints.js and a
wasm/regress test. Adding this functionality into 'wat' proved to be a large
change for a function that didn't seem to have much use cases. A new
wasmCodeOffsets function was added to replicate this feature. It's implemented
in rust using the 'wasmparser' crate that cranelift uses. When cranelift is
compiled, this shouldn't result in an increased binary size. If the compile
time or binary size proves to be an issue in non-cranelift builds, I think
we can make this a conditional feature for JS-shells only.

Differential Revision: https://phabricator.services.mozilla.com/D67245

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Ryan Hunt 2020-03-23 16:35:51 +00:00
parent ed65822beb
commit 5e0c6d3046
12 changed files with 271 additions and 9179 deletions

37
Cargo.lock generated
View File

@ -2127,6 +2127,7 @@ dependencies = [
"mozglue-static",
"mozilla-central-workspace-hack",
"smoosh",
"wasm-rust",
]
[[package]]
@ -2178,6 +2179,12 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f"
[[package]]
name = "leb128"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3576a87f2ba00f6f106fdfcd16db1d698d648a26ad8e0573cad8537c3c362d2a"
[[package]]
name = "libc"
version = "0.2.59"
@ -4668,9 +4675,9 @@ checksum = "aa6024fc12ddfd1c6dbc14a80fa2324d4568849869b779f6bd37e5e4c03344d1"
[[package]]
name = "unicode-width"
version = "0.1.4"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f"
checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
[[package]]
name = "unicode-xid"
@ -4793,12 +4800,38 @@ dependencies = [
"urlencoding",
]
[[package]]
name = "wasm-rust"
version = "0.1.0"
dependencies = [
"wasmparser",
"wat",
]
[[package]]
name = "wasmparser"
version = "0.48.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "073da89bf1c84db000dd68ce660c1b4a08e3a2d28fd1e3394ab9e7abdde4a0f8"
[[package]]
name = "wast"
version = "11.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df4d67ba9266f4fcaf2e8a1afadc5e2a959e51aecc07b1ecbdf85a6ddaf08bde"
dependencies = [
"leb128",
]
[[package]]
name = "wat"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a9400dc1c8512087b2d974b1b9b0a6c4e6e26e7e8acf629e3e351165a1ed301"
dependencies = [
"wast",
]
[[package]]
name = "webdriver"
version = "0.40.2"

View File

@ -91,7 +91,7 @@
#include "wasm/WasmJS.h"
#include "wasm/WasmModule.h"
#include "wasm/WasmSignalHandlers.h"
#include "wasm/WasmTextToBinary.h"
#include "wasm/WasmTesting.h"
#include "wasm/WasmTypes.h"
#include "debugger/DebugAPI-inl.h"
@ -917,26 +917,12 @@ static bool WasmTextToBinary(JSContext* cx, unsigned argc, Value* vp) {
return false;
}
bool withOffsets = false;
if (args.hasDefined(1)) {
if (!args[1].isBoolean()) {
ReportUsageErrorASCII(cx, callee,
"Second argument, if present, must be a boolean");
return false;
}
withOffsets = ToBoolean(args[1]);
}
uintptr_t stackLimit = GetNativeStackLimit(cx);
wasm::Bytes bytes;
UniqueChars error;
wasm::Uint32Vector offsets;
if (!wasm::TextToBinary(twoByteChars.twoByteChars(), textLen, stackLimit,
&bytes, &offsets, &error)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_WASM_TEXT_FAIL,
error.get() ? error.get() : "out of memory");
if (!wasm::TextToBinary(twoByteChars.twoByteChars(), textLen, &bytes,
&error)) {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_TEXT_FAIL,
error.get() ? error.get() : "out of memory");
return false;
}
@ -948,21 +934,38 @@ static bool WasmTextToBinary(JSContext* cx, unsigned argc, Value* vp) {
memcpy(binary->as<TypedArrayObject>().dataPointerUnshared(), bytes.begin(),
bytes.length());
if (!withOffsets) {
args.rval().setObject(*binary);
return true;
}
args.rval().setObject(*binary);
return true;
}
RootedObject obj(cx, JS_NewPlainObject(cx));
if (!obj) {
static bool WasmCodeOffsets(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
RootedObject callee(cx, &args.callee());
if (!args.requireAtLeast(cx, "wasmCodeOffsets", 1)) {
return false;
}
constexpr unsigned propAttrs = JSPROP_ENUMERATE;
if (!JS_DefineProperty(cx, obj, "binary", binary, propAttrs)) {
if (!args.get(0).isObject()) {
JS_ReportErrorASCII(cx, "argument is not an object");
return false;
}
SharedMem<uint8_t*> bytes;
size_t byteLength;
JSObject* bufferObject = &args[0].toObject();
JSObject* unwrappedBufferObject = CheckedUnwrapStatic(bufferObject);
if (!unwrappedBufferObject ||
!IsBufferSource(unwrappedBufferObject, &bytes, &byteLength)) {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
JSMSG_WASM_BAD_BUF_ARG);
return false;
}
wasm::Uint32Vector offsets;
wasm::CodeOffsets(bytes.unwrap(), byteLength, &offsets);
RootedObject jsOffsets(cx, JS::NewArrayObject(cx, offsets.length()));
if (!jsOffsets) {
return false;
@ -974,11 +977,7 @@ static bool WasmTextToBinary(JSContext* cx, unsigned argc, Value* vp) {
return false;
}
}
if (!JS_DefineProperty(cx, obj, "offsets", jsOffsets, propAttrs)) {
return false;
}
args.rval().setObject(*obj);
args.rval().setObject(*jsOffsets);
return true;
}
@ -6715,6 +6714,11 @@ gc::ZealModeHelpText),
"wasmTextToBinary(str)",
" Translates the given text wasm module into its binary encoding."),
JS_FN_HELP("wasmCodeOffsets", WasmCodeOffsets, 1, 0,
"wasmCodeOffsets(binary)",
" Decodes the given wasm binary to find the offsets of every instruction in the"
" code section."),
JS_FN_HELP("wasmExtractCode", WasmExtractCode, 1, 0,
"wasmExtractCode(module[, tier])",
" Extracts generated machine code from WebAssembly.Module. The tier is a string,\n"

View File

@ -65,9 +65,9 @@ const unusedValuesError = /unused values not explicitly dropped by end of block/
function jsify(wasmVal) {
if (wasmVal === 'nan')
return NaN;
if (wasmVal === 'infinity')
if (wasmVal === 'inf')
return Infinity;
if (wasmVal === '-infinity')
if (wasmVal === '-inf')
return Infinity;
if (wasmVal === '-0')
return -0;

View File

@ -13,6 +13,7 @@ baldrdash = { path = "../../wasm/cranelift", optional = true }
encoding_c = "0.9.5"
encoding_c_mem = "0.2.4"
smoosh = { path = "../../frontend/smoosh", optional = true }
wasm-rust = { path = "../../wasm/rust" }
mozilla-central-workspace-hack = { path = "../../../../build/workspace-hack" }
mozglue-static = { path = "../../../../mozglue/static/rust" }

View File

@ -21,3 +21,5 @@ extern crate mozglue_static;
#[cfg(feature = "smoosh")]
extern crate smoosh;
extern crate wasm_rust;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,75 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
*
* Copyright 2015 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "wasm/WasmTesting.h"
#include <inttypes.h>
#include <stdbool.h>
#include <stddef.h>
#include "wasm/WasmTypes.h"
using namespace js;
using namespace js::wasm;
extern "C" {
bool wasm_text_to_binary(const char16_t* text, size_t text_len,
uint8_t** out_bytes, size_t* out_bytes_len,
uint8_t** out_error, size_t* out_error_len);
void wasm_code_offsets(const uint8_t* bytes, size_t bytes_len,
uint32_t** out_offsets, size_t* out_offset_len);
} // extern "C"
bool wasm::TextToBinary(const char16_t* text, size_t textLen, Bytes* bytes,
UniqueChars* error) {
uint8_t* outBytes = nullptr;
size_t outBytesLength = 0;
uint8_t* outError = nullptr;
size_t outErrorLength = 0;
bool result = wasm_text_to_binary(text, textLen, &outBytes, &outBytesLength,
&outError, &outErrorLength);
if (result) {
MOZ_ASSERT(outBytes);
MOZ_ASSERT(outBytesLength > 0);
bytes->replaceRawBuffer(outBytes, outBytesLength);
return true;
}
MOZ_ASSERT(outError);
MOZ_ASSERT(outErrorLength > 0);
*error = UniqueChars{(char*)outError};
return false;
}
void wasm::CodeOffsets(const uint8_t* bytes, size_t bytesLen,
Uint32Vector* offsets) {
uint32_t* outOffsets = nullptr;
size_t outOffsetsLength = 0;
wasm_code_offsets(bytes, bytesLen, &outOffsets, &outOffsetsLength);
if (outOffsets) {
MOZ_ASSERT(outOffsetsLength > 0);
offsets->replaceRawBuffer(outOffsets, outOffsetsLength);
} else {
offsets->clear();
}
}

View File

@ -1,7 +1,7 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
*
* Copyright 2015 Mozilla Foundation
* Copyright 2020 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,8 +16,8 @@
* limitations under the License.
*/
#ifndef wasm_text_to_binary_h
#define wasm_text_to_binary_h
#ifndef wasm_testing_h
#define wasm_testing_h
#include "wasm/WasmTypes.h"
@ -29,11 +29,18 @@ namespace wasm {
// other than out-of-memory an error message string will be stored in 'error'.
extern MOZ_MUST_USE bool TextToBinary(const char16_t* text, size_t textLen,
uintptr_t stackLimit, Bytes* bytes,
Uint32Vector* offsets,
UniqueChars* error);
Bytes* bytes, UniqueChars* error);
// Decode the binary wasm module given and return the offsets of all
// instructions inside of the the code section.
//
// This function is used exclusively for testing and handles errors by
// returning an empty offset array.
extern void CodeOffsets(const uint8_t* bytes, size_t bytesLen,
Uint32Vector* offsets);
} // namespace wasm
} // namespace js
#endif // wasm_text_to_binary_h
#endif // wasm_testing_h

File diff suppressed because it is too large Load Diff

View File

@ -40,7 +40,7 @@ UNIFIED_SOURCES += [
'WasmSignalHandlers.cpp',
'WasmStubs.cpp',
'WasmTable.cpp',
'WasmTextToBinary.cpp',
'WasmTesting.cpp',
'WasmTypes.cpp',
'WasmValidate.cpp'
]

View File

@ -0,0 +1,13 @@
[package]
name = "wasm-rust"
version = "0.1.0"
authors = ["The Spidermonkey developers"]
edition = "2018"
[lib]
crate-type = ["rlib"]
name = "wasm_rust"
[dependencies]
wat = { version = "1.0.12" }
wasmparser = { version = "0.48.2" }

View File

@ -0,0 +1,91 @@
/* Copyright 2020 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#[no_mangle]
pub unsafe extern "C" fn wasm_text_to_binary(
text: *const u16,
text_len: usize,
out_bytes: *mut *mut u8,
out_bytes_len: *mut usize,
out_error: *mut *mut u8,
out_error_len: *mut usize,
) -> bool {
let text_slice = std::slice::from_raw_parts(text, text_len);
let text = String::from_utf16_lossy(text_slice);
match wat::parse_str(&text) {
Ok(bytes) => {
let bytes_box = bytes.into_boxed_slice();
let bytes_slice = Box::leak(bytes_box);
out_bytes.write(bytes_slice.as_mut_ptr());
out_bytes_len.write(bytes_slice.len());
true
}
Err(error) => {
let error = Box::leak(format!("{}\0", error).into_boxed_str());
out_error.write(error.as_mut_ptr());
out_error_len.write(error.len());
false
}
}
}
#[no_mangle]
pub unsafe extern "C" fn wasm_code_offsets(
bytes: *const u8,
bytes_len: usize,
out_offsets: *mut *mut u32,
out_offsets_len: *mut usize,
) {
fn code_offsets(bytes: &[u8]) -> Vec<u32> {
use wasmparser::*;
if bytes.is_empty() {
return Vec::new();
}
let mut offsets = Vec::new();
let mut parser = Parser::new(bytes);
let mut next_input = ParserInput::Default;
while !parser.eof() {
let offset = parser.current_position();
match parser.read_with_input(next_input) {
ParserState::BeginSection { code, .. } if *code != SectionCode::Code => {
next_input = ParserInput::SkipSection;
}
ParserState::CodeOperator(..) => {
offsets.push(offset as u32);
next_input = ParserInput::Default
}
_ => next_input = ParserInput::Default,
}
}
offsets
}
let bytes = std::slice::from_raw_parts(bytes, bytes_len);
let offsets = code_offsets(bytes);
if offsets.len() == 0 {
out_offsets.write(std::ptr::null_mut());
out_offsets_len.write(0);
} else {
let offsets_box = offsets.into_boxed_slice();
let offsets_slice = Box::leak(offsets_box);
out_offsets.write(offsets_slice.as_mut_ptr());
out_offsets_len.write(offsets_slice.len());
}
}