[stringrefs] Implement stringview_iter.slice
Feature complete! Bug: v8:12868 Change-Id: I7727071bdd062a6dae26206a65080f675ef7ee93 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3758226 Reviewed-by: Jakob Kummerow <jkummerow@chromium.org> Commit-Queue: Andy Wingo <wingo@igalia.com> Cr-Commit-Position: refs/heads/main@{#81694}
This commit is contained in:
parent
77425d0a3e
commit
74289c46bb
@ -1133,4 +1133,25 @@ builtin WasmStringViewIterRewind(
|
|||||||
view.offset = offset;
|
view.offset = offset;
|
||||||
return rewound;
|
return rewound;
|
||||||
}
|
}
|
||||||
|
builtin WasmStringViewIterSlice(
|
||||||
|
view: WasmStringViewIter, codepoints: uint32): String {
|
||||||
|
const string = view.string;
|
||||||
|
const start = view.offset;
|
||||||
|
let end = view.offset;
|
||||||
|
let advanced: uint32 = 0;
|
||||||
|
while (advanced < codepoints) {
|
||||||
|
if (end == Unsigned(string.length)) break;
|
||||||
|
advanced = advanced + 1;
|
||||||
|
if (end + 1 < Unsigned(string.length) &&
|
||||||
|
IsLeadSurrogate(StringCharCodeAt(string, Convert<uintptr>(end))) &&
|
||||||
|
IsTrailSurrogate(StringCharCodeAt(string, Convert<uintptr>(end + 1)))) {
|
||||||
|
end = end + 2;
|
||||||
|
} else {
|
||||||
|
end = end + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (start == end) ?
|
||||||
|
kEmptyString :
|
||||||
|
string::SubString(string, Convert<uintptr>(start), Convert<uintptr>(end));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6007,6 +6007,15 @@ Node* WasmGraphBuilder::StringViewIterRewind(Node* view,
|
|||||||
Operator::kNoDeopt, view, codepoints);
|
Operator::kNoDeopt, view, codepoints);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Node* WasmGraphBuilder::StringViewIterSlice(Node* view, CheckForNull null_check,
|
||||||
|
Node* codepoints,
|
||||||
|
wasm::WasmCodePosition position) {
|
||||||
|
if (null_check == kWithNullCheck) view = AssertNotNull(view, position);
|
||||||
|
|
||||||
|
return gasm_->CallBuiltin(Builtin::kWasmStringViewIterSlice,
|
||||||
|
Operator::kNoDeopt, view, codepoints);
|
||||||
|
}
|
||||||
|
|
||||||
// 1 bit V8 Smi tag, 31 bits V8 Smi shift, 1 bit i31ref high-bit truncation.
|
// 1 bit V8 Smi tag, 31 bits V8 Smi shift, 1 bit i31ref high-bit truncation.
|
||||||
constexpr int kI31To32BitSmiShift = 33;
|
constexpr int kI31To32BitSmiShift = 33;
|
||||||
|
|
||||||
|
@ -596,6 +596,8 @@ class WasmGraphBuilder {
|
|||||||
wasm::WasmCodePosition position);
|
wasm::WasmCodePosition position);
|
||||||
Node* StringViewIterRewind(Node* view, CheckForNull null_check,
|
Node* StringViewIterRewind(Node* view, CheckForNull null_check,
|
||||||
Node* codepoints, wasm::WasmCodePosition position);
|
Node* codepoints, wasm::WasmCodePosition position);
|
||||||
|
Node* StringViewIterSlice(Node* view, CheckForNull null_check,
|
||||||
|
Node* codepoints, wasm::WasmCodePosition position);
|
||||||
Node* IsNull(Node* object);
|
Node* IsNull(Node* object);
|
||||||
Node* TypeGuard(Node* value, wasm::ValueType type);
|
Node* TypeGuard(Node* value, wasm::ValueType type);
|
||||||
|
|
||||||
|
@ -6918,7 +6918,27 @@ class LiftoffCompiler {
|
|||||||
|
|
||||||
void StringViewIterSlice(FullDecoder* decoder, const Value& view,
|
void StringViewIterSlice(FullDecoder* decoder, const Value& view,
|
||||||
const Value& codepoints, Value* result) {
|
const Value& codepoints, Value* result) {
|
||||||
UNIMPLEMENTED();
|
LiftoffRegList pinned;
|
||||||
|
|
||||||
|
LiftoffAssembler::VarState& codepoints_var =
|
||||||
|
__ cache_state()->stack_state.end()[-1];
|
||||||
|
|
||||||
|
LiftoffRegister view_reg = pinned.set(
|
||||||
|
__ LoadToRegister(__ cache_state()->stack_state.end()[-2], pinned));
|
||||||
|
MaybeEmitNullCheck(decoder, view_reg.gp(), pinned, view.type);
|
||||||
|
LiftoffAssembler::VarState view_var(kRef, view_reg, 0);
|
||||||
|
|
||||||
|
CallRuntimeStub(WasmCode::kWasmStringViewIterSlice,
|
||||||
|
MakeSig::Returns(kRef).Params(kRef, kI32),
|
||||||
|
{
|
||||||
|
view_var,
|
||||||
|
codepoints_var,
|
||||||
|
},
|
||||||
|
decoder->position());
|
||||||
|
RegisterDebugSideTableEntry(decoder, DebugSideTableBuilder::kDidSpill);
|
||||||
|
|
||||||
|
LiftoffRegister result_reg(kReturnRegister0);
|
||||||
|
__ PushRegister(kRef, result_reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Forward(FullDecoder* decoder, const Value& from, Value* to) {
|
void Forward(FullDecoder* decoder, const Value& from, Value* to) {
|
||||||
|
@ -1575,7 +1575,9 @@ class WasmGraphBuildingInterface {
|
|||||||
|
|
||||||
void StringViewIterSlice(FullDecoder* decoder, const Value& view,
|
void StringViewIterSlice(FullDecoder* decoder, const Value& view,
|
||||||
const Value& codepoints, Value* result) {
|
const Value& codepoints, Value* result) {
|
||||||
UNIMPLEMENTED();
|
SetAndTypeNode(result, builder_->StringViewIterSlice(
|
||||||
|
view.node, NullCheckFor(view.type),
|
||||||
|
codepoints.node, decoder->position()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Forward(FullDecoder* decoder, const Value& from, Value* to) {
|
void Forward(FullDecoder* decoder, const Value& from, Value* to) {
|
||||||
|
@ -147,7 +147,8 @@ struct WasmModule;
|
|||||||
V(WasmStringAsIter) \
|
V(WasmStringAsIter) \
|
||||||
V(WasmStringViewIterNext) \
|
V(WasmStringViewIterNext) \
|
||||||
V(WasmStringViewIterAdvance) \
|
V(WasmStringViewIterAdvance) \
|
||||||
V(WasmStringViewIterRewind)
|
V(WasmStringViewIterRewind) \
|
||||||
|
V(WasmStringViewIterSlice)
|
||||||
|
|
||||||
// Sorted, disjoint and non-overlapping memory regions. A region is of the
|
// Sorted, disjoint and non-overlapping memory regions. A region is of the
|
||||||
// form [start, end). So there's no [start, end), [end, other_end),
|
// form [start, end). So there's no [start, end), [end, other_end),
|
||||||
|
@ -17,6 +17,7 @@ let kSig_i_wiii = makeSig([kWasmStringRef, kWasmI32, kWasmI32, kWasmI32],
|
|||||||
let kSig_ii_wiii = makeSig([kWasmStringRef, kWasmI32, kWasmI32, kWasmI32],
|
let kSig_ii_wiii = makeSig([kWasmStringRef, kWasmI32, kWasmI32, kWasmI32],
|
||||||
[kWasmI32, kWasmI32]);
|
[kWasmI32, kWasmI32]);
|
||||||
let kSig_v_w = makeSig([kWasmStringRef], []);
|
let kSig_v_w = makeSig([kWasmStringRef], []);
|
||||||
|
let kSig_w_i = makeSig([kWasmI32], [kWasmStringRef]);
|
||||||
let kSig_w_wii = makeSig([kWasmStringRef, kWasmI32, kWasmI32],
|
let kSig_w_wii = makeSig([kWasmStringRef, kWasmI32, kWasmI32],
|
||||||
[kWasmStringRef]);
|
[kWasmStringRef]);
|
||||||
let kSig_w_ww = makeSig([kWasmStringRef, kWasmStringRef], [kWasmStringRef]);
|
let kSig_w_ww = makeSig([kWasmStringRef, kWasmStringRef], [kWasmStringRef]);
|
||||||
@ -1101,6 +1102,22 @@ function makeWtf16TestDataSegment() {
|
|||||||
kGCPrefix, kExprStringViewIterRewind
|
kGCPrefix, kExprStringViewIterRewind
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
builder.addFunction("slice", kSig_w_i)
|
||||||
|
.exportFunc()
|
||||||
|
.addBody([
|
||||||
|
kExprGlobalGet, global.index,
|
||||||
|
kExprLocalGet, 0,
|
||||||
|
kGCPrefix, kExprStringViewIterSlice
|
||||||
|
]);
|
||||||
|
|
||||||
|
builder.addFunction("slice_null", kSig_w_i)
|
||||||
|
.exportFunc()
|
||||||
|
.addBody([
|
||||||
|
kExprRefNull, kStringViewIterCode,
|
||||||
|
kExprI32Const, 0,
|
||||||
|
kGCPrefix, kExprStringViewIterSlice
|
||||||
|
]);
|
||||||
|
|
||||||
let instance = builder.instantiate();
|
let instance = builder.instantiate();
|
||||||
|
|
||||||
for (let str of interestingStrings) {
|
for (let str of interestingStrings) {
|
||||||
@ -1131,6 +1148,22 @@ function makeWtf16TestDataSegment() {
|
|||||||
assertEquals(0, instance.exports.rewind(-1));
|
assertEquals(0, instance.exports.rewind(-1));
|
||||||
assertEquals(codepoints.length, instance.exports.advance(-1));
|
assertEquals(codepoints.length, instance.exports.advance(-1));
|
||||||
assertEquals(0, instance.exports.advance(-1));
|
assertEquals(0, instance.exports.advance(-1));
|
||||||
|
|
||||||
|
for (let start = 0; start <= codepoints.length; start++) {
|
||||||
|
for (let end = start; end <= codepoints.length; end++) {
|
||||||
|
let expected_slice =
|
||||||
|
String.fromCodePoint(...codepoints.slice(start, end));
|
||||||
|
instance.exports.iterate(str);
|
||||||
|
assertEquals(start, instance.exports.advance(start));
|
||||||
|
assertEquals(expected_slice, instance.exports.slice(end - start));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
instance.exports.iterate(str);
|
||||||
|
assertEquals(str, instance.exports.slice(codepoints.length));
|
||||||
|
assertEquals(str, instance.exports.slice(-1));
|
||||||
|
assertEquals("", instance.exports.slice(0));
|
||||||
|
assertEquals(codepoints.length, instance.exports.advance(-1));
|
||||||
|
assertEquals("", instance.exports.slice(-1));
|
||||||
}
|
}
|
||||||
|
|
||||||
assertThrows(() => instance.exports.iterate_null(),
|
assertThrows(() => instance.exports.iterate_null(),
|
||||||
@ -1141,4 +1174,6 @@ function makeWtf16TestDataSegment() {
|
|||||||
WebAssembly.RuntimeError, "dereferencing a null pointer");
|
WebAssembly.RuntimeError, "dereferencing a null pointer");
|
||||||
assertThrows(() => instance.exports.rewind_null(),
|
assertThrows(() => instance.exports.rewind_null(),
|
||||||
WebAssembly.RuntimeError, "dereferencing a null pointer");
|
WebAssembly.RuntimeError, "dereferencing a null pointer");
|
||||||
|
assertThrows(() => instance.exports.slice_null(),
|
||||||
|
WebAssembly.RuntimeError, "dereferencing a null pointer");
|
||||||
})();
|
})();
|
||||||
|
Loading…
Reference in New Issue
Block a user