diff --git a/builtin_string.go b/builtin_string.go index a11babe4..03f59dad 100644 --- a/builtin_string.go +++ b/builtin_string.go @@ -254,7 +254,9 @@ func builtinStringReplace(call FunctionCall) Value { argumentList[index] = Value{} } } - argumentList[matchCount+0] = intValue(match[0]) + // Replace expects rune offsets not byte offsets. + startIndex := utf8.RuneCountInString(target[0:match[0]]) + argumentList[matchCount+0] = intValue(startIndex) argumentList[matchCount+1] = stringValue(target) replacement := replace.call(Value{}, argumentList, false, nativeFrame).string() result = append(result, []byte(replacement)...) @@ -394,16 +396,18 @@ func builtinStringSplit(call FunctionCall) Value { } } +// builtinStringSlice returns the string sliced by the given values +// which are rune not byte offsets, as per String.prototype.slice. func builtinStringSlice(call FunctionCall) Value { checkObjectCoercible(call.runtime, call.This) - target := call.This.string() + target := []rune(call.This.string()) length := int64(len(target)) start, end := rangeStartEnd(call.ArgumentList, length, false) if end-start <= 0 { return stringValue("") } - return stringValue(target[start:end]) + return stringValue(string(target[start:end])) } func builtinStringSubstring(call FunctionCall) Value { diff --git a/string_test.go b/string_test.go index 3810e8bf..0cfcc506 100644 --- a/string_test.go +++ b/string_test.go @@ -331,6 +331,7 @@ func TestString_slice_unicode(t *testing.T) { test(`"uñiçode".slice(0,11)`, "uñiçode") test(`"uñiçode".slice(0,-1)`, "uñiçod") test(`"uñiçode".slice(-1,11)`, "e") + test(`"发送 213123".slice(0,2)`, "发送") }) }