// Copyright 2022 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Flags: --experimental-wasm-stringref d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js"); let kSig_w_ii = makeSig([kWasmI32, kWasmI32], [kWasmStringRef]); function encodeWtf8(str) { // String iterator coalesces surrogate pairs. let out = []; for (let codepoint of str) { codepoint = codepoint.codePointAt(0); if (codepoint <= 0x7f) { out.push(codepoint); } else if (codepoint <= 0x7ff) { out.push(0xc0 | (codepoint >> 6)); out.push(0x80 | (codepoint & 0x3f)); } else if (codepoint <= 0xffff) { out.push(0xe0 | (codepoint >> 12)); out.push(0x80 | ((codepoint >> 6) & 0x3f)); out.push(0x80 | (codepoint & 0x3f)); } else if (codepoint <= 0x10ffff) { out.push(0xf0 | (codepoint >> 18)); out.push(0x80 | ((codepoint >> 12) & 0x3f)); out.push(0x80 | ((codepoint >> 6) & 0x3f)); out.push(0x80 | (codepoint & 0x3f)); } else { throw new Error("bad codepoint " + codepoint); } } return out; } function makeWtf8TestDataSegment() { let data = [] let valid = {}; let invalid = {}; for (let str of ['', 'ascii', 'latin \xa9 1', 'two \ucccc byte', 'surrogate \ud800\udc000 pair', 'isolated \ud800 leading', 'isolated \udc00 trailing']) { let bytes = encodeWtf8(str); valid[str] = { offset: data.length, length: bytes.length }; for (let byte of bytes) { data.push(byte); } } for (let bytes of ['trailing high byte \xa9', 'interstitial high \xa9 byte', 'invalid \xc0 byte', 'surrogate \xed\xa0\x80\xed\xd0\x80 pair']) { invalid[bytes] = { offset: data.length, length: bytes.length }; for (let i = 0; i < bytes.length; i++) { data.push(bytes.charCodeAt(i)); } } return { valid, invalid, data: Uint8Array.from(data) }; }; (function TestStringNewWtf8() { let builder = new WasmModuleBuilder(); builder.addMemory(1, undefined, false, false); let data = makeWtf8TestDataSegment(); builder.addDataSegment(0, data.data); builder.addFunction("string_new_wtf8", kSig_w_ii) .exportAs("string_new_wtf8") .addBody([ kExprLocalGet, 0, kExprLocalGet, 1, kGCPrefix, kExprStringNewWtf8, 0 ]); let instance = builder.instantiate(); for (let [str, {offset, length}] of Object.entries(data.valid)) { assertEquals(str, instance.exports.string_new_wtf8(offset, length)); } for (let [str, {offset, length}] of Object.entries(data.invalid)) { assertThrows(() => instance.exports.string_new_wtf8(offset, length), WebAssembly.RuntimeError, "invalid WTF-8 string"); } })(); function encodeWtf16LE(str) { // String iterator coalesces surrogate pairs. let out = []; for (let i = 0; i < str.length; i++) { codeunit = str.charCodeAt(i); out.push(codeunit & 0xff) out.push(codeunit >> 8); } return out; } function makeWtf16TestDataSegment() { let data = [] let valid = {}; for (let str of ['', 'ascii', 'latin \xa9 1', 'two \ucccc byte', 'surrogate \ud800\udc000 pair', 'isolated \ud800 leading', 'isolated \udc00 trailing']) { valid[str] = { offset: data.length, length: str.length }; for (let byte of encodeWtf16LE(str)) { data.push(byte); } } return { valid, data: Uint8Array.from(data) }; }; (function TestStringNewWtf16() { let builder = new WasmModuleBuilder(); builder.addMemory(1, undefined, false, false); let data = makeWtf16TestDataSegment(); builder.addDataSegment(0, data.data); builder.addFunction("string_new_wtf16", kSig_w_ii) .exportAs("string_new_wtf16") .addBody([ kExprLocalGet, 0, kExprLocalGet, 1, kGCPrefix, kExprStringNewWtf16, 0 ]); let instance = builder.instantiate(); for (let [str, {offset, length}] of Object.entries(data.valid)) { assertEquals(str, instance.exports.string_new_wtf16(offset, length)); } })();