diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 79091199fd..0bb81e6ffc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -81,9 +81,9 @@ jobs: run: npm run build - name: Test experimental features env: - ASC_FEATURES: threads,reference-types,gc,exception-handling + ASC_FEATURES: threads,reference-types,gc,exception-handling,stringref run: | - npm run test:compiler features/threads features/reference-types features/gc features/exception-handling + npm run test:compiler features/threads features/reference-types features/gc features/exception-handling features/stringref runtimes: name: "Runtimes" runs-on: ubuntu-latest diff --git a/src/builtins.ts b/src/builtins.ts index 492b186600..14b620a39c 100644 --- a/src/builtins.ts +++ b/src/builtins.ts @@ -715,6 +715,48 @@ export namespace BuiltinNames { export const i31_new = "~lib/builtins/i31.new"; export const i31_get = "~lib/builtins/i31.get"; + export const string_new_utf8 = "~lib/builtins/string.new_utf8"; + export const string_new_utf8_array = "~lib/builtins/string.new_utf8_array"; + export const string_new_lossy_utf8 = "~lib/builtins/string.new_lossy_utf8"; + export const string_new_lossy_utf8_array = "~lib/builtins/string.new_lossy_utf8_array"; + export const string_new_wtf8 = "~lib/builtins/string.new_wtf8"; + export const string_new_wtf8_array = "~lib/builtins/string.new_wtf8_array"; + export const string_new_wtf16 = "~lib/builtins/string.new_wtf16"; + export const string_new_wtf16_array = "~lib/builtins/string.new_wtf16_array"; + export const string_from_code_point = "~lib/builtins/string.from_code_point"; + export const string_hash = "~lib/builtins/string.hash"; + export const string_measure_utf8 = "~lib/builtins/string.measure_utf8"; + export const string_measure_wtf8 = "~lib/builtins/string.measure_wtf8"; + export const string_measure_wtf16 = "~lib/builtins/string.measure_wtf16"; + export const string_is_usv_sequence = "~lib/builtins/string.is_usv_sequence"; + export const string_encode_utf8 = "~lib/builtins/string.encode_utf8"; + export const string_encode_utf8_array = "~lib/builtins/string.encode_utf8_array"; + // TODO: string_encode_lossy_utf8 + // TODO: string_encode_lossy_utf8_array + export const string_encode_wtf8 = "~lib/builtins/string.encode_wtf8"; + export const string_encode_wtf8_array = "~lib/builtins/string.encode_wtf8_array"; + export const string_encode_wtf16 = "~lib/builtins/string.encode_wtf16"; + export const string_encode_wtf16_array = "~lib/builtins/string.encode_wtf16_array"; + export const string_concat = "~lib/builtins/string.concat"; + export const string_eq = "~lib/builtins/string.eq"; + export const string_compare = "~lib/builtins/string.compare"; + export const string_as_wtf8 = "~lib/builtins/string.as_wtf8"; + export const string_as_wtf16 = "~lib/builtins/string.as_wtf16"; + export const string_as_iter = "~lib/builtins/string.as_iter"; + export const stringview_wtf8_advance = "~lib/builtins/stringview_wtf8.advance"; + // TODO: stringview_wtf8_encode_utf8 + // TODO: stringview_wtf8_encode_lossy_utf8 + // TODO: stringview_wtf8_encode_wtf8 + export const stringview_wtf8_slice = "~lib/builtins/stringview_wtf8.slice"; + export const stringview_wtf16_length = "~lib/builtins/stringview_wtf16.length"; + export const stringview_wtf16_slice = "~lib/builtins/stringview_wtf16.slice"; + export const stringview_wtf16_get_codeunit = "~lib/builtins/stringview_wtf16.get_codeunit"; + // TODO: stringview_wtf16_encode + export const stringview_iter_next = "~lib/builtins/stringview_iter.next"; + export const stringview_iter_advance = "~lib/builtins/stringview_iter.advance"; + export const stringview_iter_rewind = "~lib/builtins/stringview_iter.rewind"; + export const stringview_iter_slice = "~lib/builtins/stringview_iter.slice"; + // internals export const data_end = "~lib/memory/__data_end"; export const stack_pointer = "~lib/memory/__stack_pointer"; @@ -10826,6 +10868,619 @@ function builtin_i32x4_relaxed_dot_i8x16_i7x16_add_s(ctx: BuiltinFunctionContext } builtinFunctions.set(BuiltinNames.i32x4_relaxed_dot_i8x16_i7x16_add_s, builtin_i32x4_relaxed_dot_i8x16_i7x16_add_s); +// === Stringref ============================================================================== + +// string.new_utf8(ptr: usize, bytes: i32, immMemory?: i32) -> stringref +function builtin_string_new_utf8(ctx: BuiltinFunctionContext): ExpressionRef { + let compiler = ctx.compiler; + let module = compiler.module; + if ( + checkFeatureEnabled(ctx, Feature.Stringref) | + checkTypeAbsent(ctx) | + checkArgsOptional(ctx, 2, 3) + ) return module.unreachable(); + let operands = ctx.operands; + let arg0 = compiler.compileExpression(operands[0], compiler.options.usizeType, Constraints.ConvImplicit); + let arg1 = compiler.compileExpression(operands[1], Type.i32, Constraints.ConvImplicit); + // TODO: immMemory + compiler.currentType = Type.string; + return module.string_new_utf8(arg0, arg1); +} +builtinFunctions.set(BuiltinNames.string_new_utf8, builtin_string_new_utf8); + +// string.new_utf8_array(arr: array, start: i32, end: i32) -> stringref +function builtin_string_new_utf8_array(ctx: BuiltinFunctionContext): ExpressionRef { + let compiler = ctx.compiler; + let module = compiler.module; + if ( + checkFeatureEnabled(ctx, Feature.Stringref) | + checkTypeAbsent(ctx) | + checkArgsRequired(ctx, 3) + ) return module.unreachable(); + let operands = ctx.operands; + let arg0 = compiler.compileExpression(operands[0], Type.array, Constraints.ConvImplicit); // TODO + let arg1 = compiler.compileExpression(operands[1], Type.i32, Constraints.ConvImplicit); + let arg2 = compiler.compileExpression(operands[2], Type.i32, Constraints.ConvImplicit); + compiler.currentType = Type.string; + return module.string_new_utf8_array(arg0, arg1, arg2); +} +builtinFunctions.set(BuiltinNames.string_new_utf8_array, builtin_string_new_utf8_array); + +// string.new_lossy_utf8(ptr: usize, bytes: i32, immMemory?: i32) -> stringref +function builtin_string_new_lossy_utf8(ctx: BuiltinFunctionContext): ExpressionRef { + let compiler = ctx.compiler; + let module = compiler.module; + if ( + checkFeatureEnabled(ctx, Feature.Stringref) | + checkTypeAbsent(ctx) | + checkArgsOptional(ctx, 2, 3) + ) return module.unreachable(); + let operands = ctx.operands; + let arg0 = compiler.compileExpression(operands[0], compiler.options.usizeType, Constraints.ConvImplicit); + let arg1 = compiler.compileExpression(operands[1], Type.i32, Constraints.ConvImplicit); + // TODO: immMemory + compiler.currentType = Type.string; + return module.string_new_lossy_utf8(arg0, arg1); +} +builtinFunctions.set(BuiltinNames.string_new_lossy_utf8, builtin_string_new_lossy_utf8); + +// string.new_lossy_utf8_array(arr: array, start: i32, end: i32) -> stringref +function builtin_string_new_lossy_utf8_array(ctx: BuiltinFunctionContext): ExpressionRef { + let compiler = ctx.compiler; + let module = compiler.module; + if ( + checkFeatureEnabled(ctx, Feature.Stringref) | + checkTypeAbsent(ctx) | + checkArgsRequired(ctx, 3) + ) return module.unreachable(); + let operands = ctx.operands; + let arg0 = compiler.compileExpression(operands[0], Type.array, Constraints.ConvImplicit); // TODO + let arg1 = compiler.compileExpression(operands[1], Type.i32, Constraints.ConvImplicit); + let arg2 = compiler.compileExpression(operands[2], Type.i32, Constraints.ConvImplicit); + compiler.currentType = Type.string; + return module.string_new_lossy_utf8_array(arg0, arg1, arg2); +} +builtinFunctions.set(BuiltinNames.string_new_lossy_utf8_array, builtin_string_new_lossy_utf8_array); + +// string.new_wtf8(ptr: usize, bytes: i32, immMemory?: i32) -> stringref +function builtin_string_new_wtf8(ctx: BuiltinFunctionContext): ExpressionRef { + let compiler = ctx.compiler; + let module = compiler.module; + if ( + checkFeatureEnabled(ctx, Feature.Stringref) | + checkTypeAbsent(ctx) | + checkArgsOptional(ctx, 2, 3) + ) return module.unreachable(); + let operands = ctx.operands; + let arg0 = compiler.compileExpression(operands[0], compiler.options.usizeType, Constraints.ConvImplicit); + let arg1 = compiler.compileExpression(operands[1], Type.i32, Constraints.ConvImplicit); + // TODO: immMemory + compiler.currentType = Type.string; + return module.string_new_wtf8(arg0, arg1); +} +builtinFunctions.set(BuiltinNames.string_new_wtf8, builtin_string_new_wtf8); + +// string.new_wtf8_array(arr: array, start: i32, end: i32) -> stringref +function builtin_string_new_wtf8_array(ctx: BuiltinFunctionContext): ExpressionRef { + let compiler = ctx.compiler; + let module = compiler.module; + if ( + checkFeatureEnabled(ctx, Feature.Stringref) | + checkTypeAbsent(ctx) | + checkArgsRequired(ctx, 3) + ) return module.unreachable(); + let operands = ctx.operands; + let arg0 = compiler.compileExpression(operands[0], Type.array, Constraints.ConvImplicit); // TODO + let arg1 = compiler.compileExpression(operands[1], Type.i32, Constraints.ConvImplicit); + let arg2 = compiler.compileExpression(operands[2], Type.i32, Constraints.ConvImplicit); + compiler.currentType = Type.string; + return module.string_new_wtf8_array(arg0, arg1, arg2); +} +builtinFunctions.set(BuiltinNames.string_new_wtf8_array, builtin_string_new_wtf8_array); + +// string.new_wtf16(ptr: usize, bytes: i32, immMemory?: i32) -> stringref +function builtin_string_new_wtf16(ctx: BuiltinFunctionContext): ExpressionRef { + let compiler = ctx.compiler; + let module = compiler.module; + if ( + checkFeatureEnabled(ctx, Feature.Stringref) | + checkTypeAbsent(ctx) | + checkArgsOptional(ctx, 2, 3) + ) return module.unreachable(); + let operands = ctx.operands; + let arg0 = compiler.compileExpression(operands[0], compiler.options.usizeType, Constraints.ConvImplicit); + let arg1 = compiler.compileExpression(operands[1], Type.i32, Constraints.ConvImplicit); + // TODO: immMemory + compiler.currentType = Type.string; + return module.string_new_wtf16(arg0, arg1); +} +builtinFunctions.set(BuiltinNames.string_new_wtf16, builtin_string_new_wtf16); + +// string.new_wtf16_array(arr: array, start: i32, end: i32) -> stringref +function builtin_string_new_wtf16_array(ctx: BuiltinFunctionContext): ExpressionRef { + let compiler = ctx.compiler; + let module = compiler.module; + if ( + checkFeatureEnabled(ctx, Feature.Stringref) | + checkTypeAbsent(ctx) | + checkArgsOptional(ctx, 2, 3) + ) return module.unreachable(); + let operands = ctx.operands; + let arg0 = compiler.compileExpression(operands[0], Type.array, Constraints.ConvImplicit); // TODO + let arg1 = compiler.compileExpression(operands[1], Type.i32, Constraints.ConvImplicit); + let arg2 = compiler.compileExpression(operands[2], Type.i32, Constraints.ConvImplicit); + compiler.currentType = Type.string; + return module.string_new_wtf16_array(arg0, arg1, arg2); +} +builtinFunctions.set(BuiltinNames.string_new_wtf16_array, builtin_string_new_wtf16_array); + +// string.from_code_point(codepoint: i32) -> stringref +function builtin_string_from_code_point(ctx: BuiltinFunctionContext): ExpressionRef { + let compiler = ctx.compiler; + let module = compiler.module; + if ( + checkFeatureEnabled(ctx, Feature.Stringref) | + checkTypeAbsent(ctx) | + checkArgsRequired(ctx, 1) + ) return module.unreachable(); + let operands = ctx.operands; + let arg0 = compiler.compileExpression(operands[0], Type.i32, Constraints.ConvImplicit); + compiler.currentType = Type.string; + return module.string_from_code_point(arg0); +} +builtinFunctions.set(BuiltinNames.string_from_code_point, builtin_string_from_code_point); + +// string.hash(str: stringref) -> i32 +function builtin_string_hash(ctx: BuiltinFunctionContext): ExpressionRef { + let compiler = ctx.compiler; + let module = compiler.module; + if ( + checkFeatureEnabled(ctx, Feature.Stringref) | + checkTypeAbsent(ctx) | + checkArgsRequired(ctx, 1) + ) return module.unreachable(); + let operands = ctx.operands; + let arg0 = compiler.compileExpression(operands[0], Type.string, Constraints.ConvImplicit); + compiler.currentType = Type.i32; + return module.string_hash(arg0); +} +builtinFunctions.set(BuiltinNames.string_hash, builtin_string_hash); + +// string.measure_utf8(str: stringref) -> i32 +function builtin_string_measure_utf8(ctx: BuiltinFunctionContext): ExpressionRef { + let compiler = ctx.compiler; + let module = compiler.module; + if ( + checkFeatureEnabled(ctx, Feature.Stringref) | + checkTypeAbsent(ctx) | + checkArgsRequired(ctx, 1) + ) return module.unreachable(); + let operands = ctx.operands; + let arg0 = compiler.compileExpression(operands[0], Type.string, Constraints.ConvImplicit); + compiler.currentType = Type.i32; + return module.string_measure_utf8(arg0); +} +builtinFunctions.set(BuiltinNames.string_measure_utf8, builtin_string_measure_utf8); + +// string.measure_wtf8(str: stringref) -> i32 +function builtin_string_measure_wtf8(ctx: BuiltinFunctionContext): ExpressionRef { + let compiler = ctx.compiler; + let module = compiler.module; + if ( + checkFeatureEnabled(ctx, Feature.Stringref) | + checkTypeAbsent(ctx) | + checkArgsRequired(ctx, 1) + ) return module.unreachable(); + let operands = ctx.operands; + let arg0 = compiler.compileExpression(operands[0], Type.string, Constraints.ConvImplicit); + compiler.currentType = Type.i32; + return module.string_measure_wtf8(arg0); +} +builtinFunctions.set(BuiltinNames.string_measure_wtf8, builtin_string_measure_wtf8); + +// string.measure_wtf16(str: stringref) -> i32 +function builtin_string_measure_wtf16(ctx: BuiltinFunctionContext): ExpressionRef { + let compiler = ctx.compiler; + let module = compiler.module; + if ( + checkFeatureEnabled(ctx, Feature.Stringref) | + checkTypeAbsent(ctx) | + checkArgsRequired(ctx, 1) + ) return module.unreachable(); + let operands = ctx.operands; + let arg0 = compiler.compileExpression(operands[0], Type.string, Constraints.ConvImplicit); + compiler.currentType = Type.i32; + return module.string_measure_wtf16(arg0); +} +builtinFunctions.set(BuiltinNames.string_measure_wtf16, builtin_string_measure_wtf16); + +// string.is_usv_sequence(str: stringref) -> bool +function builtin_string_is_usv_sequence(ctx: BuiltinFunctionContext): ExpressionRef { + let compiler = ctx.compiler; + let module = compiler.module; + if ( + checkFeatureEnabled(ctx, Feature.Stringref) | + checkTypeAbsent(ctx) | + checkArgsRequired(ctx, 1) + ) return module.unreachable(); + let operands = ctx.operands; + let arg0 = compiler.compileExpression(operands[0], Type.string, Constraints.ConvImplicit); + compiler.currentType = Type.bool; + return module.string_is_usv_sequence(arg0); +} +builtinFunctions.set(BuiltinNames.string_is_usv_sequence, builtin_string_is_usv_sequence); + +// string.encode_utf8(str: stringref, ptr: usize) -> i32 +function builtin_string_encode_utf8(ctx: BuiltinFunctionContext): ExpressionRef { + let compiler = ctx.compiler; + let module = compiler.module; + if ( + checkFeatureEnabled(ctx, Feature.Stringref) | + checkTypeAbsent(ctx) | + checkArgsOptional(ctx, 2, 3) + ) return module.unreachable(); + let operands = ctx.operands; + let arg0 = compiler.compileExpression(operands[0], Type.string, Constraints.ConvImplicit); + let arg1 = compiler.compileExpression(operands[1], compiler.options.usizeType, Constraints.ConvImplicit); + // TODO: immMemory + compiler.currentType = Type.i32; + return module.string_encode_utf8(arg0, arg1); +} +builtinFunctions.set(BuiltinNames.string_encode_utf8, builtin_string_encode_utf8); + +// string.encode_utf8_array(str: stringref, arr: array, start: i32) -> i32 +function builtin_string_encode_utf8_array(ctx: BuiltinFunctionContext): ExpressionRef { + let compiler = ctx.compiler; + let module = compiler.module; + if ( + checkFeatureEnabled(ctx, Feature.Stringref) | + checkTypeAbsent(ctx) | + checkArgsRequired(ctx, 3) + ) return module.unreachable(); + let operands = ctx.operands; + let arg0 = compiler.compileExpression(operands[0], Type.string, Constraints.ConvImplicit); + let arg1 = compiler.compileExpression(operands[1], Type.array, Constraints.ConvImplicit); // TODO: array + let arg2 = compiler.compileExpression(operands[2], Type.i32, Constraints.ConvImplicit); + compiler.currentType = Type.i32; + return module.string_encode_utf8_array(arg0, arg1, arg2); +} +builtinFunctions.set(BuiltinNames.string_encode_utf8_array, builtin_string_encode_utf8_array); + +// TOOD: string.encode_lossy_utf8(str: stringref, ptr: usize) -> i32 +// TODO: string.encode_lossy_utf8_array(str: stringref, arr: array, start: i32) -> i32 + +// string.encode_wtf8(str: stringref, ptr: usize) -> i32 +function builtin_string_encode_wtf8(ctx: BuiltinFunctionContext): ExpressionRef { + let compiler = ctx.compiler; + let module = compiler.module; + if ( + checkFeatureEnabled(ctx, Feature.Stringref) | + checkTypeAbsent(ctx) | + checkArgsOptional(ctx, 2, 3) + ) return module.unreachable(); + let operands = ctx.operands; + let arg0 = compiler.compileExpression(operands[0], Type.string, Constraints.ConvImplicit); + let arg1 = compiler.compileExpression(operands[1], compiler.options.usizeType, Constraints.ConvImplicit); + // TODO: immMemory + compiler.currentType = Type.i32; + return module.string_encode_wtf8(arg0, arg1); +} +builtinFunctions.set(BuiltinNames.string_encode_wtf8, builtin_string_encode_wtf8); + +// string.encode_wtf8_array(str: stringref, arr: array, start: i32) -> i32 +function builtin_string_encode_wtf8_array(ctx: BuiltinFunctionContext): ExpressionRef { + let compiler = ctx.compiler; + let module = compiler.module; + if ( + checkFeatureEnabled(ctx, Feature.Stringref) | + checkTypeAbsent(ctx) | + checkArgsRequired(ctx, 3) + ) return module.unreachable(); + let operands = ctx.operands; + let arg0 = compiler.compileExpression(operands[0], Type.string, Constraints.ConvImplicit); + let arg1 = compiler.compileExpression(operands[1], Type.array, Constraints.ConvImplicit); // TODO: array + let arg2 = compiler.compileExpression(operands[2], Type.i32, Constraints.ConvImplicit); + compiler.currentType = Type.i32; + return module.string_encode_wtf8_array(arg0, arg1, arg2); +} +builtinFunctions.set(BuiltinNames.string_encode_wtf8_array, builtin_string_encode_wtf8_array); + +// string.encode_wtf16(str: stringref, ptr: usize) -> i32 +function builtin_string_encode_wtf16(ctx: BuiltinFunctionContext): ExpressionRef { + let compiler = ctx.compiler; + let module = compiler.module; + if ( + checkFeatureEnabled(ctx, Feature.Stringref) | + checkTypeAbsent(ctx) | + checkArgsOptional(ctx, 2, 3) + ) return module.unreachable(); + let operands = ctx.operands; + let arg0 = compiler.compileExpression(operands[0], Type.string, Constraints.ConvImplicit); + let arg1 = compiler.compileExpression(operands[1], compiler.options.usizeType, Constraints.ConvImplicit); + // TODO: immMemory + compiler.currentType = Type.i32; + return module.string_encode_wtf16(arg0, arg1); +} +builtinFunctions.set(BuiltinNames.string_encode_wtf16, builtin_string_encode_wtf16); + +// string.encode_wtf16_array(str: stringref, arr: array, start: i32) -> i32 +function builtin_string_encode_wtf16_array(ctx: BuiltinFunctionContext): ExpressionRef { + let compiler = ctx.compiler; + let module = compiler.module; + if ( + checkFeatureEnabled(ctx, Feature.Stringref) | + checkTypeAbsent(ctx) | + checkArgsRequired(ctx, 3) + ) return module.unreachable(); + let operands = ctx.operands; + let arg0 = compiler.compileExpression(operands[0], Type.string, Constraints.ConvImplicit); + let arg1 = compiler.compileExpression(operands[1], Type.array, Constraints.ConvImplicit); // TODO: array + let arg2 = compiler.compileExpression(operands[2], Type.i32, Constraints.ConvImplicit); + compiler.currentType = Type.i32; + return module.string_encode_wtf16_array(arg0, arg1, arg2); +} +builtinFunctions.set(BuiltinNames.string_encode_wtf16_array, builtin_string_encode_wtf16_array); + +// string.concat(left: stringref, right: stringref) -> stringref +function builtin_string_concat(ctx: BuiltinFunctionContext): ExpressionRef { + let compiler = ctx.compiler; + let module = compiler.module; + if ( + checkFeatureEnabled(ctx, Feature.Stringref) | + checkTypeAbsent(ctx) | + checkArgsRequired(ctx, 2) + ) return module.unreachable(); + let operands = ctx.operands; + let arg0 = compiler.compileExpression(operands[0], Type.string, Constraints.ConvImplicit); + let arg1 = compiler.compileExpression(operands[1], Type.string, Constraints.ConvImplicit); + compiler.currentType = Type.string; + return module.string_concat(arg0, arg1); +} +builtinFunctions.set(BuiltinNames.string_concat, builtin_string_concat); + +// string.eq(left: stringref, right: stringref) -> bool +function builtin_string_eq(ctx: BuiltinFunctionContext): ExpressionRef { + let compiler = ctx.compiler; + let module = compiler.module; + if ( + checkFeatureEnabled(ctx, Feature.Stringref) | + checkTypeAbsent(ctx) | + checkArgsRequired(ctx, 2) + ) return module.unreachable(); + let operands = ctx.operands; + let nullableString = Type.string.asNullable(); + let arg0 = compiler.compileExpression(operands[0], nullableString, Constraints.ConvImplicit); + let arg1 = compiler.compileExpression(operands[1], nullableString, Constraints.ConvImplicit); + compiler.currentType = Type.bool; + return module.string_eq(arg0, arg1); +} +builtinFunctions.set(BuiltinNames.string_eq, builtin_string_eq); + +// string.compare(left: stringref, right: stringref) -> i32 +function builtin_string_compare(ctx: BuiltinFunctionContext): ExpressionRef { + let compiler = ctx.compiler; + let module = compiler.module; + if ( + checkFeatureEnabled(ctx, Feature.Stringref) | + checkTypeAbsent(ctx) | + checkArgsRequired(ctx, 2) + ) return module.unreachable(); + let operands = ctx.operands; + let arg0 = compiler.compileExpression(operands[0], Type.string, Constraints.ConvImplicit); + let arg1 = compiler.compileExpression(operands[1], Type.string, Constraints.ConvImplicit); + compiler.currentType = Type.i32; + return module.string_compare(arg0, arg1); +} +builtinFunctions.set(BuiltinNames.string_compare, builtin_string_compare); + +// string.as_wtf8(str: stringref) -> stringview_wtf8 +function builtin_string_as_wtf8(ctx: BuiltinFunctionContext): ExpressionRef { + let compiler = ctx.compiler; + let module = compiler.module; + if ( + checkFeatureEnabled(ctx, Feature.Stringref) | + checkTypeAbsent(ctx) | + checkArgsRequired(ctx, 1) + ) return module.unreachable(); + let operands = ctx.operands; + let arg0 = compiler.compileExpression(operands[0], Type.string, Constraints.ConvImplicit); + compiler.currentType = Type.stringview_wtf8; + return module.string_as_wtf8(arg0); +} +builtinFunctions.set(BuiltinNames.string_as_wtf8, builtin_string_as_wtf8); + +// string.as_wtf16(str: stringref) -> stringview_wtf16 +function builtin_string_as_wtf16(ctx: BuiltinFunctionContext): ExpressionRef { + let compiler = ctx.compiler; + let module = compiler.module; + if ( + checkFeatureEnabled(ctx, Feature.Stringref) | + checkTypeAbsent(ctx) | + checkArgsRequired(ctx, 1) + ) return module.unreachable(); + let operands = ctx.operands; + let arg0 = compiler.compileExpression(operands[0], Type.string, Constraints.ConvImplicit); + compiler.currentType = Type.stringview_wtf16; + return module.string_as_wtf16(arg0); +} +builtinFunctions.set(BuiltinNames.string_as_wtf16, builtin_string_as_wtf16); + +// string.as_iter(str: stringref) -> stringview_iter +function builtin_string_as_iter(ctx: BuiltinFunctionContext): ExpressionRef { + let compiler = ctx.compiler; + let module = compiler.module; + if ( + checkFeatureEnabled(ctx, Feature.Stringref) | + checkTypeAbsent(ctx) | + checkArgsRequired(ctx, 1) + ) return module.unreachable(); + let operands = ctx.operands; + let arg0 = compiler.compileExpression(operands[0], Type.string, Constraints.ConvImplicit); + compiler.currentType = Type.stringview_iter; + return module.string_as_iter(arg0); +} +builtinFunctions.set(BuiltinNames.string_as_iter, builtin_string_as_iter); + +// stringview_wtf8.advance(view: stringview_wtf8, pos: i32, bytes: i32) -> i32 +function builtin_stringview_wtf8_advance(ctx: BuiltinFunctionContext): ExpressionRef { + let compiler = ctx.compiler; + let module = compiler.module; + if ( + checkFeatureEnabled(ctx, Feature.Stringref) | + checkTypeAbsent(ctx) | + checkArgsRequired(ctx, 3) + ) return module.unreachable(); + let operands = ctx.operands; + let arg0 = compiler.compileExpression(operands[0], Type.stringview_wtf8, Constraints.ConvImplicit); + let arg1 = compiler.compileExpression(operands[1], Type.i32, Constraints.ConvImplicit); + let arg2 = compiler.compileExpression(operands[2], Type.i32, Constraints.ConvImplicit); + compiler.currentType = Type.i32; + return module.stringview_wtf8_advance(arg0, arg1, arg2); +} +builtinFunctions.set(BuiltinNames.stringview_wtf8_advance, builtin_stringview_wtf8_advance); + +// TODO: stringview_wtf8.encode_utf8 +// TODO: stringview_wtf8.encode_lossy_utf8 +// TODO: stringview_wtf8.encode_wtf8 + +// stringview_wtf8.slice(view: stringview_wtf8, start: i32, end: i32) -> stringview_wtf8 +function builtin_stringview_wtf8_slice(ctx: BuiltinFunctionContext): ExpressionRef { + let compiler = ctx.compiler; + let module = compiler.module; + if ( + checkFeatureEnabled(ctx, Feature.Stringref) | + checkTypeAbsent(ctx) | + checkArgsRequired(ctx, 3) + ) return module.unreachable(); + let operands = ctx.operands; + let arg0 = compiler.compileExpression(operands[0], Type.stringview_wtf8, Constraints.ConvImplicit); + let arg1 = compiler.compileExpression(operands[1], Type.i32, Constraints.ConvImplicit); + let arg2 = compiler.compileExpression(operands[2], Type.i32, Constraints.ConvImplicit); + compiler.currentType = Type.string; + return module.stringview_wtf8_slice(arg0, arg1, arg2); +} +builtinFunctions.set(BuiltinNames.stringview_wtf8_slice, builtin_stringview_wtf8_slice); + +// stringview_wtf16.length(view: stringview_wtf8) -> i32 +function builtin_stringview_wtf16_length(ctx: BuiltinFunctionContext): ExpressionRef { + let compiler = ctx.compiler; + let module = compiler.module; + if ( + checkFeatureEnabled(ctx, Feature.Stringref) | + checkTypeAbsent(ctx) | + checkArgsRequired(ctx, 1) + ) return module.unreachable(); + let operands = ctx.operands; + let arg0 = compiler.compileExpression(operands[0], Type.stringview_wtf16, Constraints.ConvImplicit); + compiler.currentType = Type.i32; + return module.stringview_wtf16_length(arg0); +} +builtinFunctions.set(BuiltinNames.stringview_wtf16_length, builtin_stringview_wtf16_length); + +// stringview_wtf16.slice(view: stringview_wtf16, start: i32, end: i32) -> stringref +function builtin_stringview_wtf16_slice(ctx: BuiltinFunctionContext): ExpressionRef { + let compiler = ctx.compiler; + let module = compiler.module; + if ( + checkFeatureEnabled(ctx, Feature.Stringref) | + checkTypeAbsent(ctx) | + checkArgsRequired(ctx, 3) + ) return module.unreachable(); + let operands = ctx.operands; + let arg0 = compiler.compileExpression(operands[0], Type.stringview_wtf16, Constraints.ConvImplicit); + let arg1 = compiler.compileExpression(operands[1], Type.i32, Constraints.ConvImplicit); + let arg2 = compiler.compileExpression(operands[2], Type.i32, Constraints.ConvImplicit); + compiler.currentType = Type.string; + return module.stringview_wtf16_slice(arg0, arg1, arg2); +} +builtinFunctions.set(BuiltinNames.stringview_wtf16_slice, builtin_stringview_wtf16_slice); + +// stringview_wtf16.get_codeunit(view: stringview_wtf16, pos: i32) -> i32 +function builtin_stringview_wtf16_get_codeunit(ctx: BuiltinFunctionContext): ExpressionRef { + let compiler = ctx.compiler; + let module = compiler.module; + if ( + checkFeatureEnabled(ctx, Feature.Stringref) | + checkTypeAbsent(ctx) | + checkArgsRequired(ctx, 2) + ) return module.unreachable(); + let operands = ctx.operands; + let arg0 = compiler.compileExpression(operands[0], Type.stringview_wtf16, Constraints.ConvImplicit); + let arg1 = compiler.compileExpression(operands[1], Type.i32, Constraints.ConvImplicit); + compiler.currentType = Type.i32; + return module.stringview_wtf16_get_codeunit(arg0, arg1); +} +builtinFunctions.set(BuiltinNames.stringview_wtf16_get_codeunit, builtin_stringview_wtf16_get_codeunit); + +// TODO: stringview_wtf16.encode + +// stringview_iter.next(view: stringview_iter) -> i32 +function builtin_stringview_iter_next(ctx: BuiltinFunctionContext): ExpressionRef { + let compiler = ctx.compiler; + let module = compiler.module; + if ( + checkFeatureEnabled(ctx, Feature.Stringref) | + checkTypeAbsent(ctx) | + checkArgsRequired(ctx, 1) + ) return module.unreachable(); + let operands = ctx.operands; + let arg0 = compiler.compileExpression(operands[0], Type.stringview_iter, Constraints.ConvImplicit); + compiler.currentType = Type.i32; + return module.stringview_iter_next(arg0); +} +builtinFunctions.set(BuiltinNames.stringview_iter_next, builtin_stringview_iter_next); + +// stringview_iter.advance(view: stringview_iter, count: i32) -> i32 +function builtin_stringview_iter_advance(ctx: BuiltinFunctionContext): ExpressionRef { + let compiler = ctx.compiler; + let module = compiler.module; + if ( + checkFeatureEnabled(ctx, Feature.Stringref) | + checkTypeAbsent(ctx) | + checkArgsRequired(ctx, 2) + ) return module.unreachable(); + let operands = ctx.operands; + let arg0 = compiler.compileExpression(operands[0], Type.stringview_iter, Constraints.ConvImplicit); + let arg1 = compiler.compileExpression(operands[1], Type.i32, Constraints.ConvImplicit); + compiler.currentType = Type.i32; + return module.stringview_iter_advance(arg0, arg1); +} +builtinFunctions.set(BuiltinNames.stringview_iter_advance, builtin_stringview_iter_advance); + +// stringview_iter.rewind(view: stringview_iter, count: i32) -> i32 +function builtin_stringview_iter_rewind(ctx: BuiltinFunctionContext): ExpressionRef { + let compiler = ctx.compiler; + let module = compiler.module; + if ( + checkFeatureEnabled(ctx, Feature.Stringref) | + checkTypeAbsent(ctx) | + checkArgsRequired(ctx, 2) + ) return module.unreachable(); + let operands = ctx.operands; + let arg0 = compiler.compileExpression(operands[0], Type.stringview_iter, Constraints.ConvImplicit); + let arg1 = compiler.compileExpression(operands[1], Type.i32, Constraints.ConvImplicit); + compiler.currentType = Type.i32; + return module.stringview_iter_rewind(arg0, arg1); +} +builtinFunctions.set(BuiltinNames.stringview_iter_rewind, builtin_stringview_iter_rewind); + +// stringview_iter.slice(view: stringview_iter, count: i32) -> stringref +function builtin_stringview_iter_slice(ctx: BuiltinFunctionContext): ExpressionRef { + let compiler = ctx.compiler; + let module = compiler.module; + if ( + checkFeatureEnabled(ctx, Feature.Stringref) | + checkTypeAbsent(ctx) | + checkArgsRequired(ctx, 2) + ) return module.unreachable(); + let operands = ctx.operands; + let arg0 = compiler.compileExpression(operands[0], Type.stringview_iter, Constraints.ConvImplicit); + let arg1 = compiler.compileExpression(operands[1], Type.i32, Constraints.ConvImplicit); + compiler.currentType = Type.string; + return module.stringview_iter_slice(arg0, arg1); +} +builtinFunctions.set(BuiltinNames.stringview_iter_slice, builtin_stringview_iter_slice); + // === Internal helpers ======================================================================= /** Compiles the `visit_globals` function. */ diff --git a/src/compiler.ts b/src/compiler.ts index 7703f5de18..e2b37b9958 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -7923,6 +7923,11 @@ export class Compiler extends DiagnosticEmitter { } case LiteralKind.String: { assert(!implicitlyNegate); + // Emit a stringref if context indicates + if (contextualType.kind == TypeKind.String) { + this.currentType = Type.string; + return module.string_const((expression).value); + } return this.compileStringLiteral(expression, constraints); } case LiteralKind.Template: { diff --git a/src/flow.ts b/src/flow.ts index 3f4838ab3d..2ff37ea6f6 100644 --- a/src/flow.ts +++ b/src/flow.ts @@ -70,6 +70,7 @@ import { getUnaryValue, getCallOperandAt, getCallOperandCount, + getRefIsNullValue, isConstZero, isConstNonZero } from "./module"; @@ -1049,6 +1050,10 @@ export class Flow { } break; } + case ExpressionId.RefIsNull: { + this.inheritNonnullIfFalse(getRefIsNullValue(expr), iff); // value == null -> value must have been null + break; + } } } @@ -1140,6 +1145,10 @@ export class Flow { } break; } + case ExpressionId.RefIsNull: { + this.inheritNonnullIfTrue(getRefIsNullValue(expr), iff); // value == null -> value must have been non-null + break; + } } } diff --git a/src/module.ts b/src/module.ts index 705750ec4a..c7853b025f 100644 --- a/src/module.ts +++ b/src/module.ts @@ -1298,7 +1298,9 @@ export const enum StringMeasureOp { /** string.is_usv_sequence */ IsUSV = 3 /* _BinaryenStringMeasureIsUSV */, /** stringview_wtf16.length */ - WTF16View = 4 /* _BinaryenStringMeasureWTF16View */ + WTF16View = 4 /* _BinaryenStringMeasureWTF16View */, + /** string.hash */ + Hash = 5 /* TODO_BinaryenStringMeasureHash */ } /** Binaryen StringEncode operation constants. */ @@ -1472,14 +1474,6 @@ export class Module { return binaryen._BinaryenRefEq(this.ref, left, right); } - string_eq(left: ExpressionRef, right: ExpressionRef): ExpressionRef { - return binaryen._BinaryenStringEq(this.ref, StringEqOp.Equal, left, right); - } - - string_compare(left: ExpressionRef, right: ExpressionRef): ExpressionRef { - return binaryen._BinaryenStringEq(this.ref, StringEqOp.Compare, left, right); - } - // expressions unary( @@ -2136,6 +2130,274 @@ export class Module { return binaryen._BinaryenI31Get(this.ref, expr, signed); } + // stringref + + string_const( + str: string + ): ExpressionRef { + return binaryen._BinaryenStringConst(this.ref, this.allocStringCached(str)); + } + + string_new_utf8( + ptr: ExpressionRef, + length: ExpressionRef, + memory: string = CommonNames.DefaultMemory // TODO + ): ExpressionRef { + return binaryen._BinaryenStringNew(this.ref, StringNewOp.UTF8, ptr, length, 0, 0, false); + } + + string_new_utf8_array( + arr: ExpressionRef, + start: ExpressionRef, + end: ExpressionRef + ): ExpressionRef { + return binaryen._BinaryenStringNew(this.ref, StringNewOp.UTF8Array, arr, 0, start, end, false); + } + + string_new_lossy_utf8( + ptr: ExpressionRef, + length: ExpressionRef, + memory: string = CommonNames.DefaultMemory // TODO + ): ExpressionRef { + return binaryen._BinaryenStringNew(this.ref, StringNewOp.Replace, ptr, length, 0, 0, false); + } + + string_new_lossy_utf8_array( + arr: ExpressionRef, + start: ExpressionRef, + end: ExpressionRef + ): ExpressionRef { + return binaryen._BinaryenStringNew(this.ref, StringNewOp.ReplaceArray, arr, 0, start, end, false); + } + + string_new_wtf8( + ptr: ExpressionRef, + length: ExpressionRef, + memory: string = CommonNames.DefaultMemory // TODO + ): ExpressionRef { + return binaryen._BinaryenStringNew(this.ref, StringNewOp.WTF8, ptr, length, 0, 0, false); + } + + string_new_wtf8_array( + arr: ExpressionRef, + start: ExpressionRef, + end: ExpressionRef + ): ExpressionRef { + return binaryen._BinaryenStringNew(this.ref, StringNewOp.WTF8Array, arr, 0, start, end, false); + } + + string_new_wtf16( + ptr: ExpressionRef, + length: ExpressionRef, + memory: string = CommonNames.DefaultMemory // TODO + ): ExpressionRef { + return binaryen._BinaryenStringNew(this.ref, StringNewOp.WTF16, ptr, length, 0, 0, false); + } + + string_new_wtf16_array( + arr: ExpressionRef, + start: ExpressionRef, + end: ExpressionRef + ): ExpressionRef { + return binaryen._BinaryenStringNew(this.ref, StringNewOp.WTF16Array, arr, 0, start, end, false); + } + + string_from_code_point( + codepoint: ExpressionRef + ): ExpressionRef { + return binaryen._BinaryenStringNew(this.ref, StringNewOp.FromCodePoint, codepoint, 0, 0, 0, false); + } + + string_hash( + str: ExpressionRef + ): ExpressionRef { + return binaryen._BinaryenStringMeasure(this.ref, StringMeasureOp.Hash, str); + } + + string_measure_utf8( + str: ExpressionRef + ): ExpressionRef { + return binaryen._BinaryenStringMeasure(this.ref, StringMeasureOp.UTF8, str); + } + + string_measure_wtf8( + str: ExpressionRef + ): ExpressionRef { + return binaryen._BinaryenStringMeasure(this.ref, StringMeasureOp.WTF8, str); + } + + string_measure_wtf16( + str: ExpressionRef + ): ExpressionRef { + return binaryen._BinaryenStringMeasure(this.ref, StringMeasureOp.WTF16, str); + } + + string_is_usv_sequence( + str: ExpressionRef + ): ExpressionRef { + return binaryen._BinaryenStringMeasure(this.ref, StringMeasureOp.IsUSV, str); + } + + string_encode_utf8( + str: ExpressionRef, + ptr: ExpressionRef, + memory: string = CommonNames.DefaultMemory // TODO + ): ExpressionRef { + return binaryen._BinaryenStringEncode(this.ref, StringEncodeOp.UTF8, str, ptr, 0); + } + + string_encode_utf8_array( + str: ExpressionRef, + arr: ExpressionRef, + start: ExpressionRef + ): ExpressionRef { + return binaryen._BinaryenStringEncode(this.ref, StringEncodeOp.UTF8Array, str, arr, start); + } + + // TOOD: string_encode_lossy_utf8 + // TODO: string_encode_lossy_utf8_array + + string_encode_wtf8( + str: ExpressionRef, + ptr: ExpressionRef, + memory: string = CommonNames.DefaultMemory // TODO + ): ExpressionRef { + return binaryen._BinaryenStringEncode(this.ref, StringEncodeOp.WTF8, str, ptr, 0); + } + + string_encode_wtf8_array( + str: ExpressionRef, + arr: ExpressionRef, + start: ExpressionRef + ): ExpressionRef { + return binaryen._BinaryenStringEncode(this.ref, StringEncodeOp.WTF8Array, str, arr, start); + } + + string_encode_wtf16( + str: ExpressionRef, + ptr: ExpressionRef, + memory: string = CommonNames.DefaultMemory // TODO + ): ExpressionRef { + return binaryen._BinaryenStringEncode(this.ref, StringEncodeOp.WTF16, str, ptr, 0); + } + + string_encode_wtf16_array( + str: ExpressionRef, + arr: ExpressionRef, + start: ExpressionRef + ): ExpressionRef { + return binaryen._BinaryenStringEncode(this.ref, StringEncodeOp.WTF16Array, str, arr, start); + } + + string_concat( + left: ExpressionRef, + right: ExpressionRef + ): ExpressionRef { + return binaryen._BinaryenStringConcat(this.ref, left, right); + } + + string_eq( + left: ExpressionRef, + right: ExpressionRef + ): ExpressionRef { + return binaryen._BinaryenStringEq(this.ref, StringEqOp.Equal, left, right); + } + + string_compare( + left: ExpressionRef, + right: ExpressionRef + ): ExpressionRef { + return binaryen._BinaryenStringEq(this.ref, StringEqOp.Compare, left, right); + } + + string_as_wtf8( + str: ExpressionRef + ): ExpressionRef { + return binaryen._BinaryenStringAs(this.ref, StringAsOp.WTF8, str); + } + + string_as_wtf16( + str: ExpressionRef + ): ExpressionRef { + return binaryen._BinaryenStringAs(this.ref, StringAsOp.WTF16, str); + } + + string_as_iter( + str: ExpressionRef + ): ExpressionRef { + return binaryen._BinaryenStringAs(this.ref, StringAsOp.Iter, str); + } + + stringview_wtf8_advance( + view: ExpressionRef, + pos: ExpressionRef, + bytes: ExpressionRef + ): ExpressionRef { + return binaryen._BinaryenStringWTF8Advance(this.ref, view, pos, bytes); + } + + // TODO: stringview_wtf8_encode_utf8 + // TODO: stringview_wtf8_encode_lossy_utf8 + // TODO: stringview_wtf8_encode_wtf8 + + stringview_wtf8_slice( + view: ExpressionRef, + start: ExpressionRef, + end: ExpressionRef + ): ExpressionRef { + return binaryen._BinaryenStringSliceWTF(this.ref, StringSliceWTFOp.WTF8, view, start, end); + } + + stringview_wtf16_length( + view: ExpressionRef + ): ExpressionRef { + return binaryen._BinaryenStringMeasure(this.ref, StringMeasureOp.WTF16View, view); + } + + stringview_wtf16_slice( + view: ExpressionRef, + start: ExpressionRef, + end: ExpressionRef + ): ExpressionRef { + return binaryen._BinaryenStringSliceWTF(this.ref, StringSliceWTFOp.WTF16, view, start, end); + } + + stringview_wtf16_get_codeunit( + view: ExpressionRef, + pos: ExpressionRef + ): ExpressionRef { + return binaryen._BinaryenStringWTF16Get(this.ref, view, pos); + } + + // TODO: stringview_wtf16_encode + + stringview_iter_next( + view: ExpressionRef + ): ExpressionRef { + return binaryen._BinaryenStringIterNext(this.ref, view); + } + + stringview_iter_advance( + view: ExpressionRef, + count: ExpressionRef + ): ExpressionRef { + return binaryen._BinaryenStringIterMove(this.ref, StringIterMoveOp.Advance, view, count); + } + + stringview_iter_rewind( + view: ExpressionRef, + count: ExpressionRef + ): ExpressionRef { + return binaryen._BinaryenStringIterMove(this.ref, StringIterMoveOp.Rewind, view, count); + } + + stringview_iter_slice( + view: ExpressionRef, + count: ExpressionRef + ): ExpressionRef { + return binaryen._BinaryenStringSliceIter(this.ref, view, count); + } + // globals addGlobal( @@ -2959,7 +3221,8 @@ export class Module { case ExpressionId.Const: case ExpressionId.RefNull: case ExpressionId.RefFunc: - case ExpressionId.I31New: return true; + case ExpressionId.I31New: + case ExpressionId.StringConst: return true; case ExpressionId.Binary: { if (this.getFeatures() & FeatureFlags.ExtendedConst) { switch (getBinaryOp(expr)) { @@ -3290,6 +3553,10 @@ export function getMemoryGrowDelta(expr: ExpressionRef): ExpressionRef { return binaryen._BinaryenMemoryGrowGetDelta(expr); } +export function getRefIsNullValue(expr: ExpressionRef): ExpressionRef { + return binaryen._BinaryenRefIsNullGetValue(expr); +} + // functions export function getFunctionBody(func: FunctionRef): ExpressionRef { diff --git a/src/program.ts b/src/program.ts index 829cffee05..8f0e805020 100644 --- a/src/program.ts +++ b/src/program.ts @@ -633,6 +633,14 @@ export class Program extends DiagnosticEmitter { } private _stringInstance: Class | null = null; + /** Gets the standard `RefString` instance. */ + get refStringInstance(): Class { + let cached = this._refStringInstance; + if (!cached) this._refStringInstance = cached = this.requireClass(CommonNames.RefString); + return cached; + } + private _refStringInstance: Class | null = null; + /** Gets the standard `RegExp` instance. */ get regexpInstance(): Class { let cached = this._regexpInstance; diff --git a/src/resolver.ts b/src/resolver.ts index 16bd967847..e2d9c7167d 100644 --- a/src/resolver.ts +++ b/src/resolver.ts @@ -2341,7 +2341,11 @@ export class Resolver extends DiagnosticEmitter { let fltType = ctxType == Type.f32 ? Type.f32 : Type.f64; return assert(fltType.getClassOrWrapper(this.program)); } - case LiteralKind.String: + case LiteralKind.String: { + // Resolve to stringref if context indicates + if (ctxType.kind == TypeKind.String) return this.program.refStringInstance; + // fall-through + } case LiteralKind.Template: { return this.program.stringInstance; } diff --git a/std/assembly/builtins.ts b/std/assembly/builtins.ts index 12f0dfd574..e672b08912 100644 --- a/std/assembly/builtins.ts +++ b/std/assembly/builtins.ts @@ -2596,6 +2596,170 @@ export abstract class i31 { // FIXME: usage of 'new' requires a class :( static get(i31expr: i31ref): i32 { return unreachable(); } } +export namespace string { + + // @ts-ignore: decorator + @unsafe @builtin + export declare function new_utf8(ptr: usize, bytes: i32): ref_string; + + // @ts-ignore: decorator + @builtin + export declare function new_utf8_array(array: arrayref, start: i32, end: i32): ref_string; + + // @ts-ignore: decorator + @unsafe @builtin + export declare function new_lossy_utf8(ptr: usize, bytes: i32): ref_string; + + // @ts-ignore: decorator + @builtin + export declare function new_lossy_utf8_array(array: ref_array, start: i32, end: i32): ref_string; + + // @ts-ignore: decorator + @unsafe @builtin + export declare function new_wtf8(ptr: usize, bytes: i32): ref_string; + + // @ts-ignore: decorator + @builtin + export declare function new_wtf8_array(arr: ref_array, start: i32, end: i32): ref_string; + + // @ts-ignore: decorator + @unsafe @builtin + export declare function new_wtf16(ptr: usize, units: i32): ref_string; + + // @ts-ignore: decorator + @builtin + export declare function new_wtf16_array(arr: ref_array, start: i32, end: i32): ref_string; + + // @ts-ignore: decorator + @builtin + export declare function from_code_point(codepoint: i32): ref_string; + + // @ts-ignore: decorator + @builtin + export declare function hash(str: ref_string): i32; + + // @ts-ignore: decorator + @builtin + export declare function measure_utf8(str: ref_string): i32; + + // @ts-ignore: decorator + @builtin + export declare function measure_wtf8(str: ref_string): i32; + + // @ts-ignore: decorator + @builtin + export declare function measure_wtf16(str: ref_string): i32; + + // @ts-ignore: decorator + @builtin + export declare function is_usv_sequence(str: ref_string): i32; + + // @ts-ignore: decorator + @unsafe @builtin + export declare function encode_utf8(str: ref_string, ptr: usize): i32; + + // @ts-ignore: decorator + @builtin + export declare function encode_utf8_array(str: ref_string, arr: arrayref, start: i32): i32; + + // TODO: encode_lossy_utf8 + + // TODO: encode_lossy_utf8_array + + // @ts-ignore: decorator + @unsafe @builtin + export declare function encode_wtf8(str: ref_string, ptr: usize): i32; + + // @ts-ignore: decorator + @builtin + export declare function encode_wtf8_array(str: ref_string, arr: ref_array, start: i32): i32; + + // @ts-ignore: decorator + @unsafe @builtin + export declare function encode_wtf16(str: ref_string, ptr: usize): i32; + + // @ts-ignore: decorator + @builtin + export declare function encode_wtf16_array(str: ref_string, arr: ref_array, start: i32): i32; + + // @ts-ignore: decorator + @builtin + export declare function concat(left: ref_string, right: ref_string): ref_string; + + // @ts-ignore: decorator + @builtin + export declare function eq(left: ref_string | null, right: ref_string | null): bool; + + // @ts-ignore: decorator + @builtin + export declare function compare(left: ref_string, right: ref_string): i32; + + // @ts-ignore: decorator + @builtin + export declare function as_wtf8(str: ref_string): ref_stringview_wtf8; + + // @ts-ignore: decorator + @builtin + export declare function as_wtf16(str: ref_string): ref_stringview_wtf16; + + // @ts-ignore: decorator + @builtin + export declare function as_iter(str: ref_string): ref_stringview_iter; +} + +export namespace stringview_wtf8 { + + // @ts-ignore: decorator + @builtin + export declare function advance(view: ref_stringview_wtf8, pos: i32, bytes: i32): i32; + + // @ts-ignore: decorator + @builtin + export declare function slice(view: ref_stringview_wtf8, start: i32, end: i32): ref_string; + + // TODO: encode_utf8 + + // TODO: encode_lossy_utf8 + + // TODO: encode_wtf8 +} + +export namespace stringview_wtf16 { + + // @ts-ignore: decorator + @builtin + export declare function length(view: ref_stringview_wtf16): i32; + + // @ts-ignore: decorator + @builtin + export declare function slice(view: ref_stringview_wtf16, start: i32, end: i32): ref_string; + + // @ts-ignore: decorator + @builtin + export declare function get_codeunit(view: ref_stringview_wtf16, pos: i32): i32; + + // TODO: encode +} + +export namespace stringview_iter { + + // @ts-ignore: decorator + @builtin + export declare function next(view: ref_stringview_iter): i32; + + // @ts-ignore: decorator + @builtin + export declare function advance(view: ref_stringview_iter, count: i32): i32; + + // @ts-ignore: decorator + @builtin + export declare function rewind(view: ref_stringview_iter, count: i32): i32; + + // @ts-ignore: decorator + @builtin + export declare function slice(view: ref_stringview_iter, count: i32): ref_string; +} + /* eslint-disable @typescript-eslint/no-unused-vars */ // @ts-ignore: decorator diff --git a/std/assembly/index.d.ts b/std/assembly/index.d.ts index cbba3592aa..6255df2afe 100644 --- a/std/assembly/index.d.ts +++ b/std/assembly/index.d.ts @@ -64,7 +64,7 @@ declare type ref_i31 = object; /** Canonical nullable 31-bit integer reference. */ declare type i31ref = ref_i31 | null; /** Non-nullable string reference. */ -declare type ref_string = object; +declare type ref_string = string; /** Canonical nullable string reference. */ declare type stringref = ref_string | null; /** Non-nullable WTF-8 string view. */ @@ -1668,6 +1668,94 @@ declare abstract class i31 { static get(i31expr: ref_i31 | null): i32; } +declare namespace string { + /** Creates a new string from memory using a strict UTF-8 decoder, decoding `bytes` bytes starting at `ptr`. */ + export function new_utf8(ptr: usize, bytes: i32): ref_string; + /** Creates a new string from `array` using a strict UTF-8 decoder, decoding from `start` inclusive to `end` exclusive. */ + export function new_utf8_array(array: ref_array, start: i32, end: i32): ref_string; + /** Creates a new string from memory using a lossy UTF-8 decoder, decoding `bytes` bytes starting at `ptr`. */ + export function new_lossy_utf8(ptr: usize, bytes: i32): ref_string; + /** Creates a new string from `array` using a lossy UTF-8 decoder, decoding from `start` inclusive to `end` exclusive. */ + export function new_lossy_utf8_array(array: ref_array, start: i32, end: i32): ref_string; + /** Creates a new string from memory using a strict WTF-8 decoder, decodiing `bytes` bytes starting at `ptr`. */ + export function new_wtf8(ptr: usize, bytes: i32): ref_string; + /** Creates a new string from `array` using a strict WTF-8 decoder, decoding from `start` inclusive to `end` exclusive. */ + export function new_wtf8_array(array: ref_array, start: i32, end: i32): ref_string; + /** Creates a new string from memory assuming WTF-16 encoding, reading `codeunits` code units starting at `ptr`. `ptr` must be two-byte aligned. */ + export function new_wtf16(ptr: usize, codeunits: i32): ref_string; + /** Creates a new string from `array` assuming WTF-16 encoding, reading from `start` inclusive to `end` exclusive. */ + export function new_wtf16_array(array: ref_array, start: i32, end: i32): ref_string; + /** Creates a new string from the given `codepoint`. */ + export function from_code_point(codepoint: i32): ref_string; + /** Obtains an implementation-defined 32-bit hash value of `str`. */ + export function hash(str: ref_string): i32; + /** Measures the number of bytes required to encode `str` to UTF-8. Returns `-1` if the string contains an isolated surrogate. */ + export function measure_utf8(str: ref_string): i32; + /** Measures the number of bytes required to encode `str` to WTF-8. */ + export function measure_wtf8(str: ref_string): i32; + /** Measures the number of 16-bit code units required to encode `str` to WTF-16. */ + export function measure_wtf16(str: ref_string): i32; + /** Tests whether `str` is a sequence of Unicode scalar values, i.e. does not contain isolated surrogates. */ + export function is_usv_sequence(str: ref_string): bool; + /** Encodes `str` to memory at `ptr` using a strict UTF-8 encoder. */ + export function encode_utf8(str: ref_string, ptr: usize): i32; + /** Encodes `str` to `arr` at `start` using a strict UTF-8 encoder. */ + export function encode_utf8_array(str: ref_string, arr: ref_array, start: i32): i32; + // TODO: encode_lossy_utf8 + // TODO: encode_lossy_utf8_array + /** Encodes `str` to memory at `ptr` using a WTF-8 encoder. */ + export function encode_wtf8(str: ref_string, ptr: usize): i32; + /** Encodes `str` to `arr` at `start` using a WTF-8 encoder. */ + export function encode_wtf8_array(str: ref_string, arr: ref_array, start: i32): i32; + /** Encodes `str` to memory at `ptr` using a WTF-16 encoder. */ + export function encode_wtf16(str: ref_string, ptr: usize): i32; + /** Encodes `str` to `arr` at `start` using a WTF-16 encoder. */ + export function encode_wtf16_array(str: ref_string, arr: arrayref, start: i32): i32; + /** Concatenates `left` and `right` in this order. Traps if either operand is `null`. */ + export function concat(left: ref_string, right: ref_string): ref_string; + /** Tests whether `left` and `right` are equal, including if both are `null`. */ + export function eq(left: ref_string | null, right: ref_string | null): bool; + /** Compares the contents of `left` and `right`, returning `-1` if `left < right`, `0` if `left == right` or `1` if `left > right`. Traps if either operand is `null`. */ + export function compare(left: ref_string, right: ref_string): i32; + /** Obtains a WTF-8 view on `str`. */ + export function as_wtf8(str: ref_string): ref_stringview_wtf8; + /** Obtains a WTF-16 view on `str`. */ + export function as_wtf16(str: ref_string): ref_stringview_wtf16; + /** Obtains an iterator view on `str`. */ + export function as_iter(str: ref_string): ref_stringview_iter; +} + +declare namespace stringview_wtf8 { + /** Obtains the highest code point offset in `view` that is not greater than `pos + bytes`. If `pos` does not match the start of a code point, it is advanced to the next code point. */ + export function advance(view: ref_stringview_wtf8, pos: i32, bytes: i32): i32; + /** Returns a substring of `view` from `start` inclusive to `end` exclusive. If `start` or `end` do not match the start of a code point, these are advanced to the next code point. */ + export function slice(view: ref_stringview_wtf8, start: i32, end: i32): ref_string; + // TODO: encode_utf8 + // TODO: encode_lossy_utf8 + // TODO: encode_wtf8 +} + +declare namespace stringview_wtf16 { + /** Obtains the number of 16-bit code units in `view`. */ + export function length(view: ref_stringview_wtf16): i32; + /** Returns a substring of `view` from `start` inclusive to `end` exclusive. */ + export function slice(view: ref_stringview_wtf16, start: i32, end: i32): ref_string; + /** Obtains the 16-bit code unit at `pos` in `view`. Traps if `pos` is greater than or equal to the view's WTF-16 length. */ + export function get_codeunit(view: ref_stringview_wtf16, pos: i32): i32; + // TODO: encode +} + +declare namespace stringview_iter { + /** Obtains the code point at the iterator's current position, advancing the iterator by one code point. Returns `-1` if already at the end. */ + export function next(view: ref_stringview_iter): i32; + /** Advances the iterator by up to `count` code points, returning the number of code points consumed. */ + export function advance(view: ref_stringview_iter, count: i32): i32; + /** Rewinds the iterator by up to `count` code points, returning the number of coode points consumed. */ + export function rewind(view: ref_stringview_iter, count: i32): i32; + /** Returns a substring of `view`, starting at the current position for up to `count` code points. */ + export function slice(view: ref_stringview_iter, count: i32): ref_string; +} + /** Macro type evaluating to the underlying native WebAssembly type. */ declare type native = T; /** Special type evaluating the indexed access index type. */ diff --git a/std/assembly/reference.ts b/std/assembly/reference.ts index 148f456799..fb5f927597 100644 --- a/std/assembly/reference.ts +++ b/std/assembly/reference.ts @@ -7,9 +7,11 @@ export type i31ref = ref_i31 | null; export type structref = ref_struct | null; export type arrayref = ref_array | null; export type stringref = ref_string | null; -export type stringview_wtf8 = ref_stringview_wtf8 | null; -export type stringview_wtf16 = ref_stringview_wtf16 | null; -export type stringview_iter = ref_stringview_iter | null; + +// TODO: Conflict with the instruction namespaces +// export type stringview_wtf8 = ref_stringview_wtf8 | null; +// export type stringview_wtf16 = ref_stringview_wtf16 | null; +// export type stringview_iter = ref_stringview_iter | null; @unmanaged abstract class Ref { @@ -43,6 +45,142 @@ export abstract class RefStruct extends Ref { export abstract class RefArray extends Ref { } +import { E_INDEXOUTOFRANGE } from "util/error"; + @final @unmanaged export abstract class RefString extends Ref { + + @lazy static readonly MAX_LENGTH: i32 = (1 << 30) - 1; + + static fromCharCode(unit: i32, surr: i32 = -1): ref_string { + if (~surr) unit = 0x10000 + ((unit & 0x3FF) << 10) | (surr & 0x3FF); + return string.from_code_point(unit); + } + + static fromCodePoint(cp: i32): ref_string { + if (cp > 0x10ffff) throw new Error("Invalid code point"); + return string.from_code_point(cp); + } + + // @ts-ignore: this on getter + get length(this: ref_string): i32 { + return string.measure_wtf16(this); + } + + at(this: ref_string, pos: i32): stringref { + let len = string.measure_wtf16(this); + pos += select(0, len, pos >= 0); + if (pos >= len) throw new RangeError(E_INDEXOUTOFRANGE); + return string.from_code_point(stringview_wtf16.get_codeunit(string.as_wtf16(this), pos)); + } + + @operator("[]") charAt(this: ref_string, pos: i32): stringref { + if (pos >= string.measure_wtf16(this)) return ""; + return string.from_code_point(stringview_wtf16.get_codeunit(string.as_wtf16(this), pos)); + } + + charCodeAt(this: ref_string, pos: i32): i32 { + if (pos >= string.measure_wtf16(this)) return -1; // (NaN) + return stringview_wtf16.get_codeunit(string.as_wtf16(this), pos); + } + + codePointAt(this: ref_string, pos: i32): i32 { + let len = string.measure_wtf16(this); + if (pos >= len) return -1; // (undefined) + let view = string.as_wtf16(this); + let first = stringview_wtf16.get_codeunit(view, pos); + if ((first & 0xFC00) != 0xD800 || pos + 1 == len) return first; + let second = stringview_wtf16.get_codeunit(view, pos + 1); + if ((second & 0xFC00) != 0xDC00) return first; + return (first - 0xD800 << 10) + (second - 0xDC00) + 0x10000; + } + + @operator("+") + concat(this: ref_string, other: ref_string): ref_string { + return string.concat(this, other); + } + + endsWith(this: ref_string, search: ref_string, end: i32 = RefString.MAX_LENGTH): bool { + end = min(max(end, 0), string.measure_wtf16(this)); + let searchLength = string.measure_wtf16(search); + let searchStart = end - searchLength; + if (searchStart < 0) return false; + return string.eq( + stringview_wtf16.slice(string.as_wtf16(this), searchStart, searchStart + searchLength), + search + ); + } + + @operator("==") private static __eq(left: ref_string | null, right: ref_string | null): bool { + return string.eq(left, right); + } + + @operator.prefix("!") + private static __not(str: ref_string | null): bool { + return str == null; + } + + @operator("!=") + private static __ne(left: ref_string | null, right: ref_string | null): bool { + return !string.eq(left, right); + } + + @operator(">") private static __gt(left: ref_string, right: ref_string): bool { + return string.compare(left, right) > 0; + } + + @operator(">=") private static __gte(left: ref_string, right: ref_string): bool { + return string.compare(left, right) >= 0; + } + + @operator("<") private static __lt(left: ref_string, right: ref_string): bool { + return string.compare(left, right) < 0; + } + + @operator("<=") private static __lte(left: ref_string, right: ref_string): bool { + return string.compare(left, right) <= 0; + } + + includes(this: ref_string, search: ref_string, start: i32 = 0): bool { + return this.indexOf(search, start) != -1; + } + + indexOf(this: ref_string, search: ref_string, start: i32 = 0): i32 { + let searchLen = string.measure_wtf16(search); + if (!searchLen) return 0; + let len = string.measure_wtf16(this); + if (!len) return -1; + let searchStart = min(max(start, 0), len); + let view = string.as_wtf16(this); + for (len -= searchLen; searchStart <= len; ++searchStart) { + // FIXME: slice is suboptimal + if (string.eq( + stringview_wtf16.slice(view, searchStart, searchStart + searchLen), + search + )) { + return searchStart; + } + } + return -1; + } + + lastIndexOf(this: ref_string, search: ref_string, start: i32 = i32.MAX_VALUE): i32 { + let searchLen = string.measure_wtf16(search); + if (!searchLen) return string.measure_wtf16(this); + let len = string.measure_wtf16(this); + if (!len) return -1; + let searchStart = min(max(start, 0), len - searchLen); + for (; searchStart >= 0; --searchStart) { + // FIXME: slice is suboptimal + if (string.eq( + stringview_wtf16.slice(string.as_wtf16(this), searchStart, searchStart + searchLen), + search + )) { + return searchStart; + } + } + return -1; + } + + // TODO: port more } diff --git a/tests/compiler.js b/tests/compiler.js index 1095bc4e4c..fcf12ade97 100644 --- a/tests/compiler.js +++ b/tests/compiler.js @@ -410,7 +410,8 @@ async function testInstantiate(binaryBuffer, glue, stderr) { return new Date().getTimezoneOffset(); }, ...toEnv("Date", Date), - ...toEnv("Math", Math) + ...toEnv("Math", Math), + ...toEnv("console", console) }) }); diff --git a/tests/compiler/features/stringref.debug.wat b/tests/compiler/features/stringref.debug.wat new file mode 100644 index 0000000000..3795771c70 --- /dev/null +++ b/tests/compiler/features/stringref.debug.wat @@ -0,0 +1,1871 @@ +(module + (type $none_=>_none (func)) + (type $ref|string|_ref|string|_i32_=>_i32 (func (param (ref string) (ref string) i32) (result i32))) + (type $ref|string|_ref|string|_=>_i32 (func (param (ref string) (ref string)) (result i32))) + (type $ref|string|_i32_=>_stringref (func (param (ref string) i32) (result stringref))) + (type $ref|string|_i32_=>_i32 (func (param (ref string) i32) (result i32))) + (type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32))) + (type $i32_=>_ref|string| (func (param i32) (result (ref string)))) + (type $stringref_stringref_=>_i32 (func (param stringref stringref) (result i32))) + (type $ref|string|_=>_i32 (func (param (ref string)) (result i32))) + (type $ref|string|_ref|string|_=>_ref|string| (func (param (ref string) (ref string)) (result (ref string)))) + (type $stringref_=>_i32 (func (param stringref) (result i32))) + (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) + (global $features/stringref/stringGlobalNull (mut stringref) (ref.null none)) + (global $features/stringref/stringviewWtf8GlobalNull (mut stringview_wtf8) (ref.null none)) + (global $features/stringref/stringviewWtf16GlobalNull (mut stringview_wtf16) (ref.null none)) + (global $features/stringref/stringviewIterGlobalNull (mut stringview_iter) (ref.null none)) + (global $features/stringref/stringGlobal (mut (ref string)) (string.const "")) + (global $features/stringref/stringviewWtf8Global (mut stringview_wtf8) (ref.null none)) + (global $features/stringref/stringviewWtf16Global (mut stringview_wtf16) (ref.null none)) + (global $features/stringref/stringviewIterGlobal (mut stringview_iter) (ref.null none)) + (global $features/stringref/utf8_data i32 (i32.const 8)) + (global $features/stringref/wtf16_data i32 (i32.const 12)) + (global $features/stringref/temp_data i32 (i32.const 32)) + (global $~lib/native/ASC_SHRINK_LEVEL i32 (i32.const 0)) + (global $features/stringref/str (mut (ref string)) (string.const "abc")) + (global $~lib/reference/RefString.MAX_LENGTH i32 (i32.const 1073741823)) + (global $~argumentsLength (mut i32) (i32.const 0)) + (global $~lib/builtins/i32.MAX_VALUE i32 (i32.const 2147483647)) + (global $~lib/memory/__data_end i32 (i32.const 316)) + (global $~lib/memory/__stack_pointer (mut i32) (i32.const 33084)) + (global $~lib/memory/__heap_base i32 (i32.const 33084)) + (memory $0 1) + (data $0 (i32.const 8) "abc") + (data $1 (i32.const 12) "a\00b\00c\00") + (data $2 (i32.const 32) "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00") + (data $3 (i32.const 60) "<\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00*\00\00\00f\00e\00a\00t\00u\00r\00e\00s\00/\00s\00t\00r\00i\00n\00g\00r\00e\00f\00.\00t\00s\00\00\00") + (data $4 (i32.const 124) "<\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00$\00\00\00I\00n\00v\00a\00l\00i\00d\00 \00c\00o\00d\00e\00 \00p\00o\00i\00n\00t\00\00\00\00\00\00\00\00\00") + (data $5 (i32.const 188) "<\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00\"\00\00\00~\00l\00i\00b\00/\00r\00e\00f\00e\00r\00e\00n\00c\00e\00.\00t\00s\00\00\00\00\00\00\00\00\00\00\00") + (data $6 (i32.const 252) "<\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00$\00\00\00I\00n\00d\00e\00x\00 \00o\00u\00t\00 \00o\00f\00 \00r\00a\00n\00g\00e\00\00\00\00\00\00\00\00\00") + (table $0 1 1 funcref) + (elem $0 (i32.const 1)) + (export "memory" (memory $0)) + (start $~start) + (func $features/stringref/test_locals (type $none_=>_none) + (local $stringLocalNull stringref) + (local $stringviewWtf8LocalNull stringview_wtf8) + (local $stringviewWtf16LocalNull stringview_wtf16) + (local $stringviewIterLocalNull stringview_iter) + (local $stringLocal (ref string)) + (local $stringviewWtf8Local (ref stringview_wtf8)) + (local $stringviewWtf16Local (ref stringview_wtf16)) + (local $stringviewIterLocal (ref stringview_iter)) + ref.null none + local.set $stringLocalNull + ref.null none + local.set $stringLocalNull + ref.null none + local.set $stringviewWtf8LocalNull + ref.null none + local.set $stringviewWtf8LocalNull + ref.null none + local.set $stringviewWtf16LocalNull + ref.null none + local.set $stringviewWtf16LocalNull + ref.null none + local.set $stringviewIterLocalNull + ref.null none + local.set $stringviewIterLocalNull + string.const "" + local.set $stringLocal + local.get $stringLocal + string.as_wtf8 + local.set $stringviewWtf8Local + local.get $stringLocal + string.as_wtf16 + local.set $stringviewWtf16Local + local.get $stringLocal + string.as_iter + local.set $stringviewIterLocal + ) + (func $features/stringref/test_utf8 (type $none_=>_none) + nop + ) + (func $features/stringref/test_lossy_utf8 (type $none_=>_none) + nop + ) + (func $features/stringref/test_wtf8 (type $none_=>_none) + (local $str (ref string)) + (local $vl i32) + (local $vr i32) + (local $n i32) + (local $vl|4 i32) + (local $vr|5 i32) + (local $n|6 i32) + (local $a i32) + (local $b i32) + (local $9 i32) + (local $a|10 i32) + (local $b|11 i32) + (local $view (ref stringview_wtf8)) + global.get $features/stringref/utf8_data + i32.const 3 + string.new_wtf8 wtf8 + local.set $str + local.get $str + string.measure_wtf8 wtf8 + i32.const 3 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 60 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $str + string.hash + drop + local.get $str + string.is_usv_sequence + i32.const 0 + i32.ne + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 62 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $str + local.get $str + string.eq + i32.const 0 + i32.ne + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 63 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $str + string.const "abc" + string.eq + i32.const 0 + i32.ne + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 64 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $str + local.get $str + string.compare + i32.const 0 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 65 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $str + string.const "b" + string.compare + i32.const -1 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 66 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $str + string.const "`" + string.compare + i32.const 1 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 67 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $str + global.get $features/stringref/temp_data + string.encode_wtf8 wtf8 + i32.const 3 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 68 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + block $~lib/memory/memory.compare|inlined.0 (result i32) + global.get $features/stringref/utf8_data + local.set $vl + global.get $features/stringref/temp_data + local.set $vr + i32.const 3 + local.set $n + block $~lib/util/memory/memcmp|inlined.0 (result i32) + local.get $vl + local.set $vl|4 + local.get $vr + local.set $vr|5 + local.get $n + local.set $n|6 + local.get $vl|4 + local.get $vr|5 + i32.eq + if + i32.const 0 + br $~lib/util/memory/memcmp|inlined.0 + end + i32.const 0 + i32.const 2 + i32.lt_s + drop + local.get $vl|4 + i32.const 7 + i32.and + local.get $vr|5 + i32.const 7 + i32.and + i32.eq + if + loop $while-continue|0 + local.get $vl|4 + i32.const 7 + i32.and + if + local.get $n|6 + i32.eqz + if + i32.const 0 + br $~lib/util/memory/memcmp|inlined.0 + end + local.get $vl|4 + i32.load8_u $0 + local.set $a + local.get $vr|5 + i32.load8_u $0 + local.set $b + local.get $a + local.get $b + i32.ne + if + local.get $a + local.get $b + i32.sub + br $~lib/util/memory/memcmp|inlined.0 + end + local.get $n|6 + i32.const 1 + i32.sub + local.set $n|6 + local.get $vl|4 + i32.const 1 + i32.add + local.set $vl|4 + local.get $vr|5 + i32.const 1 + i32.add + local.set $vr|5 + br $while-continue|0 + end + end + block $while-break|1 + loop $while-continue|1 + local.get $n|6 + i32.const 8 + i32.ge_u + if + local.get $vl|4 + i64.load $0 + local.get $vr|5 + i64.load $0 + i64.ne + if + br $while-break|1 + end + local.get $vl|4 + i32.const 8 + i32.add + local.set $vl|4 + local.get $vr|5 + i32.const 8 + i32.add + local.set $vr|5 + local.get $n|6 + i32.const 8 + i32.sub + local.set $n|6 + br $while-continue|1 + end + end + end + end + loop $while-continue|2 + local.get $n|6 + local.tee $9 + i32.const 1 + i32.sub + local.set $n|6 + local.get $9 + if + local.get $vl|4 + i32.load8_u $0 + local.set $a|10 + local.get $vr|5 + i32.load8_u $0 + local.set $b|11 + local.get $a|10 + local.get $b|11 + i32.ne + if + local.get $a|10 + local.get $b|11 + i32.sub + br $~lib/util/memory/memcmp|inlined.0 + end + local.get $vl|4 + i32.const 1 + i32.add + local.set $vl|4 + local.get $vr|5 + i32.const 1 + i32.add + local.set $vr|5 + br $while-continue|2 + end + end + i32.const 0 + br $~lib/util/memory/memcmp|inlined.0 + end + br $~lib/memory/memory.compare|inlined.0 + end + i32.const 0 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 69 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $str + string.as_wtf8 + local.set $view + local.get $view + i32.const 0 + i32.const 0 + stringview_wtf8.advance + i32.const 0 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 72 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $view + i32.const 0 + i32.const 1 + stringview_wtf8.advance + i32.const 1 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 73 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $view + i32.const 0 + i32.const 2 + stringview_wtf8.advance + i32.const 2 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 74 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $view + i32.const 0 + i32.const 3 + stringview_wtf8.advance + i32.const 3 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 75 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $view + i32.const 0 + i32.const 4 + stringview_wtf8.advance + i32.const 3 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 76 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $view + i32.const -1 + i32.const 0 + stringview_wtf8.advance + i32.const 3 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 77 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $str + local.get $view + i32.const 0 + i32.const 3 + stringview_wtf8.slice + string.eq + i32.const 0 + i32.ne + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 78 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + ) + (func $features/stringref/test_wtf16 (type $none_=>_none) + (local $str (ref string)) + (local $vl i32) + (local $vr i32) + (local $n i32) + (local $vl|4 i32) + (local $vr|5 i32) + (local $n|6 i32) + (local $a i32) + (local $b i32) + (local $9 i32) + (local $a|10 i32) + (local $b|11 i32) + (local $view (ref stringview_wtf16)) + global.get $features/stringref/wtf16_data + i32.const 3 + string.new_wtf16 + local.set $str + local.get $str + string.measure_wtf16 + i32.const 3 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 86 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $str + string.hash + drop + local.get $str + string.is_usv_sequence + i32.const 0 + i32.ne + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 88 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $str + local.get $str + string.eq + i32.const 0 + i32.ne + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 89 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $str + string.const "abc" + string.eq + i32.const 0 + i32.ne + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 90 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $str + local.get $str + string.compare + i32.const 0 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 91 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $str + string.const "b" + string.compare + i32.const -1 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 92 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $str + string.const "`" + string.compare + i32.const 1 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 93 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $str + global.get $features/stringref/temp_data + string.encode_wtf16 + i32.const 3 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 94 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + block $~lib/memory/memory.compare|inlined.1 (result i32) + global.get $features/stringref/wtf16_data + local.set $vl + global.get $features/stringref/temp_data + local.set $vr + i32.const 6 + local.set $n + block $~lib/util/memory/memcmp|inlined.1 (result i32) + local.get $vl + local.set $vl|4 + local.get $vr + local.set $vr|5 + local.get $n + local.set $n|6 + local.get $vl|4 + local.get $vr|5 + i32.eq + if + i32.const 0 + br $~lib/util/memory/memcmp|inlined.1 + end + i32.const 0 + i32.const 2 + i32.lt_s + drop + local.get $vl|4 + i32.const 7 + i32.and + local.get $vr|5 + i32.const 7 + i32.and + i32.eq + if + loop $while-continue|0 + local.get $vl|4 + i32.const 7 + i32.and + if + local.get $n|6 + i32.eqz + if + i32.const 0 + br $~lib/util/memory/memcmp|inlined.1 + end + local.get $vl|4 + i32.load8_u $0 + local.set $a + local.get $vr|5 + i32.load8_u $0 + local.set $b + local.get $a + local.get $b + i32.ne + if + local.get $a + local.get $b + i32.sub + br $~lib/util/memory/memcmp|inlined.1 + end + local.get $n|6 + i32.const 1 + i32.sub + local.set $n|6 + local.get $vl|4 + i32.const 1 + i32.add + local.set $vl|4 + local.get $vr|5 + i32.const 1 + i32.add + local.set $vr|5 + br $while-continue|0 + end + end + block $while-break|1 + loop $while-continue|1 + local.get $n|6 + i32.const 8 + i32.ge_u + if + local.get $vl|4 + i64.load $0 + local.get $vr|5 + i64.load $0 + i64.ne + if + br $while-break|1 + end + local.get $vl|4 + i32.const 8 + i32.add + local.set $vl|4 + local.get $vr|5 + i32.const 8 + i32.add + local.set $vr|5 + local.get $n|6 + i32.const 8 + i32.sub + local.set $n|6 + br $while-continue|1 + end + end + end + end + loop $while-continue|2 + local.get $n|6 + local.tee $9 + i32.const 1 + i32.sub + local.set $n|6 + local.get $9 + if + local.get $vl|4 + i32.load8_u $0 + local.set $a|10 + local.get $vr|5 + i32.load8_u $0 + local.set $b|11 + local.get $a|10 + local.get $b|11 + i32.ne + if + local.get $a|10 + local.get $b|11 + i32.sub + br $~lib/util/memory/memcmp|inlined.1 + end + local.get $vl|4 + i32.const 1 + i32.add + local.set $vl|4 + local.get $vr|5 + i32.const 1 + i32.add + local.set $vr|5 + br $while-continue|2 + end + end + i32.const 0 + br $~lib/util/memory/memcmp|inlined.1 + end + br $~lib/memory/memory.compare|inlined.1 + end + i32.const 0 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 95 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $str + string.as_wtf16 + local.set $view + local.get $view + stringview_wtf16.length + i32.const 3 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 98 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $str + local.get $view + i32.const 0 + i32.const 3 + stringview_wtf16.slice + string.eq + i32.const 0 + i32.ne + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 99 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $view + i32.const 0 + stringview_wtf16.get_codeunit + i32.const 97 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 100 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $view + i32.const 1 + stringview_wtf16.get_codeunit + i32.const 98 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 101 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $view + i32.const 2 + stringview_wtf16.get_codeunit + i32.const 99 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 102 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + ) + (func $features/stringref/test_iter (type $none_=>_none) + (local $str (ref string)) + (local $view (ref stringview_iter)) + global.get $features/stringref/wtf16_data + i32.const 3 + string.new_wtf16 + local.set $str + local.get $str + string.as_iter + local.set $view + local.get $view + stringview_iter.next + i32.const 97 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 111 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $view + stringview_iter.next + i32.const 98 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 112 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $view + stringview_iter.next + i32.const 99 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 113 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $view + stringview_iter.next + i32.const -1 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 114 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $view + i32.const 1 + stringview_iter.rewind + i32.const 1 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 115 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $view + stringview_iter.next + i32.const 99 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 116 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $view + stringview_iter.next + i32.const -1 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 117 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $view + i32.const -1 + stringview_iter.rewind + i32.const 3 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 118 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $view + i32.const 2 + stringview_iter.advance + i32.const 2 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 119 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $view + stringview_iter.next + i32.const 99 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 120 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $view + stringview_iter.next + i32.const -1 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 121 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $view + i32.const -1 + stringview_iter.rewind + i32.const 3 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 122 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $view + i32.const 1 + stringview_iter.slice + i32.const 97 + string.from_code_point + string.eq + i32.const 0 + i32.ne + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 123 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $view + i32.const 2 + stringview_iter.slice + i32.const 97 + string.from_code_point + i32.const 98 + string.from_code_point + string.concat + string.eq + i32.const 0 + i32.ne + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 124 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $view + i32.const 3 + stringview_iter.slice + local.get $str + string.eq + i32.const 0 + i32.ne + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 125 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $view + i32.const -1 + stringview_iter.slice + local.get $str + string.eq + i32.const 0 + i32.ne + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 126 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $view + stringview_iter.next + drop + local.get $view + i32.const 1 + stringview_iter.slice + i32.const 98 + string.from_code_point + string.eq + i32.const 0 + i32.ne + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 128 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $view + i32.const 2 + stringview_iter.slice + i32.const 98 + string.from_code_point + i32.const 99 + string.from_code_point + string.concat + string.eq + i32.const 0 + i32.ne + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 129 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $view + i32.const -1 + stringview_iter.slice + i32.const 98 + string.from_code_point + i32.const 99 + string.from_code_point + string.concat + string.eq + i32.const 0 + i32.ne + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 130 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + ) + (func $~lib/reference/RefString.fromCodePoint (type $i32_=>_ref|string|) (param $cp i32) (result (ref string)) + local.get $cp + i32.const 1114111 + i32.gt_u + if + i32.const 144 + i32.const 208 + i32.const 61 + i32.const 29 + call $~lib/builtins/abort + unreachable + end + local.get $cp + string.from_code_point + return + ) + (func $~lib/reference/RefString.__eq (type $stringref_stringref_=>_i32) (param $left stringref) (param $right stringref) (result i32) + local.get $left + local.get $right + string.eq + return + ) + (func $~lib/reference/RefString#get:length (type $ref|string|_=>_i32) (param $this (ref string)) (result i32) + local.get $this + string.measure_wtf16 + return + ) + (func $~lib/reference/RefString#at (type $ref|string|_i32_=>_stringref) (param $this (ref string)) (param $pos i32) (result stringref) + (local $len i32) + local.get $this + string.measure_wtf16 + local.set $len + local.get $pos + i32.const 0 + local.get $len + local.get $pos + i32.const 0 + i32.ge_s + select + i32.add + local.set $pos + local.get $pos + local.get $len + i32.ge_u + if + i32.const 272 + i32.const 208 + i32.const 73 + i32.const 31 + call $~lib/builtins/abort + unreachable + end + local.get $this + string.as_wtf16 + local.get $pos + stringview_wtf16.get_codeunit + string.from_code_point + return + ) + (func $~lib/reference/RefString#charAt (type $ref|string|_i32_=>_stringref) (param $this (ref string)) (param $pos i32) (result stringref) + local.get $pos + local.get $this + string.measure_wtf16 + i32.ge_u + if + string.const "" + return + end + local.get $this + string.as_wtf16 + local.get $pos + stringview_wtf16.get_codeunit + string.from_code_point + return + ) + (func $~lib/reference/RefString#charCodeAt (type $ref|string|_i32_=>_i32) (param $this (ref string)) (param $pos i32) (result i32) + local.get $pos + local.get $this + string.measure_wtf16 + i32.ge_u + if + i32.const -1 + return + end + local.get $this + string.as_wtf16 + local.get $pos + stringview_wtf16.get_codeunit + return + ) + (func $~lib/reference/RefString#codePointAt (type $ref|string|_i32_=>_i32) (param $this (ref string)) (param $pos i32) (result i32) + (local $len i32) + (local $view (ref stringview_wtf16)) + (local $first i32) + (local $second i32) + local.get $this + string.measure_wtf16 + local.set $len + local.get $pos + local.get $len + i32.ge_u + if + i32.const -1 + return + end + local.get $this + string.as_wtf16 + local.set $view + local.get $view + local.get $pos + stringview_wtf16.get_codeunit + local.set $first + local.get $first + i32.const 64512 + i32.and + i32.const 55296 + i32.ne + if (result i32) + i32.const 1 + else + local.get $pos + i32.const 1 + i32.add + local.get $len + i32.eq + end + if + local.get $first + return + end + local.get $view + local.get $pos + i32.const 1 + i32.add + stringview_wtf16.get_codeunit + local.set $second + local.get $second + i32.const 64512 + i32.and + i32.const 56320 + i32.ne + if + local.get $first + return + end + local.get $first + i32.const 55296 + i32.sub + i32.const 10 + i32.shl + local.get $second + i32.const 56320 + i32.sub + i32.add + i32.const 65536 + i32.add + return + ) + (func $~lib/reference/RefString#concat (type $ref|string|_ref|string|_=>_ref|string|) (param $this (ref string)) (param $other (ref string)) (result (ref string)) + local.get $this + local.get $other + string.concat + return + ) + (func $~lib/reference/RefString#endsWith (type $ref|string|_ref|string|_i32_=>_i32) (param $this (ref string)) (param $search (ref string)) (param $end i32) (result i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local $6 i32) + (local $searchLength i32) + (local $searchStart i32) + local.get $end + local.tee $3 + i32.const 0 + local.tee $4 + local.get $3 + local.get $4 + i32.gt_s + select + local.tee $5 + local.get $this + string.measure_wtf16 + local.tee $6 + local.get $5 + local.get $6 + i32.lt_s + select + local.set $end + local.get $search + string.measure_wtf16 + local.set $searchLength + local.get $end + local.get $searchLength + i32.sub + local.set $searchStart + local.get $searchStart + i32.const 0 + i32.lt_s + if + i32.const 0 + return + end + local.get $this + string.as_wtf16 + local.get $searchStart + local.get $searchStart + local.get $searchLength + i32.add + stringview_wtf16.slice + local.get $search + string.eq + return + ) + (func $~lib/reference/RefString#endsWith@varargs (type $ref|string|_ref|string|_i32_=>_i32) (param $this (ref string)) (param $search (ref string)) (param $end i32) (result i32) + block $1of1 + block $0of1 + block $outOfRange + global.get $~argumentsLength + i32.const 1 + i32.sub + br_table $0of1 $1of1 $outOfRange + end + unreachable + end + global.get $~lib/reference/RefString.MAX_LENGTH + local.set $end + end + local.get $this + local.get $search + local.get $end + call $~lib/reference/RefString#endsWith + ) + (func $~lib/reference/RefString.__not (type $stringref_=>_i32) (param $str stringref) (result i32) + local.get $str + ref.null none + string.eq + return + ) + (func $~lib/reference/RefString.__gt (type $ref|string|_ref|string|_=>_i32) (param $left (ref string)) (param $right (ref string)) (result i32) + local.get $left + local.get $right + string.compare + i32.const 0 + i32.gt_s + return + ) + (func $~lib/reference/RefString.__gte (type $ref|string|_ref|string|_=>_i32) (param $left (ref string)) (param $right (ref string)) (result i32) + local.get $left + local.get $right + string.compare + i32.const 0 + i32.ge_s + return + ) + (func $~lib/reference/RefString.__lt (type $ref|string|_ref|string|_=>_i32) (param $left (ref string)) (param $right (ref string)) (result i32) + local.get $left + local.get $right + string.compare + i32.const 0 + i32.lt_s + return + ) + (func $~lib/reference/RefString.__lte (type $ref|string|_ref|string|_=>_i32) (param $left (ref string)) (param $right (ref string)) (result i32) + local.get $left + local.get $right + string.compare + i32.const 0 + i32.le_s + return + ) + (func $~lib/reference/RefString#indexOf (type $ref|string|_ref|string|_i32_=>_i32) (param $this (ref string)) (param $search (ref string)) (param $start i32) (result i32) + (local $searchLen i32) + (local $len i32) + (local $5 i32) + (local $6 i32) + (local $7 i32) + (local $8 i32) + (local $searchStart i32) + (local $view (ref stringview_wtf16)) + local.get $search + string.measure_wtf16 + local.set $searchLen + local.get $searchLen + i32.eqz + if + i32.const 0 + return + end + local.get $this + string.measure_wtf16 + local.set $len + local.get $len + i32.eqz + if + i32.const -1 + return + end + local.get $start + local.tee $5 + i32.const 0 + local.tee $6 + local.get $5 + local.get $6 + i32.gt_s + select + local.tee $7 + local.get $len + local.tee $8 + local.get $7 + local.get $8 + i32.lt_s + select + local.set $searchStart + local.get $this + string.as_wtf16 + local.set $view + local.get $len + local.get $searchLen + i32.sub + local.set $len + loop $for-loop|0 + local.get $searchStart + local.get $len + i32.le_s + if + local.get $view + local.get $searchStart + local.get $searchStart + local.get $searchLen + i32.add + stringview_wtf16.slice + local.get $search + string.eq + if + local.get $searchStart + return + end + local.get $searchStart + i32.const 1 + i32.add + local.set $searchStart + br $for-loop|0 + end + end + i32.const -1 + return + ) + (func $~lib/reference/RefString#includes (type $ref|string|_ref|string|_i32_=>_i32) (param $this (ref string)) (param $search (ref string)) (param $start i32) (result i32) + local.get $this + local.get $search + local.get $start + call $~lib/reference/RefString#indexOf + i32.const -1 + i32.ne + return + ) + (func $~lib/reference/RefString#lastIndexOf (type $ref|string|_ref|string|_i32_=>_i32) (param $this (ref string)) (param $search (ref string)) (param $start i32) (result i32) + (local $searchLen i32) + (local $len i32) + (local $5 i32) + (local $6 i32) + (local $7 i32) + (local $8 i32) + (local $searchStart i32) + local.get $search + string.measure_wtf16 + local.set $searchLen + local.get $searchLen + i32.eqz + if + local.get $this + string.measure_wtf16 + return + end + local.get $this + string.measure_wtf16 + local.set $len + local.get $len + i32.eqz + if + i32.const -1 + return + end + local.get $start + local.tee $5 + i32.const 0 + local.tee $6 + local.get $5 + local.get $6 + i32.gt_s + select + local.tee $7 + local.get $len + local.get $searchLen + i32.sub + local.tee $8 + local.get $7 + local.get $8 + i32.lt_s + select + local.set $searchStart + loop $for-loop|0 + local.get $searchStart + i32.const 0 + i32.ge_s + if + local.get $this + string.as_wtf16 + local.get $searchStart + local.get $searchStart + local.get $searchLen + i32.add + stringview_wtf16.slice + local.get $search + string.eq + if + local.get $searchStart + return + end + local.get $searchStart + i32.const 1 + i32.sub + local.set $searchStart + br $for-loop|0 + end + end + i32.const -1 + return + ) + (func $~lib/reference/RefString#lastIndexOf@varargs (type $ref|string|_ref|string|_i32_=>_i32) (param $this (ref string)) (param $search (ref string)) (param $start i32) (result i32) + block $1of1 + block $0of1 + block $outOfRange + global.get $~argumentsLength + i32.const 1 + i32.sub + br_table $0of1 $1of1 $outOfRange + end + unreachable + end + global.get $~lib/builtins/i32.MAX_VALUE + local.set $start + end + local.get $this + local.get $search + local.get $start + call $~lib/reference/RefString#lastIndexOf + ) + (func $start:features/stringref (type $none_=>_none) + global.get $features/stringref/stringGlobal + string.as_wtf8 + global.set $features/stringref/stringviewWtf8Global + global.get $features/stringref/stringGlobal + string.as_wtf16 + global.set $features/stringref/stringviewWtf16Global + global.get $features/stringref/stringGlobal + string.as_iter + global.set $features/stringref/stringviewIterGlobal + call $features/stringref/test_locals + call $features/stringref/test_utf8 + call $features/stringref/test_lossy_utf8 + call $features/stringref/test_wtf8 + call $features/stringref/test_wtf16 + call $features/stringref/test_iter + i32.const 97 + call $~lib/reference/RefString.fromCodePoint + string.const "a" + call $~lib/reference/RefString.__eq + i32.const 0 + i32.ne + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 145 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $features/stringref/str + call $~lib/reference/RefString#get:length + i32.const 3 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 147 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $features/stringref/str + i32.const 1 + call $~lib/reference/RefString#at + string.const "b" + string.eq + i32.const 0 + i32.ne + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 148 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $features/stringref/str + i32.const 0 + call $~lib/reference/RefString#charAt + string.const "a" + string.eq + i32.const 0 + i32.ne + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 149 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $features/stringref/str + i32.const 0 + call $~lib/reference/RefString#charCodeAt + i32.const 97 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 150 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $features/stringref/str + i32.const 0 + call $~lib/reference/RefString#codePointAt + i32.const 97 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 151 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $features/stringref/str + global.get $features/stringref/str + call $~lib/reference/RefString#concat + string.const "abcabc" + call $~lib/reference/RefString.__eq + i32.const 0 + i32.ne + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 152 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $features/stringref/str + string.const "abc" + i32.const 1 + global.set $~argumentsLength + i32.const 0 + call $~lib/reference/RefString#endsWith@varargs + i32.const 0 + i32.ne + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 153 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $features/stringref/str + global.get $features/stringref/str + call $~lib/reference/RefString.__eq + i32.const 0 + i32.ne + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 154 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $features/stringref/str + call $~lib/reference/RefString.__not + i32.const 0 + i32.ne + i32.const 0 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 155 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $features/stringref/str + string.const "" + string.eq + i32.eqz + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 156 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $features/stringref/str + string.const "ab" + call $~lib/reference/RefString.__gt + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 157 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $features/stringref/str + string.const "ab" + call $~lib/reference/RefString.__gte + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 158 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $features/stringref/str + string.const "abcd" + call $~lib/reference/RefString.__lt + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 159 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $features/stringref/str + string.const "abcd" + call $~lib/reference/RefString.__lte + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 160 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $features/stringref/str + string.const "b" + i32.const 0 + call $~lib/reference/RefString#includes + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 161 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $features/stringref/str + string.const "b" + i32.const 0 + call $~lib/reference/RefString#indexOf + i32.const 1 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 162 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $features/stringref/str + string.const "b" + i32.const 1 + global.set $~argumentsLength + i32.const 0 + call $~lib/reference/RefString#lastIndexOf@varargs + i32.const 1 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 80 + i32.const 163 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + ) + (func $~start (type $none_=>_none) + call $start:features/stringref + ) +) diff --git a/tests/compiler/features/stringref.json b/tests/compiler/features/stringref.json new file mode 100644 index 0000000000..4cbf3f3730 --- /dev/null +++ b/tests/compiler/features/stringref.json @@ -0,0 +1,7 @@ +{ + "asc_flags": [ + ], + "features": [ + "stringref" + ] +} diff --git a/tests/compiler/features/stringref.release.wat b/tests/compiler/features/stringref.release.wat new file mode 100644 index 0000000000..89f7aeedb0 --- /dev/null +++ b/tests/compiler/features/stringref.release.wat @@ -0,0 +1,1336 @@ +(module + (type $none_=>_none (func)) + (type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32))) + (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) + (memory $0 1) + (data $0 (i32.const 1024) "abc") + (data $1 (i32.const 1028) "a\00b\00c") + (data $3 (i32.const 1068) "<") + (data $3.1 (i32.const 1080) "\02\00\00\00*\00\00\00f\00e\00a\00t\00u\00r\00e\00s\00/\00s\00t\00r\00i\00n\00g\00r\00e\00f\00.\00t\00s") + (data $4 (i32.const 1132) "<") + (data $4.1 (i32.const 1144) "\02\00\00\00$\00\00\00I\00n\00v\00a\00l\00i\00d\00 \00c\00o\00d\00e\00 \00p\00o\00i\00n\00t") + (data $5 (i32.const 1196) "<") + (data $5.1 (i32.const 1208) "\02\00\00\00\"\00\00\00~\00l\00i\00b\00/\00r\00e\00f\00e\00r\00e\00n\00c\00e\00.\00t\00s") + (data $6 (i32.const 1260) "<") + (data $6.1 (i32.const 1272) "\02\00\00\00$\00\00\00I\00n\00d\00e\00x\00 \00o\00u\00t\00 \00o\00f\00 \00r\00a\00n\00g\00e") + (export "memory" (memory $0)) + (start $~start) + (func $features/stringref/test_wtf8 (type $none_=>_none) + (local $0 i32) + (local $1 i32) + (local $2 i32) + (local $3 (ref stringview_wtf8)) + (local $4 i32) + (local $5 i32) + (local $6 (ref string)) + (local $7 i32) + i32.const 1024 + i32.const 3 + string.new_wtf8 wtf8 + local.tee $6 + string.measure_wtf8 wtf8 + i32.const 3 + i32.ne + if + i32.const 0 + i32.const 1088 + i32.const 60 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $6 + string.hash + drop + local.get $6 + string.is_usv_sequence + i32.eqz + if + i32.const 0 + i32.const 1088 + i32.const 62 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $6 + local.get $6 + string.eq + i32.eqz + if + i32.const 0 + i32.const 1088 + i32.const 63 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $6 + string.const "abc" + string.eq + i32.eqz + if + i32.const 0 + i32.const 1088 + i32.const 64 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $6 + local.get $6 + string.compare + if + i32.const 0 + i32.const 1088 + i32.const 65 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $6 + string.const "b" + string.compare + i32.const -1 + i32.ne + if + i32.const 0 + i32.const 1088 + i32.const 66 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $6 + string.const "`" + string.compare + i32.const 1 + i32.ne + if + i32.const 0 + i32.const 1088 + i32.const 67 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $6 + i32.const 1040 + string.encode_wtf8 wtf8 + i32.const 3 + i32.ne + if + i32.const 0 + i32.const 1088 + i32.const 68 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + i32.const 3 + local.set $1 + i32.const 1024 + local.set $5 + i32.const 1040 + local.set $4 + block $~lib/util/memory/memcmp|inlined.0 + loop $while-continue|0 + local.get $5 + i32.const 7 + i32.and + if + i32.const 0 + local.set $7 + local.get $1 + i32.eqz + br_if $~lib/util/memory/memcmp|inlined.0 + local.get $5 + i32.load8_u $0 + local.tee $2 + local.get $4 + i32.load8_u $0 + local.tee $0 + i32.sub + local.set $7 + local.get $0 + local.get $2 + i32.ne + br_if $~lib/util/memory/memcmp|inlined.0 + local.get $1 + i32.const 1 + i32.sub + local.set $1 + local.get $5 + i32.const 1 + i32.add + local.set $5 + local.get $4 + i32.const 1 + i32.add + local.set $4 + br $while-continue|0 + end + end + loop $while-continue|1 + local.get $1 + i32.const 8 + i32.ge_u + if + local.get $5 + i64.load $0 + local.get $4 + i64.load $0 + i64.eq + if + local.get $5 + i32.const 8 + i32.add + local.set $5 + local.get $4 + i32.const 8 + i32.add + local.set $4 + local.get $1 + i32.const 8 + i32.sub + local.set $1 + br $while-continue|1 + end + end + end + loop $while-continue|2 + local.get $1 + local.tee $0 + i32.const 1 + i32.sub + local.set $1 + local.get $0 + if + local.get $5 + i32.load8_u $0 + local.tee $2 + local.get $4 + i32.load8_u $0 + local.tee $0 + i32.sub + local.set $7 + local.get $0 + local.get $2 + i32.ne + br_if $~lib/util/memory/memcmp|inlined.0 + local.get $5 + i32.const 1 + i32.add + local.set $5 + local.get $4 + i32.const 1 + i32.add + local.set $4 + br $while-continue|2 + end + end + i32.const 0 + local.set $7 + end + local.get $7 + if + i32.const 0 + i32.const 1088 + i32.const 69 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $6 + string.as_wtf8 + local.tee $3 + i32.const 0 + i32.const 0 + stringview_wtf8.advance + if + i32.const 0 + i32.const 1088 + i32.const 72 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $3 + i32.const 0 + i32.const 1 + stringview_wtf8.advance + i32.const 1 + i32.ne + if + i32.const 0 + i32.const 1088 + i32.const 73 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $3 + i32.const 0 + i32.const 2 + stringview_wtf8.advance + i32.const 2 + i32.ne + if + i32.const 0 + i32.const 1088 + i32.const 74 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $3 + i32.const 0 + i32.const 3 + stringview_wtf8.advance + i32.const 3 + i32.ne + if + i32.const 0 + i32.const 1088 + i32.const 75 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $3 + i32.const 0 + i32.const 4 + stringview_wtf8.advance + i32.const 3 + i32.ne + if + i32.const 0 + i32.const 1088 + i32.const 76 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $3 + i32.const -1 + i32.const 0 + stringview_wtf8.advance + i32.const 3 + i32.ne + if + i32.const 0 + i32.const 1088 + i32.const 77 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $6 + local.get $3 + i32.const 0 + i32.const 3 + stringview_wtf8.slice + string.eq + i32.eqz + if + i32.const 0 + i32.const 1088 + i32.const 78 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + ) + (func $features/stringref/test_wtf16 (type $none_=>_none) + (local $0 i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 (ref stringview_wtf16)) + (local $6 (ref string)) + (local $7 i32) + i32.const 1028 + i32.const 3 + string.new_wtf16 + local.tee $6 + string.measure_wtf16 + i32.const 3 + i32.ne + if + i32.const 0 + i32.const 1088 + i32.const 86 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $6 + string.hash + drop + local.get $6 + string.is_usv_sequence + i32.eqz + if + i32.const 0 + i32.const 1088 + i32.const 88 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $6 + local.get $6 + string.eq + i32.eqz + if + i32.const 0 + i32.const 1088 + i32.const 89 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $6 + string.const "abc" + string.eq + i32.eqz + if + i32.const 0 + i32.const 1088 + i32.const 90 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $6 + local.get $6 + string.compare + if + i32.const 0 + i32.const 1088 + i32.const 91 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $6 + string.const "b" + string.compare + i32.const -1 + i32.ne + if + i32.const 0 + i32.const 1088 + i32.const 92 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $6 + string.const "`" + string.compare + i32.const 1 + i32.ne + if + i32.const 0 + i32.const 1088 + i32.const 93 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $6 + i32.const 1040 + string.encode_wtf16 + i32.const 3 + i32.ne + if + i32.const 0 + i32.const 1088 + i32.const 94 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + i32.const 6 + local.set $1 + i32.const 1028 + local.set $4 + i32.const 1040 + local.set $3 + block $~lib/util/memory/memcmp|inlined.1 + loop $while-continue|2 + local.get $1 + local.tee $0 + i32.const 1 + i32.sub + local.set $1 + local.get $0 + if + local.get $4 + i32.load8_u $0 + local.tee $2 + local.get $3 + i32.load8_u $0 + local.tee $0 + i32.sub + local.set $7 + local.get $0 + local.get $2 + i32.ne + br_if $~lib/util/memory/memcmp|inlined.1 + local.get $4 + i32.const 1 + i32.add + local.set $4 + local.get $3 + i32.const 1 + i32.add + local.set $3 + br $while-continue|2 + end + end + i32.const 0 + local.set $7 + end + local.get $7 + if + i32.const 0 + i32.const 1088 + i32.const 95 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $6 + string.as_wtf16 + local.tee $5 + stringview_wtf16.length + i32.const 3 + i32.ne + if + i32.const 0 + i32.const 1088 + i32.const 98 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $6 + local.get $5 + i32.const 0 + i32.const 3 + stringview_wtf16.slice + string.eq + i32.eqz + if + i32.const 0 + i32.const 1088 + i32.const 99 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $5 + i32.const 0 + stringview_wtf16.get_codeunit + i32.const 97 + i32.ne + if + i32.const 0 + i32.const 1088 + i32.const 100 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $5 + i32.const 1 + stringview_wtf16.get_codeunit + i32.const 98 + i32.ne + if + i32.const 0 + i32.const 1088 + i32.const 101 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $5 + i32.const 2 + stringview_wtf16.get_codeunit + i32.const 99 + i32.ne + if + i32.const 0 + i32.const 1088 + i32.const 102 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + ) + (func $features/stringref/test_iter (type $none_=>_none) + (local $0 (ref stringview_iter)) + (local $1 (ref string)) + i32.const 1028 + i32.const 3 + string.new_wtf16 + local.tee $1 + string.as_iter + local.tee $0 + stringview_iter.next + i32.const 97 + i32.ne + if + i32.const 0 + i32.const 1088 + i32.const 111 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $0 + stringview_iter.next + i32.const 98 + i32.ne + if + i32.const 0 + i32.const 1088 + i32.const 112 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $0 + stringview_iter.next + i32.const 99 + i32.ne + if + i32.const 0 + i32.const 1088 + i32.const 113 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $0 + stringview_iter.next + i32.const -1 + i32.ne + if + i32.const 0 + i32.const 1088 + i32.const 114 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.const 1 + stringview_iter.rewind + i32.const 1 + i32.ne + if + i32.const 0 + i32.const 1088 + i32.const 115 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $0 + stringview_iter.next + i32.const 99 + i32.ne + if + i32.const 0 + i32.const 1088 + i32.const 116 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $0 + stringview_iter.next + i32.const -1 + i32.ne + if + i32.const 0 + i32.const 1088 + i32.const 117 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.const -1 + stringview_iter.rewind + i32.const 3 + i32.ne + if + i32.const 0 + i32.const 1088 + i32.const 118 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.const 2 + stringview_iter.advance + i32.const 2 + i32.ne + if + i32.const 0 + i32.const 1088 + i32.const 119 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $0 + stringview_iter.next + i32.const 99 + i32.ne + if + i32.const 0 + i32.const 1088 + i32.const 120 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $0 + stringview_iter.next + i32.const -1 + i32.ne + if + i32.const 0 + i32.const 1088 + i32.const 121 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.const -1 + stringview_iter.rewind + i32.const 3 + i32.ne + if + i32.const 0 + i32.const 1088 + i32.const 122 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.const 1 + stringview_iter.slice + i32.const 97 + string.from_code_point + string.eq + i32.eqz + if + i32.const 0 + i32.const 1088 + i32.const 123 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.const 2 + stringview_iter.slice + i32.const 97 + string.from_code_point + i32.const 98 + string.from_code_point + string.concat + string.eq + i32.eqz + if + i32.const 0 + i32.const 1088 + i32.const 124 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.const 3 + stringview_iter.slice + local.get $1 + string.eq + i32.eqz + if + i32.const 0 + i32.const 1088 + i32.const 125 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.const -1 + stringview_iter.slice + local.get $1 + string.eq + i32.eqz + if + i32.const 0 + i32.const 1088 + i32.const 126 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $0 + stringview_iter.next + drop + local.get $0 + i32.const 1 + stringview_iter.slice + i32.const 98 + string.from_code_point + string.eq + i32.eqz + if + i32.const 0 + i32.const 1088 + i32.const 128 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.const 2 + stringview_iter.slice + i32.const 98 + string.from_code_point + i32.const 99 + string.from_code_point + string.concat + string.eq + i32.eqz + if + i32.const 0 + i32.const 1088 + i32.const 129 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.const -1 + stringview_iter.slice + i32.const 98 + string.from_code_point + i32.const 99 + string.from_code_point + string.concat + string.eq + i32.eqz + if + i32.const 0 + i32.const 1088 + i32.const 130 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + ) + (func $start:features/stringref (type $none_=>_none) + (local $0 i32) + (local $1 (ref stringview_wtf16)) + (local $2 i32) + (local $3 i32) + string.const "" + string.as_wtf8 + drop + string.const "" + string.as_wtf16 + drop + string.const "" + string.as_iter + drop + call $features/stringref/test_wtf8 + call $features/stringref/test_wtf16 + call $features/stringref/test_iter + i32.const 97 + string.from_code_point + string.const "a" + string.eq + i32.eqz + if + i32.const 0 + i32.const 1088 + i32.const 145 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + string.const "abc" + string.measure_wtf16 + i32.const 3 + i32.ne + if + i32.const 0 + i32.const 1088 + i32.const 147 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + string.const "abc" + string.measure_wtf16 + i32.const 1 + i32.le_u + if + i32.const 1280 + i32.const 1216 + i32.const 73 + i32.const 31 + call $~lib/builtins/abort + unreachable + end + string.const "abc" + string.as_wtf16 + i32.const 1 + stringview_wtf16.get_codeunit + string.from_code_point + string.const "b" + string.eq + i32.eqz + if + i32.const 0 + i32.const 1088 + i32.const 148 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + string.const "abc" + string.measure_wtf16 + if (result (ref string)) + string.const "abc" + string.as_wtf16 + i32.const 0 + stringview_wtf16.get_codeunit + string.from_code_point + else + string.const "" + end + string.const "a" + string.eq + i32.eqz + if + i32.const 0 + i32.const 1088 + i32.const 149 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + string.const "abc" + string.measure_wtf16 + if (result i32) + string.const "abc" + string.as_wtf16 + i32.const 0 + stringview_wtf16.get_codeunit + else + i32.const -1 + end + i32.const 97 + i32.ne + if + i32.const 0 + i32.const 1088 + i32.const 150 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + i32.const -1 + local.set $0 + block $__inlined_func$~lib/reference/RefString#codePointAt + string.const "abc" + string.measure_wtf16 + local.tee $2 + i32.eqz + br_if $__inlined_func$~lib/reference/RefString#codePointAt + local.get $2 + i32.const 1 + i32.eq + string.const "abc" + string.as_wtf16 + local.tee $1 + i32.const 0 + stringview_wtf16.get_codeunit + local.tee $0 + i32.const 64512 + i32.and + i32.const 55296 + i32.ne + i32.or + br_if $__inlined_func$~lib/reference/RefString#codePointAt + local.get $1 + i32.const 1 + stringview_wtf16.get_codeunit + local.tee $2 + i32.const 64512 + i32.and + i32.const 56320 + i32.ne + br_if $__inlined_func$~lib/reference/RefString#codePointAt + local.get $0 + i32.const 10 + i32.shl + local.get $2 + i32.add + i32.const 56613888 + i32.sub + local.set $0 + end + local.get $0 + i32.const 97 + i32.ne + if + i32.const 0 + i32.const 1088 + i32.const 151 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + string.const "abc" + string.const "abc" + string.concat + string.const "abcabc" + string.eq + i32.eqz + if + i32.const 0 + i32.const 1088 + i32.const 152 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + i32.const 1073741823 + string.const "abc" + string.measure_wtf16 + local.tee $0 + local.get $0 + i32.const 1073741823 + i32.gt_s + select + local.get $0 + i32.sub + local.tee $2 + i32.const 0 + i32.lt_s + if (result i32) + i32.const 0 + else + string.const "abc" + string.as_wtf16 + local.get $2 + local.get $0 + local.get $2 + i32.add + stringview_wtf16.slice + string.const "abc" + string.eq + end + i32.eqz + if + i32.const 0 + i32.const 1088 + i32.const 153 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + string.const "abc" + string.const "abc" + string.eq + i32.eqz + if + i32.const 0 + i32.const 1088 + i32.const 154 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + string.const "abc" + ref.null none + string.eq + if + i32.const 0 + i32.const 1088 + i32.const 155 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + string.const "abc" + string.const "" + string.eq + if + i32.const 0 + i32.const 1088 + i32.const 156 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + string.const "abc" + string.const "ab" + string.compare + i32.const 0 + i32.le_s + if + i32.const 0 + i32.const 1088 + i32.const 157 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + string.const "abc" + string.const "ab" + string.compare + i32.const 0 + i32.lt_s + if + i32.const 0 + i32.const 1088 + i32.const 158 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + string.const "abc" + string.const "abcd" + string.compare + i32.const 0 + i32.ge_s + if + i32.const 0 + i32.const 1088 + i32.const 159 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + string.const "abc" + string.const "abcd" + string.compare + i32.const 0 + i32.gt_s + if + i32.const 0 + i32.const 1088 + i32.const 160 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + i32.const 0 + local.set $0 + block $__inlined_func$~lib/reference/RefString#indexOf + string.const "b" + string.measure_wtf16 + local.tee $2 + i32.eqz + br_if $__inlined_func$~lib/reference/RefString#indexOf + i32.const -1 + local.set $0 + string.const "abc" + string.measure_wtf16 + local.tee $3 + i32.eqz + br_if $__inlined_func$~lib/reference/RefString#indexOf + local.get $3 + i32.const 0 + local.get $3 + i32.const 0 + i32.le_s + select + local.set $0 + string.const "abc" + string.as_wtf16 + local.set $1 + local.get $3 + local.get $2 + i32.sub + local.set $3 + loop $for-loop|0 + local.get $0 + local.get $3 + i32.le_s + if + local.get $1 + local.get $0 + local.get $0 + local.get $2 + i32.add + stringview_wtf16.slice + string.const "b" + string.eq + br_if $__inlined_func$~lib/reference/RefString#indexOf + local.get $0 + i32.const 1 + i32.add + local.set $0 + br $for-loop|0 + end + end + i32.const -1 + local.set $0 + end + local.get $0 + i32.const -1 + i32.eq + if + i32.const 0 + i32.const 1088 + i32.const 161 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + i32.const 0 + local.set $0 + block $__inlined_func$~lib/reference/RefString#indexOf0 + string.const "b" + string.measure_wtf16 + local.tee $2 + i32.eqz + br_if $__inlined_func$~lib/reference/RefString#indexOf0 + i32.const -1 + local.set $0 + string.const "abc" + string.measure_wtf16 + local.tee $3 + i32.eqz + br_if $__inlined_func$~lib/reference/RefString#indexOf0 + local.get $3 + i32.const 0 + local.get $3 + i32.const 0 + i32.le_s + select + local.set $0 + string.const "abc" + string.as_wtf16 + local.set $1 + local.get $3 + local.get $2 + i32.sub + local.set $3 + loop $for-loop|01 + local.get $0 + local.get $3 + i32.le_s + if + local.get $1 + local.get $0 + local.get $0 + local.get $2 + i32.add + stringview_wtf16.slice + string.const "b" + string.eq + br_if $__inlined_func$~lib/reference/RefString#indexOf0 + local.get $0 + i32.const 1 + i32.add + local.set $0 + br $for-loop|01 + end + end + i32.const -1 + local.set $0 + end + local.get $0 + i32.const 1 + i32.ne + if + i32.const 0 + i32.const 1088 + i32.const 162 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + block $__inlined_func$~lib/reference/RefString#lastIndexOf + string.const "b" + string.measure_wtf16 + local.tee $2 + i32.eqz + if + string.const "abc" + string.measure_wtf16 + local.set $0 + br $__inlined_func$~lib/reference/RefString#lastIndexOf + end + i32.const -1 + local.set $0 + string.const "abc" + string.measure_wtf16 + local.tee $3 + i32.eqz + br_if $__inlined_func$~lib/reference/RefString#lastIndexOf + local.get $3 + local.get $2 + i32.sub + local.set $0 + loop $for-loop|03 + local.get $0 + i32.const 0 + i32.ge_s + if + string.const "abc" + string.as_wtf16 + local.get $0 + local.get $0 + local.get $2 + i32.add + stringview_wtf16.slice + string.const "b" + string.eq + br_if $__inlined_func$~lib/reference/RefString#lastIndexOf + local.get $0 + i32.const 1 + i32.sub + local.set $0 + br $for-loop|03 + end + end + i32.const -1 + local.set $0 + end + local.get $0 + i32.const 1 + i32.ne + if + i32.const 0 + i32.const 1088 + i32.const 163 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + ) + (func $~start (type $none_=>_none) + call $start:features/stringref + ) +) diff --git a/tests/compiler/features/stringref.ts b/tests/compiler/features/stringref.ts new file mode 100644 index 0000000000..f25f9f04d0 --- /dev/null +++ b/tests/compiler/features/stringref.ts @@ -0,0 +1,163 @@ +@external("env", "console.log") +declare function log_s(s: ref_string | null): void; +@external("env", "console.log") +declare function log_i(i: i32): void; + +let stringGlobalNull: ref_string | null = null; +let stringviewWtf8GlobalNull: ref_stringview_wtf8 | null = null; +let stringviewWtf16GlobalNull: ref_stringview_wtf16 | null = null; +let stringviewIterGlobalNull: ref_stringview_iter | null = null; + +let stringGlobal: ref_string = ""; +let stringviewWtf8Global: ref_stringview_wtf8 = string.as_wtf8(stringGlobal); // internally nullable +let stringviewWtf16Global: ref_stringview_wtf16 = string.as_wtf16(stringGlobal); // internally nullable +let stringviewIterGlobal: ref_stringview_iter = string.as_iter(stringGlobal); // internally nullable + +function test_locals(): void { + let stringLocalNull: ref_string | null = null; + stringLocalNull = null; + let stringviewWtf8LocalNull: ref_stringview_wtf8 | null = null; + stringviewWtf8LocalNull = null; + let stringviewWtf16LocalNull: ref_stringview_wtf16 | null = null; + stringviewWtf16LocalNull = null; + let stringviewIterLocalNull: ref_stringview_iter | null = null; + stringviewIterLocalNull = null; + + let stringLocal: ref_string; + stringLocal = ""; + let stringviewWtf8Local: ref_stringview_wtf8; + stringviewWtf8Local = string.as_wtf8(stringLocal); + let stringviewWtf16Local: ref_stringview_wtf16; + stringviewWtf16Local = string.as_wtf16(stringLocal); + let stringviewIterLocal: ref_stringview_iter; + stringviewIterLocal = string.as_iter(stringLocal); +} +test_locals(); + +const utf8_data = memory.data([0x61, 0x62, 0x63]); +const wtf16_data = memory.data([0x61, 0x62, 0x63]); +const temp_data = memory.data(16); + +function test_utf8(): void { + // TODO: RuntimeError: unreachable - not yet implemented in Node? + // string.new_utf8 + // string.measure_utf8 + // string.encode_utf8 +} +test_utf8(); + +function test_lossy_utf8(): void { + // TODO: RuntimeError: unreachable - not yet implemented in Node? + // string.new_lossy_utf8 + // string.encode_lossy_utf8 + // stringview_wtf8.encode_utf8 + // stringview_wtf8.encode_lossy_utf8 +} +test_lossy_utf8(); + +function test_wtf8(): void { + let str = string.new_wtf8(utf8_data, 3); + assert(string.measure_wtf8(str) == 3); + string.hash(str); + assert(string.is_usv_sequence(str)); + assert(string.eq(str, str)); + assert(string.eq(str, "abc")); + assert(string.compare(str, str) == 0); + assert(string.compare(str, "b") == -1); + assert(string.compare(str, "`") == 1); + assert(string.encode_wtf8(str, temp_data) == 3); + assert(memory.compare(utf8_data, temp_data, 3) == 0); + + let view = string.as_wtf8(str); + assert(stringview_wtf8.advance(view, 0, 0) == 0); // ^a + assert(stringview_wtf8.advance(view, 0, 1) == 1); // a^b + assert(stringview_wtf8.advance(view, 0, 2) == 2); // ab^c + assert(stringview_wtf8.advance(view, 0, 3) == 3); // abc^ + assert(stringview_wtf8.advance(view, 0, 4) == 3); // abc^ + assert(stringview_wtf8.advance(view, -1, 0) == 3); // abc^ + assert(string.eq(str, stringview_wtf8.slice(view, 0, 3))); + + // TODO: stringview_wtf8.encode_wtf8 +} +test_wtf8(); + +function test_wtf16(): void { + let str = string.new_wtf16(wtf16_data, 3); + assert(string.measure_wtf16(str) == 3); + string.hash(str); + assert(string.is_usv_sequence(str)); + assert(string.eq(str, str)); + assert(string.eq(str, "abc")); + assert(string.compare(str, str) == 0); + assert(string.compare(str, "b") == -1); + assert(string.compare(str, "`") == 1); + assert(string.encode_wtf16(str, temp_data) == 3); + assert(memory.compare(wtf16_data, temp_data, 6) == 0); + + let view = string.as_wtf16(str); + assert(stringview_wtf16.length(view) == 3); + assert(string.eq(str, stringview_wtf16.slice(view, 0, 3))); + assert(stringview_wtf16.get_codeunit(view, 0) == 0x61); // a + assert(stringview_wtf16.get_codeunit(view, 1) == 0x62); // b + assert(stringview_wtf16.get_codeunit(view, 2) == 0x63); // c + + // TODO: stringview_wtf16.encode +} +test_wtf16(); + +function test_iter(): void { + let str = string.new_wtf16(wtf16_data, 3); + let view = string.as_iter(str); + assert(stringview_iter.next(view) == 0x61); + assert(stringview_iter.next(view) == 0x62); + assert(stringview_iter.next(view) == 0x63); + assert(stringview_iter.next(view) == -1); + assert(stringview_iter.rewind(view, 1) == 1); + assert(stringview_iter.next(view) == 0x63); + assert(stringview_iter.next(view) == -1); + assert(stringview_iter.rewind(view, -1) == 3); + assert(stringview_iter.advance(view, 2) == 2); + assert(stringview_iter.next(view) == 0x63); + assert(stringview_iter.next(view) == -1); + assert(stringview_iter.rewind(view, -1) == 3); + assert(string.eq(stringview_iter.slice(view, 1), string.from_code_point(0x61))); + assert(string.eq(stringview_iter.slice(view, 2), string.concat(string.from_code_point(0x61), string.from_code_point(0x62)))); + assert(string.eq(stringview_iter.slice(view, 3), str)); + assert(string.eq(stringview_iter.slice(view, -1), str)); + stringview_iter.next(view); + assert(string.eq(stringview_iter.slice(view, 1), string.from_code_point(0x62))); + assert(string.eq(stringview_iter.slice(view, 2), string.concat(string.from_code_point(0x62), string.from_code_point(0x63)))); + assert(string.eq(stringview_iter.slice(view, -1), string.concat(string.from_code_point(0x62), string.from_code_point(0x63)))); +} +test_iter(); + +// TODO: Test combined Stringref+GC instructions (needs GC types) +// string.new_utf8_array +// string.new_lossy_utf8_array +// string.new_wtf8_array +// string.new_wtf16_array +// string.encode_utf8_array +// string.encode_lossy_utf8_array +// string.encode_wtf8_array +// string.encode_wtf16_array + +// POC +assert(RefString.fromCodePoint(0x61) == "a"); +let str: ref_string = "abc"; +assert(str.length == 3); +assert(str.at(1) == "b"); +assert(str.charAt(0) == "a"); +assert(str.charCodeAt(0) == 0x61); +assert(str.codePointAt(0) == 0x61); +assert(str.concat(str) == "abcabc"); +assert(str.endsWith("abc")); +assert(str == str); +assert(!str == false); +assert(str != ""); +assert(str > "ab"); +assert(str >= "ab"); +assert(str < "abcd"); +assert(str <= "abcd"); +assert(str.includes("b")); +assert(str.indexOf("b") == 1); +assert(str.lastIndexOf("b") == 1); diff --git a/tests/compiler/simd.json b/tests/compiler/simd.json index 0a393fd610..1861d3626f 100644 --- a/tests/compiler/simd.json +++ b/tests/compiler/simd.json @@ -1,5 +1,7 @@ { "asc_flags": [ - "--enable", "simd" + ], + "features": [ + "simd" ] } diff --git a/tests/features.json b/tests/features.json index 5c47772500..f2e525e6f2 100644 --- a/tests/features.json +++ b/tests/features.json @@ -43,5 +43,14 @@ "v8_flags": [ "--experimental-wasm-relaxed-simd" ] + }, + "stringref": { + "asc_flags": [ + "--enable stringref" + ], + "v8_flags": [ + "--experimental-wasm-gc", + "--experimental-wasm-stringref" + ] } }