[stringrefs] Implement string.encode_wtf16_array
Bug: v8:12868 Change-Id: I4229cefc4dfdb29214712aeef18841092cdf9e87 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3748653 Commit-Queue: Andy Wingo <wingo@igalia.com> Reviewed-by: Jakob Kummerow <jkummerow@chromium.org> Cr-Commit-Position: refs/heads/main@{#81600}
This commit is contained in:
parent
2b2ce1d8be
commit
9662376ad9
@ -881,6 +881,40 @@ builtin WasmStringEncodeWtf16(
|
||||
WasmUint32ToNumber(offset), SmiConstant(0), SmiFromInt32(string.length));
|
||||
return Unsigned(string.length);
|
||||
}
|
||||
builtin WasmStringEncodeWtf16Array(
|
||||
string: String, array: WasmArray, start: uint32): uint32 {
|
||||
try {
|
||||
if (start > array.length) goto OffsetOutOfRange;
|
||||
if (array.length - start < Unsigned(string.length)) goto OffsetOutOfRange;
|
||||
|
||||
const byteOffset: intptr = kWasmArrayHeaderSize +
|
||||
torque_internal::TimesSizeOf<char16>(Convert<intptr>(start));
|
||||
const arrayContent = torque_internal::unsafe::NewMutableSlice<char16>(
|
||||
array, byteOffset, Convert<intptr>(string.length));
|
||||
try {
|
||||
StringToSlice(string) otherwise OneByte, TwoByte;
|
||||
} label OneByte(slice: ConstSlice<char8>) {
|
||||
let fromIt = slice.Iterator();
|
||||
let toIt = arrayContent.Iterator();
|
||||
while (true) {
|
||||
let toRef = toIt.NextReference() otherwise break;
|
||||
*toRef = %RawDownCast<char16>(Convert<uint16>(fromIt.NextNotEmpty()));
|
||||
}
|
||||
} label TwoByte(slice: ConstSlice<char16>) {
|
||||
let fromIt = slice.Iterator();
|
||||
let toIt = arrayContent.Iterator();
|
||||
while (true) {
|
||||
let toRef = toIt.NextReference() otherwise break;
|
||||
*toRef = fromIt.NextNotEmpty();
|
||||
}
|
||||
}
|
||||
return Unsigned(string.length);
|
||||
} label OffsetOutOfRange deferred {
|
||||
const error = MessageTemplate::kWasmTrapArrayOutOfBounds;
|
||||
runtime::ThrowWasmError(LoadContextFromFrame(), SmiConstant(error));
|
||||
unreachable;
|
||||
}
|
||||
}
|
||||
builtin WasmStringConcat(a: String, b: String): String {
|
||||
const context = LoadContextFromFrame();
|
||||
return a + b;
|
||||
|
@ -5853,6 +5853,20 @@ Node* WasmGraphBuilder::StringEncodeWtf16(uint32_t memory, Node* string,
|
||||
string, offset, gasm_->SmiConstant(memory));
|
||||
}
|
||||
|
||||
Node* WasmGraphBuilder::StringEncodeWtf16Array(
|
||||
Node* string, CheckForNull string_null_check, Node* array,
|
||||
CheckForNull array_null_check, Node* start,
|
||||
wasm::WasmCodePosition position) {
|
||||
if (string_null_check == kWithNullCheck) {
|
||||
string = AssertNotNull(string, position);
|
||||
}
|
||||
if (array_null_check == kWithNullCheck) {
|
||||
array = AssertNotNull(array, position);
|
||||
}
|
||||
return gasm_->CallBuiltin(Builtin::kWasmStringEncodeWtf16Array,
|
||||
Operator::kNoDeopt, string, array, start);
|
||||
}
|
||||
|
||||
Node* WasmGraphBuilder::StringConcat(Node* head, CheckForNull head_null_check,
|
||||
Node* tail, CheckForNull tail_null_check,
|
||||
wasm::WasmCodePosition position) {
|
||||
|
@ -558,6 +558,9 @@ class WasmGraphBuilder {
|
||||
Node* StringEncodeWtf16(uint32_t memory, Node* string,
|
||||
CheckForNull null_check, Node* offset,
|
||||
wasm::WasmCodePosition position);
|
||||
Node* StringEncodeWtf16Array(Node* string, CheckForNull string_null_check,
|
||||
Node* array, CheckForNull array_null_check,
|
||||
Node* start, wasm::WasmCodePosition position);
|
||||
Node* StringConcat(Node* head, CheckForNull head_null_check, Node* tail,
|
||||
CheckForNull tail_null_check,
|
||||
wasm::WasmCodePosition position);
|
||||
|
@ -6472,7 +6472,34 @@ class LiftoffCompiler {
|
||||
void StringEncodeWtf16Array(FullDecoder* decoder, const Value& str,
|
||||
const Value& array, const Value& start,
|
||||
Value* result) {
|
||||
UNIMPLEMENTED();
|
||||
LiftoffRegList pinned;
|
||||
|
||||
LiftoffRegister array_reg = pinned.set(
|
||||
__ LoadToRegister(__ cache_state()->stack_state.end()[-2], pinned));
|
||||
MaybeEmitNullCheck(decoder, array_reg.gp(), pinned, array.type);
|
||||
LiftoffAssembler::VarState array_var(kRef, array_reg, 0);
|
||||
|
||||
LiftoffRegister string_reg = pinned.set(
|
||||
__ LoadToRegister(__ cache_state()->stack_state.end()[-3], pinned));
|
||||
MaybeEmitNullCheck(decoder, string_reg.gp(), pinned, str.type);
|
||||
LiftoffAssembler::VarState string_var(kRef, string_reg, 0);
|
||||
|
||||
LiftoffAssembler::VarState& start_var =
|
||||
__ cache_state()->stack_state.end()[-1];
|
||||
|
||||
CallRuntimeStub(WasmCode::kWasmStringEncodeWtf16Array,
|
||||
MakeSig::Returns(kI32).Params(kRef, kRef, kI32),
|
||||
{
|
||||
string_var,
|
||||
array_var,
|
||||
start_var,
|
||||
},
|
||||
decoder->position());
|
||||
__ DropValues(3);
|
||||
RegisterDebugSideTableEntry(decoder, DebugSideTableBuilder::kDidSpill);
|
||||
|
||||
LiftoffRegister result_reg(kReturnRegister0);
|
||||
__ PushRegister(kI32, result_reg);
|
||||
}
|
||||
|
||||
void StringConcat(FullDecoder* decoder, const Value& head, const Value& tail,
|
||||
|
@ -1465,7 +1465,9 @@ class WasmGraphBuildingInterface {
|
||||
void StringEncodeWtf16Array(FullDecoder* decoder, const Value& str,
|
||||
const Value& array, const Value& start,
|
||||
Value* result) {
|
||||
UNIMPLEMENTED();
|
||||
result->node = builder_->StringEncodeWtf16Array(
|
||||
str.node, NullCheckFor(str.type), array.node, NullCheckFor(array.type),
|
||||
start.node, decoder->position());
|
||||
}
|
||||
|
||||
void StringConcat(FullDecoder* decoder, const Value& head, const Value& tail,
|
||||
|
@ -138,7 +138,8 @@ struct WasmModule;
|
||||
V(WasmStringViewWtf16Slice) \
|
||||
V(WasmStringNewWtf8Array) \
|
||||
V(WasmStringNewWtf16Array) \
|
||||
V(WasmStringEncodeWtf8Array)
|
||||
V(WasmStringEncodeWtf8Array) \
|
||||
V(WasmStringEncodeWtf16Array)
|
||||
|
||||
// Sorted, disjoint and non-overlapping memory regions. A region is of the
|
||||
// form [start, end). So there's no [start, end), [end, other_end),
|
||||
|
@ -362,3 +362,74 @@ function makeWtf16TestDataSegment() {
|
||||
WebAssembly.RuntimeError, message);
|
||||
}
|
||||
})();
|
||||
|
||||
(function TestStringEncodeWtf16Array() {
|
||||
let builder = new WasmModuleBuilder();
|
||||
|
||||
let i16_array = builder.addArray(kWasmI16, true);
|
||||
|
||||
let kSig_w_wii =
|
||||
makeSig([kWasmStringRef, kWasmI32, kWasmI32],
|
||||
[kWasmStringRef]);
|
||||
// Allocate an array and encode into it. Then decode it.
|
||||
// (str, length, offset=0) -> str
|
||||
builder.addFunction("encode", kSig_w_wii)
|
||||
.exportFunc()
|
||||
.addLocals(wasmRefNullType(i16_array), 1)
|
||||
.addLocals(kWasmI32, 1)
|
||||
.addBody([
|
||||
// Allocate buffer.
|
||||
kExprLocalGet, 1,
|
||||
kGCPrefix, kExprArrayNewDefault, i16_array,
|
||||
kExprLocalSet, 3,
|
||||
|
||||
// Write buffer, store number of code units written.
|
||||
kExprLocalGet, 0,
|
||||
kExprLocalGet, 3,
|
||||
kExprLocalGet, 2,
|
||||
kGCPrefix, kExprStringEncodeWtf16Array,
|
||||
kExprLocalSet, 4,
|
||||
|
||||
// Read buffer.
|
||||
kExprLocalGet, 3,
|
||||
kExprLocalGet, 2,
|
||||
kExprLocalGet, 2, kExprLocalGet, 4, kExprI32Add,
|
||||
kGCPrefix, kExprStringNewWtf16Array,
|
||||
]);
|
||||
|
||||
builder.addFunction("encode_null_string", kSig_i_v)
|
||||
.exportFunc()
|
||||
.addBody([
|
||||
kExprRefNull, kStringRefCode,
|
||||
kExprI32Const, 0, kGCPrefix, kExprArrayNewDefault, i16_array,
|
||||
kExprI32Const, 0,
|
||||
kGCPrefix, kExprStringEncodeWtf16Array
|
||||
]);
|
||||
builder.addFunction("encode_null_array", kSig_i_v)
|
||||
.exportFunc()
|
||||
.addBody([
|
||||
kExprI32Const, 0, kGCPrefix, kExprArrayNewDefault, i16_array,
|
||||
kExprI32Const, 0, kExprI32Const, 0,
|
||||
kGCPrefix, kExprStringNewWtf16Array,
|
||||
kExprRefNull, i16_array,
|
||||
kExprI32Const, 0,
|
||||
kGCPrefix, kExprStringEncodeWtf16Array
|
||||
]);
|
||||
|
||||
let instance = builder.instantiate();
|
||||
for (let str of interestingStrings) {
|
||||
assertEquals(str, instance.exports.encode(str, str.length, 0));
|
||||
assertEquals(str, instance.exports.encode(str, str.length + 20, 10));
|
||||
}
|
||||
|
||||
assertThrows(() => instance.exports.encode_null_array(),
|
||||
WebAssembly.RuntimeError, "dereferencing a null pointer");
|
||||
assertThrows(() => instance.exports.encode_null_string(),
|
||||
WebAssembly.RuntimeError, "dereferencing a null pointer");
|
||||
|
||||
for (let str of interestingStrings) {
|
||||
let message = "array element access out of bounds";
|
||||
assertThrows(() => instance.exports.encode(str, str.length, 1),
|
||||
WebAssembly.RuntimeError, message);
|
||||
}
|
||||
})();
|
||||
|
Loading…
Reference in New Issue
Block a user