From b21b72066f595a549e4c825acd6dec4684951ce8 Mon Sep 17 00:00:00 2001 From: Korobkov Ilya Date: Wed, 25 Oct 2023 21:50:37 +0300 Subject: [PATCH 1/2] [stdlib, core] Replaced "charAt" with "toChar" in String.ets. Added checking in "charAt" and "charCodeAt" that index should be in the range 0...length-1. Otherwise, return empty String or NaN Signed-off-by: Korobkov Ilya --- compiler/optimizer/code_generator/codegen.cpp | 6 +- compiler/optimizer/code_generator/encode.h | 4 +- .../code_generator/target/aarch64/target.h | 2 +- .../optimizer/ir_builder/inst_builder-inl.h | 6 +- compiler/optimizer/ir_builder/inst_builder.h | 2 +- .../intrinsics_ir_build_virtual_call_ets.inl | 4 +- plugins/ets/doc/stdlib/packages/escompat.rst | 10 +-- plugins/ets/runtime/ets_libbase_runtime.yaml | 6 +- .../runtime/intrinsics/std_core_String.cpp | 2 +- plugins/ets/stdlib/escompat/Functions.ets | 20 ++--- plugins/ets/stdlib/escompat/json.ets | 4 +- plugins/ets/stdlib/std/core/Char.ets | 2 +- plugins/ets/stdlib/std/core/String.ets | 75 +++++++++---------- plugins/ets/stdlib/std/core/StringBuilder.ets | 2 +- plugins/ets/tests/checked/charAt.ets | 12 +-- .../core/list.std_core_string_instance.yaml | 16 ++-- 16 files changed, 85 insertions(+), 88 deletions(-) diff --git a/compiler/optimizer/code_generator/codegen.cpp b/compiler/optimizer/code_generator/codegen.cpp index 2816515b3..5efe9523c 100644 --- a/compiler/optimizer/code_generator/codegen.cpp +++ b/compiler/optimizer/code_generator/codegen.cpp @@ -3046,12 +3046,12 @@ void EncodeVisitor::VisitLoadCompressedStringChar(GraphVisitor *visitor, Inst *i ScopedTmpReg scoped_tmp(encoder, Codegen::ConvertDataType(DataType::GetIntTypeForReference(arch), arch)); auto tmp = scoped_tmp.GetReg(); - ASSERT(encoder->CanEncodeCompressedStringCharAt()); + ASSERT(encoder->CanEncodeCompressedStringToChar()); auto mask = runtime->GetStringCompressionMask(); if (mask != 1) { UNREACHABLE(); // mask is hardcoded in JCL, but verify it just in case it's changed } - enc->GetEncoder()->EncodeCompressedStringCharAt(dst, src0, src1, src2, tmp, offset, shift); + enc->GetEncoder()->EncodeCompressedStringToChar(dst, src0, src1, src2, tmp, offset, shift); } void EncodeVisitor::VisitLenArray(GraphVisitor *visitor, Inst *inst) @@ -4752,7 +4752,7 @@ void EncodeVisitor::VisitLoadCompressedStringCharI(GraphVisitor *visitor, Inst * auto arch = encoder->GetArch(); int32_t shift = DataType::ShiftByType(type, arch); auto index = inst->CastToLoadCompressedStringCharI()->GetImm(); - ASSERT(encoder->CanEncodeCompressedStringCharAt()); + ASSERT(encoder->CanEncodeCompressedStringToChar()); auto mask = runtime->GetStringCompressionMask(); if (mask != 1) { UNREACHABLE(); // mask is hardcoded in JCL, but verify it just in case it's changed diff --git a/compiler/optimizer/code_generator/encode.h b/compiler/optimizer/code_generator/encode.h index 3d7aaa944..4a5e917c3 100644 --- a/compiler/optimizer/code_generator/encode.h +++ b/compiler/optimizer/code_generator/encode.h @@ -329,7 +329,7 @@ public: SetFalseResult(); } - virtual void EncodeCompressedStringCharAt([[maybe_unused]] Reg dst, [[maybe_unused]] Reg str, + virtual void EncodeCompressedStringToChar([[maybe_unused]] Reg dst, [[maybe_unused]] Reg str, [[maybe_unused]] Reg idx, [[maybe_unused]] Reg length, [[maybe_unused]] Reg tmp, [[maybe_unused]] size_t data_offset, [[maybe_unused]] uint32_t shift) @@ -620,7 +620,7 @@ public: return false; } - virtual bool CanEncodeCompressedStringCharAt() + virtual bool CanEncodeCompressedStringToChar() { return false; } diff --git a/compiler/optimizer/code_generator/target/aarch64/target.h b/compiler/optimizer/code_generator/target/aarch64/target.h index 1694606d5..c1bab3a42 100644 --- a/compiler/optimizer/code_generator/target/aarch64/target.h +++ b/compiler/optimizer/code_generator/target/aarch64/target.h @@ -598,7 +598,7 @@ public: return true; } - bool CanEncodeCompressedStringCharAt() override + bool CanEncodeCompressedStringToChar() override { return true; } diff --git a/compiler/optimizer/ir_builder/inst_builder-inl.h b/compiler/optimizer/ir_builder/inst_builder-inl.h index 32d7fcfac..f1414ded8 100644 --- a/compiler/optimizer/ir_builder/inst_builder-inl.h +++ b/compiler/optimizer/ir_builder/inst_builder-inl.h @@ -1535,12 +1535,12 @@ void InstBuilder::BuildCastToAnyNumber(const BytecodeInstruction *bc_inst) } // NOLINTNEXTLINE(misc-definitions-in-headers) -bool InstBuilder::TryBuildStringCharAtIntrinsic(const BytecodeInstruction *bc_inst, bool acc_read) +bool InstBuilder::TryBuildStringToCharIntrinsic(const BytecodeInstruction *bc_inst, bool acc_read) { auto bc_addr = GetPc(bc_inst->GetAddress()); auto compression_enabled = graph_->GetRuntime()->IsCompressedStringsEnabled(); - auto can_encode_compressed_str_char_at = graph_->GetEncoder()->CanEncodeCompressedStringCharAt(); - if (compression_enabled && !can_encode_compressed_str_char_at) { + auto can_encode_compressed_str_to_char = graph_->GetEncoder()->CanEncodeCompressedStringToChar(); + if (compression_enabled && !can_encode_compressed_str_to_char) { return false; } auto save_state_nullcheck = CreateSaveState(Opcode::SaveState, bc_addr); diff --git a/compiler/optimizer/ir_builder/inst_builder.h b/compiler/optimizer/ir_builder/inst_builder.h index 017692059..fe43ded4c 100644 --- a/compiler/optimizer/ir_builder/inst_builder.h +++ b/compiler/optimizer/ir_builder/inst_builder.h @@ -422,7 +422,7 @@ private: Inst *BuildAnyTypeCheckInst(size_t bc_addr, Inst *input, Inst *save_state, AnyBaseType type = AnyBaseType::UNDEFINED_TYPE, bool type_was_profiled = false, profiling::AnyInputType allowed_input_type = {}); - bool TryBuildStringCharAtIntrinsic(const BytecodeInstruction *bc_inst, bool acc_read); + bool TryBuildStringToCharIntrinsic(const BytecodeInstruction *bc_inst, bool acc_read); #include "inst_builder_extensions.inl.h" Graph *GetGraph() diff --git a/plugins/ets/compiler/intrinsics_ir_build_virtual_call_ets.inl b/plugins/ets/compiler/intrinsics_ir_build_virtual_call_ets.inl index ceced6dea..1acf99d5d 100644 --- a/plugins/ets/compiler/intrinsics_ir_build_virtual_call_ets.inl +++ b/plugins/ets/compiler/intrinsics_ir_build_virtual_call_ets.inl @@ -17,8 +17,8 @@ case RuntimeInterface::IntrinsicId::INTRINSIC_STD_RUNTIME_EQUALS: { BuildStdRuntimeEquals(bc_inst, acc_read); break; } -case RuntimeInterface::IntrinsicId::INTRINSIC_STD_CORE_STRING_CHAR_AT: { - if (!TryBuildStringCharAtIntrinsic(bc_inst, acc_read)) { +case RuntimeInterface::IntrinsicId::INTRINSIC_STD_CORE_STRING_TO_CHAR: { + if (!TryBuildStringToCharIntrinsic(bc_inst, acc_read)) { BuildDefaultVirtualCallIntrinsic(bc_inst, is_range, acc_read); } break; diff --git a/plugins/ets/doc/stdlib/packages/escompat.rst b/plugins/ets/doc/stdlib/packages/escompat.rst index d51237f55..57c9a9753 100644 --- a/plugins/ets/doc/stdlib/packages/escompat.rst +++ b/plugins/ets/doc/stdlib/packages/escompat.rst @@ -16935,18 +16935,18 @@ Methods :kw:`public` charAt(index\: :kw:`number`)\: :ref:`string` -| Getter for char at some index +| Getter for String char at some index | -| **Returns\:** char value at index -| throws stringIndexOutOfBoundsException if index is negative or \>= length +| **Returns\:** String char value at index +| returns empty String if index is negative or \>= length | | **Arguments\:** - index\: :kw:`number` --- index in char array inside string -| **Remarks\:** Implemented as native function, +| **Remarks\:** Implemented with native function, | -| **See\:** \`charAt()\` intrinsic \[declaration\](https\://gitee.com/openharmony-sig/arkcompiler_runtime_core/blob/master/plugins/ets/runtime/ets_libbase_runtime.yaml#L585). +| **See\:** \`toChar()\` intrinsic \[declaration\](https\://gitee.com/openharmony-sig/arkcompiler_runtime_core/blob/master/plugins/ets/runtime/ets_libbase_runtime.yaml#L585). | ------ diff --git a/plugins/ets/runtime/ets_libbase_runtime.yaml b/plugins/ets/runtime/ets_libbase_runtime.yaml index dd96ec484..349ab67d2 100644 --- a/plugins/ets/runtime/ets_libbase_runtime.yaml +++ b/plugins/ets/runtime/ets_libbase_runtime.yaml @@ -602,15 +602,15 @@ intrinsics: impl: panda::ets::intrinsics::StdCoreStringIsEmpty clear_flags: [ ] - - name: StdCoreStringCharAt + - name: StdCoreStringToChar space: ets class_name: std.core.String - method_name: charAt + method_name: toChar static: false signature: ret: u16 args: [ i32 ] - impl: panda::ets::intrinsics::StdCoreStringCharAt + impl: panda::ets::intrinsics::StdCoreStringToChar set_flags: [ can_throw ] - name: StdCoreStringEquals diff --git a/plugins/ets/runtime/intrinsics/std_core_String.cpp b/plugins/ets/runtime/intrinsics/std_core_String.cpp index afc701062..8d7417cc2 100644 --- a/plugins/ets/runtime/intrinsics/std_core_String.cpp +++ b/plugins/ets/runtime/intrinsics/std_core_String.cpp @@ -63,7 +63,7 @@ EtsCharArray *StdCoreStringGetChars(EtsString *s, ets_int begin, ets_int end) return char_array; } -uint16_t StdCoreStringCharAt(EtsString *s, int32_t index) +uint16_t StdCoreStringToChar(EtsString *s, int32_t index) { ASSERT(s != nullptr); diff --git a/plugins/ets/stdlib/escompat/Functions.ets b/plugins/ets/stdlib/escompat/Functions.ets index 9273efa37..27307fc4b 100644 --- a/plugins/ets/stdlib/escompat/Functions.ets +++ b/plugins/ets/stdlib/escompat/Functions.ets @@ -138,7 +138,7 @@ function encodeURIImpl(uri: string, ok: boolean[]): string { } catch (e) { throw new URIError("malformed URI sequence") } - const c: char = uri.charAt(i) + const c: char = uri.toChar(i) if (d != c) { encodeCharAsHexUtf8(ret, d) i += 2 @@ -218,7 +218,7 @@ export function encodeURIComponent(uriComponent: string): string { export function escape(str: string): string { let ret = new StringBuilder() for (let i = 0; i < str.length(); i++) { - const d = str.charAt(i) + const d = str.toChar(i) const c = (d as int) & 0xffff assert c >= 0 assert hexChars.length == 16 @@ -246,8 +246,8 @@ function unhex(c: char): int { function encodedURIHexAt(str: string, i: int): int { try { - assert str.charAt(i) == c'%'; - return unhex(str.charAt(i + 1)) << 4 | unhex(str.charAt(i + 2)) + assert str.toChar(i) == c'%'; + return unhex(str.toChar(i + 1)) << 4 | unhex(str.toChar(i + 2)) } catch (e) { throw new URIError("malformed URI sequence") } @@ -256,12 +256,12 @@ function encodedURIHexAt(str: string, i: int): int { function unescapeImpl(str: string, push: (ret: StringBuilder, unit: int) => void): string { let ret = new StringBuilder(); for (let i = 0; i < str.length(); i++) { - const d = str.charAt(i) + const d = str.toChar(i) if (d != c'%') { ret.append(d) continue } - if (str.charAt(i) != c'%') { + if (str.toChar(i) != c'%') { ret.append(d) continue } @@ -326,15 +326,15 @@ function unescapeImpl(str: string, push: (ret: StringBuilder, unit: int) => void export function unescape(str: string): string { let ret = new StringBuilder(); for (let i = 0; i < str.length(); i++) { - const d = str.charAt(i) + const d = str.toChar(i) if (d != c'%') { ret.append(d) continue } let unit: int = 0 - if (i + 5 < str.length() && str.charAt(i + 1) == c'u') { + if (i + 5 < str.length() && str.toChar(i + 1) == c'u') { try { - unit = unhex(str.charAt(i + 2)) << 12 | unhex(str.charAt(i + 3)) << 8 | unhex(str.charAt(i + 4)) << 4 | unhex(str.charAt(i + 5)) + unit = unhex(str.toChar(i + 2)) << 12 | unhex(str.toChar(i + 3)) << 8 | unhex(str.toChar(i + 4)) << 4 | unhex(str.toChar(i + 5)) } catch (e) { ret.append(d) continue @@ -345,7 +345,7 @@ export function unescape(str: string): string { } if (i + 2 < str.length()) { try { - unit = unhex(str.charAt(i + 1)) << 4 | unhex(str.charAt(i + 2)) + unit = unhex(str.toChar(i + 1)) << 4 | unhex(str.toChar(i + 2)) } catch (e) { ret.append(d) continue diff --git a/plugins/ets/stdlib/escompat/json.ets b/plugins/ets/stdlib/escompat/json.ets index 153de97b5..504fd10c2 100644 --- a/plugins/ets/stdlib/escompat/json.ets +++ b/plugins/ets/stdlib/escompat/json.ets @@ -121,7 +121,7 @@ export class JSON { let sb = new StringBuilder([c'\"']) let len = d.length() for (let i = 0; i < len; ++i) { - let c = d.charAt(i) + let c = d.toChar(i) let escapedCharIndex = lastIndexOf(JSON.ESCAPED_CHARS, c) if (escapedCharIndex != -1) { sb.append(c'\\') @@ -486,7 +486,7 @@ export class JSONParser { if (this.curPos.index >= this.json.length()) { return false } - this.curChar = this.json.charAt(this.curPos.index) + this.curChar = this.json.toChar(this.curPos.index) ++this.curPos.col if (this.curChar == c'\n') { ++this.curPos.row diff --git a/plugins/ets/stdlib/std/core/Char.ets b/plugins/ets/stdlib/std/core/Char.ets index 513350daa..f066e279d 100644 --- a/plugins/ets/stdlib/std/core/Char.ets +++ b/plugins/ets/stdlib/std/core/Char.ets @@ -496,7 +496,7 @@ export final class Char extends Object implements Comparable, JSONable, JSONable public native length(): int; /** - * Getter for char at some index + * Getter for String char at some index * * @param index index in char array inside String * - * @returns char value at index - * - * @throws StringIndexOutOfBoundsException if index is negative or >= length + * @returns String char value at index. Return empty String if index is negative of >= length * * @remarks - * Implemented as native function, @see `charAt()` intrinsic [declaration](https://gitee.com/openharmony-sig/arkcompiler_runtime_core/blob/master/plugins/ets/runtime/ets_libbase_runtime.yaml#L585). + * Implemented with native function, @see `toChar()` intrinsic [declaration](https://gitee.com/openharmony-sig/arkcompiler_runtime_core/blob/master/plugins/ets/runtime/ets_libbase_runtime.yaml#L585). */ public charAt(index: number): String { + if (0 > index || index as int >= this.length()) { + return new String(); + } let c = new char[1]; - c[0] = this.charAt(index as int) + c[0] = this.toChar(index as int) return new String(c) } @@ -90,9 +91,9 @@ export final class String extends Object implements Comparable, JSONable * @throws StringIndexOutOfBoundsException if index is negative or >= length * * @remarks - * Implemented as native function, @see `charAt()` intrinsic [declaration](https://gitee.com/openharmony-sig/arkcompiler_runtime_core/blob/master/plugins/ets/runtime/ets_libbase_runtime.yaml#L585). + * Implemented as native function, @see `toChar()` intrinsic [declaration](https://gitee.com/openharmony-sig/arkcompiler_runtime_core/blob/master/plugins/ets/runtime/ets_libbase_runtime.yaml#L585). */ - public native charAt(index: int): char; + public native toChar(index: int): char; /** * Checks if this string is empty @@ -135,11 +136,11 @@ export final class String extends Object implements Comparable, JSONable */ public codePointAt(index: int): int { assert index >= 0 || index < this.length(): "Index is out of bound"; - let highValue: char = this.charAt(index); + let highValue: char = this.toChar(index); if (!Char.isHighSurrogate(highValue) || ++index == this.length()) { return highValue as int; } - let lowValue: char = this.charAt(index); + let lowValue: char = this.toChar(index); if (!Char.isLowSurrogate(lowValue)) { return highValue as int; } @@ -183,9 +184,9 @@ export final class String extends Object implements Comparable, JSONable let counter: int = 0; for (let i: int = begin; i < end; i++) { ++counter; - if (Char.isHighSurrogate(this.charAt(i)) && + if (Char.isHighSurrogate(this.toChar(i)) && (i + 1 < end) && - Char.isLowSurrogate(this.charAt(i + 1))) { + Char.isLowSurrogate(this.toChar(i + 1))) { ++i; } } @@ -275,8 +276,8 @@ export final class String extends Object implements Comparable, JSONable let to: String = other as String; let len: int = min(this.length(), to.length()) as int; for (let i: int = 0; i < len; i++) { - let lhsCh: Char = new Char(this.charAt(i)); - let rhsCh: Char = new Char(to.charAt(i)); + let lhsCh: Char = new Char(this.toChar(i)); + let rhsCh: Char = new Char(to.toChar(i)); let tmpRes: int = lhsCh.compareTo(rhsCh); if (tmpRes != 0) { return tmpRes; @@ -366,7 +367,7 @@ export final class String extends Object implements Comparable, JSONable return false; } for (let i: int = 0; i < prefix.length(); i++) { - if (this.charAt(fromIndex + i) != prefix.charAt(i)) { + if (this.toChar(fromIndex + i) != prefix.toChar(i)) { return false; } } @@ -389,7 +390,7 @@ export final class String extends Object implements Comparable, JSONable return false; } for (let i: int = 0; i < suffix.length(); i++) { - if (this.charAt(fromIndex + i) != suffix.charAt(i)) { + if (this.toChar(fromIndex + i) != suffix.toChar(i)) { return false; } } @@ -404,7 +405,7 @@ export final class String extends Object implements Comparable, JSONable public override hashCode(): int { let hash: int = 0; for (let i: int = 0; i < this.length(); i++) { - hash = 31 * hash + (this.charAt(i) as int); + hash = 31 * hash + (this.toChar(i) as int); } return hash; } @@ -491,7 +492,7 @@ export final class String extends Object implements Comparable, JSONable fromIndex = this.length() - 1; } for (let i: int = fromIndex; i >= 0; i--) { - if (this.charAt(i) == ch) { + if (this.toChar(i) == ch) { return i; } } @@ -865,7 +866,7 @@ export final class String extends Object implements Comparable, JSONable public trimLeft(): String { let firstNotWhiteSpaceIdx: int = this.length(); for (let i: int = 0; i < this.length(); i++) { - if (!Char.isWhiteSpace(this.charAt(i))) { + if (!Char.isWhiteSpace(this.toChar(i))) { firstNotWhiteSpaceIdx = i; break; } @@ -881,7 +882,7 @@ export final class String extends Object implements Comparable, JSONable public trimRight(): String { let lastNotWhiteSpaceIdx: int = -1; for (let i: int = this.length() - 1; i >= 0; i--) { - if (!Char.isWhiteSpace(this.charAt(i))) { + if (!Char.isWhiteSpace(this.toChar(i))) { lastNotWhiteSpaceIdx = i; break; } @@ -935,7 +936,7 @@ export final class String extends Object implements Comparable, JSONable public trimLeft(remove: char[]): String { let firstNotSpecCharIdx: int = 0; for (let i: int = 0; i < this.length(); i++) { - if (!String.isCharOneOf(this.charAt(i), remove)) { + if (!String.isCharOneOf(this.toChar(i), remove)) { firstNotSpecCharIdx = i; break; } @@ -955,7 +956,7 @@ export final class String extends Object implements Comparable, JSONable public trimRight(remove: char[]): String { let lastNotSpecCharIdx: int = 0; for (let i: int = this.length() - 1; i >= 0; i--) { - if (!String.isCharOneOf(this.charAt(i), remove)) { + if (!String.isCharOneOf(this.toChar(i), remove)) { lastNotSpecCharIdx = i; break; } @@ -1029,7 +1030,7 @@ export final class String extends Object implements Comparable, JSONable let length: int = this.length(); let resChars: char[] = new char[length * count]; for (let i: int = 0; i < length; i++) { - resChars[i] = this.charAt(i); + resChars[i] = this.toChar(i); } for (let repeat = 1; repeat < count; repeat++) { copyTo(resChars, resChars, repeat * length, 0, length); @@ -1069,9 +1070,9 @@ export final class String extends Object implements Comparable, JSONable let n = this.length(); if(index < 0 && -index <= n) { index += n - return this.charAt(index) + return this.toChar(index) } else if (index >= 0 && index < n) { - return this.charAt(index) + return this.toChar(index) } throw new ArgumentOutOfRangeException("undefined"); } @@ -1186,14 +1187,10 @@ export final class String extends Object implements Comparable, JSONable * The charCodeAt() method returns an integer between 0 and 65535 representing the UTF-16 code unit at the given index. */ public charCodeAt(index: number): number { - return this.charAt(index as int); - } - - /* - * The charCodeAt() method returns an integer between 0 and 65535 representing the UTF-16 code unit at the given index. - */ - public charCodeAt(index: int): char { - return this.charAt(index) as char; + if (0 > index || index as int >= this.length()) { + return NaN; + } + return this.toChar(index as int); } /* @@ -1247,7 +1244,7 @@ export final class String extends Object implements Comparable, JSONable } let arr = new char[end] for (let i: int = 0; i < this.length(); i++) { - arr[i] = this.charAt(i) + arr[i] = this.toChar(i) } for(let i = this.length(); i < end; i++){ arr[i] = ch @@ -1270,10 +1267,10 @@ export final class String extends Object implements Comparable, JSONable let arr = new char[end]; let length = this.length(); for (let i: int = 0; i < length; i++) { - arr[i] = this.charAt(i); + arr[i] = this.toChar(i); } for (let i = 0; i < str.length() && length + i < end; i++) { - arr[length + i] = str.charAt(i); + arr[length + i] = str.toChar(i); } for (let i = this.length() + str.length(); i < end; i++) { arr[i] = arr[i - str.length()]; @@ -1307,7 +1304,7 @@ export final class String extends Object implements Comparable, JSONable arr[i] = ch } for (let i: int = 0; i < this.length(); i++) { - arr[padLen + i] = this.charAt(i); + arr[padLen + i] = this.toChar(i); } return new String(arr) } @@ -1327,10 +1324,10 @@ export final class String extends Object implements Comparable, JSONable let arr: char[] = new char[end]; let padLen: int = end - this.length(); for (let i: int = 0; i < this.length(); i++) { - arr[padLen + i] = this.charAt(i); + arr[padLen + i] = this.toChar(i); } for (let i = 0; i < str.length() && i < padLen; i++) { - arr[i] = str.charAt(i); + arr[i] = str.toChar(i); } for (let i = str.length(); i < padLen; i++) { arr[i] = arr[i - str.length()]; diff --git a/plugins/ets/stdlib/std/core/StringBuilder.ets b/plugins/ets/stdlib/std/core/StringBuilder.ets index 8997f6712..781b8e797 100644 --- a/plugins/ets/stdlib/std/core/StringBuilder.ets +++ b/plugins/ets/stdlib/std/core/StringBuilder.ets @@ -53,7 +53,7 @@ export final class StringBuilder { this.count = s.length(); this.value = new char[capacity]; for (let i = 0; i < this.count; ++i) { - this.value[i] = s.charAt(i); + this.value[i] = s.toChar(i); } } diff --git a/plugins/ets/tests/checked/charAt.ets b/plugins/ets/tests/checked/charAt.ets index dd0e2e449..c7bcb4008 100644 --- a/plugins/ets/tests/checked/charAt.ets +++ b/plugins/ets/tests/checked/charAt.ets @@ -13,16 +13,16 @@ * limitations under the License. */ -//! CHECKER Test charAt inlining in compiler +//! CHECKER Test toChar inlining in compiler //! RUN force_jit: true, options: "", entry: "ETSGLOBAL::main" //! METHOD "ETSGLOBAL::main" //! PASS_AFTER "IrBuilder" -//! INST_NOT "Intrinsic.StdCoreStringCharAt" +//! INST_NOT "Intrinsic.StdCoreStringToCharToChar" function main(): void { let str: String = new String("aBCd"); - assert str.charAt(0) == c'a': "Wrong str.charAt(0)"; - assert str.charAt(1) == c'B': "Wrong str.charAt(1)"; - assert str.charAt(2) == c'C': "Wrong str.charAt(2)"; - assert str.charAt(3) == c'd': "Wrong str.charAt(3)"; + assert str.toChar(0) == c'a': "Wrong str.toChar(0)"; + assert str.toChar(1) == c'B': "Wrong str.toChar(1)"; + assert str.toChar(2) == c'C': "Wrong str.toChar(2)"; + assert str.toChar(3) == c'd': "Wrong str.toChar(3)"; } diff --git a/plugins/ets/tests/stdlib-templates/std/core/list.std_core_string_instance.yaml b/plugins/ets/tests/stdlib-templates/std/core/list.std_core_string_instance.yaml index 4f842a0b3..10f953c0e 100644 --- a/plugins/ets/tests/stdlib-templates/std/core/list.std_core_string_instance.yaml +++ b/plugins/ets/tests/stdlib-templates/std/core/list.std_core_string_instance.yaml @@ -256,7 +256,7 @@ } - { - method_name: charAt, + method_name: toChar, object_type: String, init_object_data_type: "String[]", init_object_type: String, @@ -1026,7 +1026,7 @@ param_types: {fillLength: int, fillChar: char}, param_nature: {fillLength: primitive, fillChar: primitive}, param_init_data_types: {fillLength: "int[]", fillChar: "char[]"}, - param_list: {fillLength: '[10, 10, 5, -2]', fillChar: "[c'!', c'*', c'*', c'*']"}, + param_list: {fillLength: '[10, 10, 5, -2]', fillChar: "[c'!', c'*', c'*', c'*']"}, method_return_type: "String", expected_test_data_item_type: "String", @@ -1046,7 +1046,7 @@ param_types: {fillLength: int, fillStr: string}, param_nature: {fillLength: primitive, fillStr: primitive}, param_init_data_types: {fillLength: "int[]", fillStr: "string[]"}, - param_list: {fillLength: '[11, 11, 5, -2]', fillStr: '["be", "*", "be", "be"]'}, + param_list: {fillLength: '[11, 11, 5, -2]', fillStr: '["be", "*", "be", "be"]'}, method_return_type: "String", expected_test_data_item_type: "String", @@ -1066,7 +1066,7 @@ param_types: {fillLength: int}, param_nature: {fillLength: primitive}, param_init_data_types: {fillLength: "int[]"}, - param_list: {fillLength: '[10, 10, 5, -2]'}, + param_list: {fillLength: '[10, 10, 5, -2]'}, method_return_type: "String", expected_test_data_item_type: "String", @@ -1086,7 +1086,7 @@ param_types: {fillLength: int, fillChar: char}, param_nature: {fillLength: primitive, fillChar: primitive}, param_init_data_types: {fillLength: "int[]", fillChar: "char[]"}, - param_list: {fillLength: '[10, 10, 5, -1]', fillChar: "[c'!', c'*', c'*', c'*']"}, + param_list: {fillLength: '[10, 10, 5, -1]', fillChar: "[c'!', c'*', c'*', c'*']"}, method_return_type: "String", expected_test_data_item_type: "String", @@ -1106,7 +1106,7 @@ param_types: {fillLength: int, fillStr: string}, param_nature: {fillLength: primitive, fillStr: primitive}, param_init_data_types: {fillLength: "int[]", fillStr: "string[]"}, - param_list: {fillLength: '[11, 11, 5, -2]', fillStr: '["be", "*", "be", "be"]'}, + param_list: {fillLength: '[11, 11, 5, -2]', fillStr: '["be", "*", "be", "be"]'}, method_return_type: "String", expected_test_data_item_type: "String", @@ -1126,7 +1126,7 @@ param_types: {fillLength: int}, param_nature: {fillLength: primitive}, param_init_data_types: {fillLength: "int[]"}, - param_list: {fillLength: '[10, 10, 5, -2]'}, + param_list: {fillLength: '[10, 10, 5, -2]'}, method_return_type: "String", expected_test_data_item_type: "String", @@ -1351,7 +1351,7 @@ object_type: String, init_object_data_type: "String[]", - init_object_data: '["Quick brown fox jumps over a lazy dog", "Съешь же ещё этих мягких французских булок да выпей чаю", "no replacement"]', + init_object_data: '["Quick brown fox jumps over a lazy dog", "Съешь же ещё этих мягких французских булок да выпей чаю", "no replacement"]', param_types: {w1: String, w2: String}, param_nature: {w1: primitive, w2: primitive}, -- Gitee From 78045fcc7a20ab5cff472ad9689569506be9ee2d Mon Sep 17 00:00:00 2001 From: Korobkov Ilya Date: Thu, 26 Oct 2023 16:22:20 +0300 Subject: [PATCH 2/2] [test] Added test for issue #13953 about charAt and charCodeAt in String.ets class Signed-off-by: Korobkov Ilya --- .../core/StringCharAtAndCharCodeAtTest.ets | 202 ++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 plugins/ets/tests/ets_func_tests/std/core/StringCharAtAndCharCodeAtTest.ets diff --git a/plugins/ets/tests/ets_func_tests/std/core/StringCharAtAndCharCodeAtTest.ets b/plugins/ets/tests/ets_func_tests/std/core/StringCharAtAndCharCodeAtTest.ets new file mode 100644 index 000000000..322bde750 --- /dev/null +++ b/plugins/ets/tests/ets_func_tests/std/core/StringCharAtAndCharCodeAtTest.ets @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2021-2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const fail : int = 1; +const success : int = 0; + +function main(): int { + + try { + let str : String = "All mimsy were the borgoves"; + let strArray : String[] = ["A", "l", "l", " ", "m", "i", "m", "s", "y", " ", "w", "e", "r", "e", " ", "t", "h", "e"," ", "b", "o", "r", "g", "o", "v", "e", "s"]; + let numArray : number[] = [65 , 108, 108, 32, 109, 105, 109, 115, 121, 32 , 119, 101, 114, 101, 32, 116, 104, 101, 32, 98, 111, 114, 103, 111, 118, 101, 115]; + + if (fail == testCharAt(strArray, str)) { + console.println("[failed]"); + return fail; + } + + if (fail == testCharCodeAt(numArray, str)) { + console.println("[failed]"); + return fail; + } + } catch (e) { + console.println("Unexpected exception " + e); + console.println("[failed]"); + return fail; + } + console.println("[success]"); + return success; +} + +function randomMinMax(min: int, max: int): int { + let numMin : number; + let numMax : number; + let numResult : number; + numMin = Math.ceil(min as number); + numMax = Math.floor(max as number); + numResult = Math.floor(Math.random() * (numMax - numMin + 1)) + numMin; + return numResult as int; +} + +function testCharAt(strArray : String[], str : String): int { + let expectedStr : String; + let expectedSize : int; + let min : int; + let max : int; + let last : int; + let index : int; + + if (0 == str.length()) { + console.println("testCharAt: str must be not empty"); + return fail; + } + + for (let i : int = 0; i < strArray.length; ++i) { + if (0 == strArray[i].length()) { + console.println("testCharAt: 0 == strArray[i].length"); + return fail; + } + } + + if (str.length() != strArray.length) { + console.println("testCharAt: str.length != strArray.length"); + return fail; + } + + // Check on invalid range (out of range) + min = str.length(); + max = min + 1000; + index = randomMinMax(min, max); + if (0 <= index && index < str.length()) { + console.println("testCharAt: index (" + index + ") must be equaled or more than str.length (" + str.length() + ")"); + return fail; + } + + expectedStr = ""; + expectedSize = 0; + if ((expectedStr != str.charAt(index)) || (expectedSize != str.charAt(index).length())) { + console.println("testCharAt Case1: expectedStr != str.charAt"); + return fail; + } + + min = -1; + max = min*str.length()-1000; + index = randomMinMax(min, max); + if (0 <= index) { + console.println("testCharAt: index (" + index + ") must be negative"); + return fail; + } + + if ((expectedStr != str.charAt(index)) || (expectedSize != str.charAt(index).length())) { + console.println("testCharAt Case2: expectedStr != str.charAt"); + return fail; + } + + // Check on valid range + min = 0; + max = str.length()-1; + index = randomMinMax(min, max); + if (index >= str.length()) { + console.println("testCharAt: index (" + index + ") must be not bigger than " + str.length()); + return fail; + } + + expectedStr = strArray[index]; + expectedSize = 1; + if (expectedStr.length() != expectedSize) { + console.println("testCharAt: expectedStr.length != expectedSize"); + return fail; + } + + if ((expectedStr != str.charAt(index)) || (expectedSize != str.charAt(1).length())) { + console.println("testCharAt Case3: expectedStr != str.charAt"); + return fail; + } + return success; +} + +function testCharCodeAt(numArray : number[], str : String): int { + let expectedNum : number; + let min : int; + let max : int; + let last : int; + let index : int; + + if (0 == str.length()) { + console.println("testCharCodeAt: str must be not empty"); + return fail; + } + + for (let i : int = 0; i < numArray.length; ++i) { + if (true == isNaN(numArray[i])) { + console.println("testCharCodeAt: true == isNaN(numArray[i])"); + return fail; + } + } + + if (str.length() != numArray.length) { + console.println("testCharCodeAt: str.length != numArray.length"); + return fail; + } + + // Check on invalid range (out of range) + min = str.length(); + max = min + 1000; + index = randomMinMax(min, max); + if (0 <= index && index < str.length()) { + console.println("testCharCodeAt: index (" + index + ") must be equaled or more than str.length (" + str.length() + ")"); + return fail; + } + + expectedNum = NaN; + if (false == isNaN(expectedNum)) { + console.println("testCharCodeAt: expectedNum != NaN"); + return fail; + } + if (false == isNaN(str.charCodeAt(index))) { + console.println("testCharCodeAt Case1: expectedNum != str.charCodeAt"); + return fail; + } + + min = -1; + max = min*str.length()-1000; + index = randomMinMax(min, max); + if (0 <= index) { + console.println("testCharCodeAt: index (" + index + ") must be negative"); + return fail; + } + + if (false == isNaN(str.charCodeAt(index))) { + console.println("testCharCodeAt Case2: expectedNum != str.charCodeAt"); + return fail; + } + + // Check on valid range + min = 0; + max = str.length()-1; + index = randomMinMax(min, max); + if (index >= str.length()) { + console.println("testCharCodeAt: index (" + index + ") must be not bigger than " + str.length()); + return fail; + } + + expectedNum = numArray[index]; + if ((expectedNum != str.charCodeAt(index)) || (true == isNaN(expectedNum))) { + console.println("testCharCodeAt Case3: expectedNum != str.charCodeAt"); + return fail; + } + return success; +} -- Gitee