소스 검색

ES6 string functions (#560)

* implement endsWith
* implement trimEnd and trimStart, update related 262 tests
* implement string iterator
* implement fromCodePoint and codePointAt
* implement normalize
* fix pad functions
* implement repeat
* remove mongolian vowel check, tweak coercible checks
* fix string equality against ConcatenatedString, tweak ignores
Marko Lahma 6 년 전
부모
커밋
3cbb20e1ad
55개의 변경된 파일656개의 추가작업 그리고 168개의 파일을 삭제
  1. 48 48
      Jint.Tests.Ecma/TestCases/alltests.json
  2. 1 1
      Jint.Tests.Test262/SingleTest.cs
  3. 23 0
      Jint.Tests.Test262/StringTests.cs
  4. 55 2
      Jint.Tests.Test262/Test262Test.cs
  5. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/indexOf/S15.5.4.7_A5_T2.js
  6. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/indexOf/S15.5.4.7_A5_T3.js
  7. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/indexOf/S15.5.4.7_A5_T5.js
  8. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/indexOf/S15.5.4.7_A5_T6.js
  9. 15 4
      Jint.Tests.Test262/test/built-ins/String/prototype/matchAll/regexp-prototype-has-no-matchAll.js
  10. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/toString/S15.5.4.2_A1_T1.js
  11. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/toString/S15.5.4.2_A1_T2.js
  12. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/toString/S15.5.4.2_A1_T3.js
  13. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/toString/S15.5.4.2_A1_T4.js
  14. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/trimEnd/this-value-line-terminator.js
  15. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/trimEnd/this-value-object-cannot-convert-to-primitive-err.js
  16. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/trimEnd/this-value-object-toprimitive-meth-priority.js
  17. 1 0
      Jint.Tests.Test262/test/built-ins/String/prototype/trimEnd/this-value-object-toprimitive-returns-object-err.js
  18. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/trimEnd/this-value-object-tostring-meth-priority.js
  19. 1 0
      Jint.Tests.Test262/test/built-ins/String/prototype/trimEnd/this-value-object-tostring-returns-object-err.js
  20. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/trimEnd/this-value-object-valueof-meth-priority.js
  21. 1 0
      Jint.Tests.Test262/test/built-ins/String/prototype/trimEnd/this-value-object-valueof-returns-object-err.js
  22. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/trimEnd/this-value-whitespace.js
  23. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/trimStart/this-value-line-terminator.js
  24. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/trimStart/this-value-object-cannot-convert-to-primitive-err.js
  25. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/trimStart/this-value-object-toprimitive-meth-priority.js
  26. 1 0
      Jint.Tests.Test262/test/built-ins/String/prototype/trimStart/this-value-object-toprimitive-returns-object-err.js
  27. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/trimStart/this-value-object-tostring-meth-priority.js
  28. 1 0
      Jint.Tests.Test262/test/built-ins/String/prototype/trimStart/this-value-object-tostring-returns-object-err.js
  29. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/trimStart/this-value-object-valueof-meth-priority.js
  30. 1 0
      Jint.Tests.Test262/test/built-ins/String/prototype/trimStart/this-value-object-valueof-returns-object-err.js
  31. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/trimStart/this-value-whitespace.js
  32. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/valueOf/S15.5.4.3_A1_T1.js
  33. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/valueOf/S15.5.4.3_A1_T2.js
  34. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/valueOf/S15.5.4.3_A1_T3.js
  35. 1 1
      Jint.Tests.Test262/test/built-ins/String/prototype/valueOf/S15.5.4.3_A1_T4.js
  36. 12 0
      Jint.Tests.Test262/test/skipped.json
  37. 9 2
      Jint/EsprimaExtensions.cs
  38. 2 2
      Jint/Native/Global/GlobalObject.cs
  39. 19 17
      Jint/Native/Iterator/IteratorConstructor.cs
  40. 25 0
      Jint/Native/Iterator/IteratorInstance.cs
  41. 1 1
      Jint/Native/Iterator/IteratorPrototype.cs
  42. 1 1
      Jint/Native/JsString.cs
  43. 5 0
      Jint/Native/JsSymbol.cs
  44. 2 2
      Jint/Native/Object/ObjectConstructor.cs
  45. 44 4
      Jint/Native/Object/ObjectInstance.cs
  46. 2 5
      Jint/Native/Object/ObjectPrototype.cs
  47. 57 5
      Jint/Native/String/StringConstructor.cs
  48. 252 43
      Jint/Native/String/StringPrototype.cs
  49. 1 1
      Jint/Native/Symbol/SymbolConstructor.cs
  50. 5 0
      Jint/Runtime/ExceptionHelper.cs
  51. 1 1
      Jint/Runtime/ExpressionIntepreter.cs
  52. 14 0
      Jint/Runtime/RangeErrorException.cs
  53. 15 0
      Jint/Runtime/StatementInterpreter.cs
  54. 16 4
      Jint/Runtime/TypeConverter.cs
  55. 1 1
      README.md

+ 48 - 48
Jint.Tests.Ecma/TestCases/alltests.json

@@ -4705,8 +4705,8 @@
     source: "ch09/9.3/9.3.1/S9.3.1_A19.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "mongolian vowel separator handling has changed",
     source: "ch09/9.3/9.3.1/S9.3.1_A2.js"
   },
   {
@@ -4775,13 +4775,13 @@
     source: "ch09/9.3/9.3.1/S9.3.1_A32.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "mongolian vowel separator handling has changed",
     source: "ch09/9.3/9.3.1/S9.3.1_A3_T1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "mongolian vowel separator handling has changed",
     source: "ch09/9.3/9.3.1/S9.3.1_A3_T2.js"
   },
   {
@@ -51025,8 +51025,8 @@
     source: "ch15/15.5/15.5.4/15.5.4.10/S15.5.4.10_A8.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "now configurable",
     source: "ch15/15.5/15.5.4/15.5.4.10/S15.5.4.10_A9.js"
   },
   {
@@ -51235,8 +51235,8 @@
     source: "ch15/15.5/15.5.4/15.5.4.11/S15.5.4.11_A8.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "now configurable",
     source: "ch15/15.5/15.5.4/15.5.4.11/S15.5.4.11_A9.js"
   },
   {
@@ -51380,8 +51380,8 @@
     source: "ch15/15.5/15.5.4/15.5.4.12/S15.5.4.12_A8.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "now configurable",
     source: "ch15/15.5/15.5.4/15.5.4.12/S15.5.4.12_A9.js"
   },
   {
@@ -51545,8 +51545,8 @@
     source: "ch15/15.5/15.5.4/15.5.4.13/S15.5.4.13_A8.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "now configurable",
     source: "ch15/15.5/15.5.4/15.5.4.13/S15.5.4.13_A9.js"
   },
   {
@@ -52060,8 +52060,8 @@
     source: "ch15/15.5/15.5.4/15.5.4.14/S15.5.4.14_A8.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "now configurable",
     source: "ch15/15.5/15.5.4/15.5.4.14/S15.5.4.14_A9.js"
   },
   {
@@ -52265,8 +52265,8 @@
     source: "ch15/15.5/15.5.4/15.5.4.15/S15.5.4.15_A8.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "now configurable",
     source: "ch15/15.5/15.5.4/15.5.4.15/S15.5.4.15_A9.js"
   },
   {
@@ -52370,8 +52370,8 @@
     source: "ch15/15.5/15.5.4/15.5.4.16/S15.5.4.16_A8.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "now configurable",
     source: "ch15/15.5/15.5.4/15.5.4.16/S15.5.4.16_A9.js"
   },
   {
@@ -52475,8 +52475,8 @@
     source: "ch15/15.5/15.5.4/15.5.4.17/S15.5.4.17_A8.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "now configurable",
     source: "ch15/15.5/15.5.4/15.5.4.17/S15.5.4.17_A9.js"
   },
   {
@@ -52580,8 +52580,8 @@
     source: "ch15/15.5/15.5.4/15.5.4.18/S15.5.4.18_A8.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "now configurable",
     source: "ch15/15.5/15.5.4/15.5.4.18/S15.5.4.18_A9.js"
   },
   {
@@ -52685,8 +52685,8 @@
     source: "ch15/15.5/15.5.4/15.5.4.19/S15.5.4.19_A8.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "now configurable",
     source: "ch15/15.5/15.5.4/15.5.4.19/S15.5.4.19_A9.js"
   },
   {
@@ -53025,28 +53025,28 @@
     source: "ch15/15.5/15.5.4/15.5.4.20/15.5.4.20-3-14.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "mongolian vowel separator handling has changed",
     source: "ch15/15.5/15.5.4/15.5.4.20/15.5.4.20-3-2.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "mongolian vowel separator handling has changed",
     source: "ch15/15.5/15.5.4/15.5.4.20/15.5.4.20-3-3.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "mongolian vowel separator handling has changed",
     source: "ch15/15.5/15.5.4/15.5.4.20/15.5.4.20-3-4.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "mongolian vowel separator handling has changed",
     source: "ch15/15.5/15.5.4/15.5.4.20/15.5.4.20-3-5.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "mongolian vowel separator handling has changed",
     source: "ch15/15.5/15.5.4/15.5.4.20/15.5.4.20-3-6.js"
   },
   {
@@ -53425,8 +53425,8 @@
     source: "ch15/15.5/15.5.4/15.5.4.4/S15.5.4.4_A8.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "now configurable",
     source: "ch15/15.5/15.5.4/15.5.4.4/S15.5.4.4_A9.js"
   },
   {
@@ -53520,8 +53520,8 @@
     source: "ch15/15.5/15.5.4/15.5.4.5/S15.5.4.5_A8.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "now configurable",
     source: "ch15/15.5/15.5.4/15.5.4.5/S15.5.4.5_A9.js"
   },
   {
@@ -53615,8 +53615,8 @@
     source: "ch15/15.5/15.5.4/15.5.4.6/S15.5.4.6_A8.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "now configurable",
     source: "ch15/15.5/15.5.4/15.5.4.6/S15.5.4.6_A9.js"
   },
   {
@@ -53790,8 +53790,8 @@
     source: "ch15/15.5/15.5.4/15.5.4.7/S15.5.4.7_A8.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "now configurable",
     source: "ch15/15.5/15.5.4/15.5.4.7/S15.5.4.7_A9.js"
   },
   {
@@ -53895,8 +53895,8 @@
     source: "ch15/15.5/15.5.4/15.5.4.8/S15.5.4.8_A8.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "now configurable",
     source: "ch15/15.5/15.5.4/15.5.4.8/S15.5.4.8_A9.js"
   },
   {
@@ -53945,8 +53945,8 @@
     source: "ch15/15.5/15.5.4/15.5.4.9/S15.5.4.9_A8.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "now configurable",
     source: "ch15/15.5/15.5.4/15.5.4.9/S15.5.4.9_A9.js"
   },
   {

+ 1 - 1
Jint.Tests.Test262/SingleTest.cs

@@ -23,7 +23,7 @@ namespace Jint.Tests.Test262
         [RunnableInDebugOnly]
         public void TestSingle()
         {
-            const string Target = @"built-ins/Array/prototype/every/15.4.4.16-5-23.js";
+            const string Target = @"built-ins/String/prototype/repeat/repeat-string-n-times.js";
             //const string Target = @"built-ins/Array/from/calling-from-valid-2.js";
             var sourceFile = SourceFiles("built-ins", false)
                 .SelectMany(x => x)

+ 23 - 0
Jint.Tests.Test262/StringTests.cs

@@ -0,0 +1,23 @@
+using Xunit;
+
+namespace Jint.Tests.Test262
+{
+    public class StringTests : Test262Test
+    {
+        [Theory(DisplayName = "built-ins\\String")]
+        [MemberData(nameof(SourceFiles), "built-ins\\String", false)]
+        [MemberData(nameof(SourceFiles), "built-ins\\String", true, Skip = "Skipped")]
+        protected void String(SourceFile sourceFile)
+        {
+            RunTestInternal(sourceFile);
+        }
+
+        [Theory(DisplayName = "built-ins\\StringIteratorPrototype")]
+        [MemberData(nameof(SourceFiles), "built-ins\\StringIteratorPrototype", false)]
+        [MemberData(nameof(SourceFiles), "built-ins\\StringIteratorPrototype", true, Skip = "Skipped")]
+        protected void StringIteratorPrototype(SourceFile sourceFile)
+        {
+            RunTestInternal(sourceFile);
+        }
+    }
+}

+ 55 - 2
Jint.Tests.Test262/Test262Test.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Collections.Generic;
 using System.IO;
+using System.Linq;
 using System.Reflection;
 using System.Text.RegularExpressions;
 using Jint.Runtime;
@@ -118,7 +119,7 @@ namespace Jint.Tests.Test262
                 if (features.Success)
                 {
                     var items = features.Groups[1].Captures[0].Value.Split(",");
-                    foreach (var item in items)
+                    foreach (var item in items.Select(x => x.Trim()))
                     {
                         // TODO implement
                         if (item == "cross-realm")
@@ -141,9 +142,61 @@ namespace Jint.Tests.Test262
                             skip = true;
                             reason = "Symbol.unscopables not implemented";
                         }
+                        else if (item == "Symbol.match")
+                        {
+                            skip = true;
+                            reason = "Symbol.match not implemented";
+                        }
+                        else if (item == "Symbol.matchAll")
+                        {
+                            skip = true;
+                            reason = "Symbol.matchAll not implemented";
+                        }
+                        else if (item == "Symbol.split")
+                        {
+                            skip = true;
+                            reason = "Symbol.split not implemented";
+                        }
+                        else if (item == "String.prototype.matchAll")
+                        {
+                            skip = true;
+                            reason = "proposal stage";
+                        }
+                        else if (item == "Symbol.search")
+                        {
+                            skip = true;
+                            reason = "Symbol.search not implemented";
+                        }
+                        else if (item == "Symbol.replace")
+                        {
+                            skip = true;
+                            reason = "Symbol.replace not implemented";
+                        }
+                        else if (item == "Symbol.toStringTag")
+                        {
+                            skip = true;
+                            reason = "Symbol.toStringTag not implemented";
+                        }
+                        else if (item == "BigInt")
+                        {
+                            skip = true;
+                            reason = "BigInt not implemented";
+                        }
                     }
                 }
-                
+
+                if (name.StartsWith("built-ins/String/raw/"))
+                {
+                    skip = true;
+                    reason = "requires template string";
+                }
+
+                if (code.IndexOf("SpecialCasing.txt") > -1)
+                {
+                    skip = true;
+                    reason = "SpecialCasing.txt not implemented";
+                }
+
                 var sourceFile = new SourceFile(
                     name,
                     file,

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/indexOf/S15.5.4.7_A5_T2.js

@@ -4,7 +4,7 @@
 /*---
 info: String.prototype.indexOf works properly
 es5id: 15.5.4.7_A5_T2
-description: Search one symbol from it`s position in the string
+description: Search one symbol from its position in the string
 ---*/
 
 var TEST_STRING = new String(" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~");

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/indexOf/S15.5.4.7_A5_T3.js

@@ -4,7 +4,7 @@
 /*---
 info: String.prototype.indexOf works properly
 es5id: 15.5.4.7_A5_T3
-description: Search one symbol from it`s position+1 in the string
+description: Search one symbol from its position+1 in the string
 ---*/
 
 var TEST_STRING = new String(" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~");

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/indexOf/S15.5.4.7_A5_T5.js

@@ -4,7 +4,7 @@
 /*---
 info: String.prototype.indexOf works properly
 es5id: 15.5.4.7_A5_T5
-description: Search substring from it`s position in the string
+description: Search substring from its position in the string
 ---*/
 
 var TEST_STRING = new String(" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~");

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/indexOf/S15.5.4.7_A5_T6.js

@@ -4,7 +4,7 @@
 /*---
 info: String.prototype.indexOf works properly
 es5id: 15.5.4.7_A5_T6
-description: Search substring from it`s position+1 in the string
+description: Search substring from its position+1 in the string
 ---*/
 
 var TEST_STRING = new String(" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~");

+ 15 - 4
Jint.Tests.Test262/test/built-ins/String/prototype/matchAll/regexp-prototype-has-no-matchAll.js

@@ -10,15 +10,26 @@ info: |
       a. Let matcher be ? GetMethod(regexp, @@matchAll).
       b. If matcher is not undefined, then
         [...]
-    3. Return ? MatchAllIterator(regexp, O).
+    [...]
+    4. Let matcher be ? RegExpCreate(R, "g").
+    [...]
+
+  21.2.3.2.3 Runtime Semantics: RegExpCreate ( P, F )
+    [...]
+    2. Return ? RegExpInitialize(obj, P, F).
+
+  21.2.3.2.2 Runtime Semantics: RegExpInitialize ( obj, pattern, flags )
+    1. If pattern is undefined, let P be the empty String.
+    2. Else, let P be ? ToString(pattern).
+    [...]
 features: [Symbol.matchAll, String.prototype.matchAll]
 includes: [compareArray.js, compareIterator.js, regExpUtils.js]
 ---*/
 
 delete RegExp.prototype[Symbol.matchAll];
-var str = 'a*b';
+var str = '/a/g*/b/g';
 
 assert.compareIterator(str.matchAll(/\w/g), [
-  matchValidator(['a'], 0, str),
-  matchValidator(['b'], 2, str)
+  matchValidator(['/a/g'], 0, str),
+  matchValidator(['/b/g'], 5, str)
 ]);

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/toString/S15.5.4.2_A1_T1.js

@@ -4,7 +4,7 @@
 /*---
 info: String.prototype.toString() returns this string value
 es5id: 15.5.4.2_A1_T1
-description: Create new String(number) and check it`s method toString()
+description: Create new String(number) and check its method toString()
 ---*/
 
 var __string__obj = new String(1);

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/toString/S15.5.4.2_A1_T2.js

@@ -4,7 +4,7 @@
 /*---
 info: String.prototype.toString() returns this string value
 es5id: 15.5.4.2_A1_T2
-description: Create new String(boolean) and check it`s method toString()
+description: Create new String(boolean) and check its method toString()
 ---*/
 
 var __string__obj = new String(true);

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/toString/S15.5.4.2_A1_T3.js

@@ -4,7 +4,7 @@
 /*---
 info: String.prototype.toString() returns this string value
 es5id: 15.5.4.2_A1_T3
-description: Create new String(string) and check it`s method toString()
+description: Create new String(string) and check its method toString()
 ---*/
 
 var __string__obj = new String("metal");

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/toString/S15.5.4.2_A1_T4.js

@@ -4,7 +4,7 @@
 /*---
 info: String.prototype.toString() returns this string value
 es5id: 15.5.4.2_A1_T4
-description: Create new String(function(){}()) and check it`s method toString()
+description: Create new String(function(){}()) and check its method toString()
 ---*/
 
 var __string__obj = new String(function() {}());

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/trimEnd/this-value-line-terminator.js

@@ -26,5 +26,5 @@ var expected = lt + 'a' + lt + 'b';
 
 assert.sameValue(
   trimEnd.call(str),
-  expected,
+  expected
 );

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/trimEnd/this-value-object-cannot-convert-to-primitive-err.js

@@ -55,5 +55,5 @@ assert.throws(
   TypeError,
   function() {
     String.prototype.trimEnd.call(thisVal);
-  },
+  }
 );

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/trimEnd/this-value-object-toprimitive-meth-priority.js

@@ -63,7 +63,7 @@ assert.sameValue(
 assert.sameValue(
   result,
   '42',
-  'thisVal[Symbol.toPrimitive] expected to have been called.',
+  'thisVal[Symbol.toPrimitive] expected to have been called.'
 );
 
 // Test that thisVal.toString and thisVal.valueOf have not been accessedo

+ 1 - 0
Jint.Tests.Test262/test/built-ins/String/prototype/trimEnd/this-value-object-toprimitive-returns-object-err.js

@@ -33,6 +33,7 @@ var thisVal = {
   },
 };
 
+assert.sameValue(typeof String.prototype.trimEnd, 'function');
 assert.throws(TypeError, function() {
   String.prototype.trimEnd.call(thisVal);
 });

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/trimEnd/this-value-object-tostring-meth-priority.js

@@ -77,7 +77,7 @@ assert.sameValue(
 assert.sameValue(
   result,
   '42',
-  'thisVal.toString expected to have been called.',
+  'thisVal.toString expected to have been called.'
 );
 
 // Test that thisVal[toPrimitive] has been accessed.

+ 1 - 0
Jint.Tests.Test262/test/built-ins/String/prototype/trimEnd/this-value-object-tostring-returns-object-err.js

@@ -51,6 +51,7 @@ var thisVal = {
   },
 };
 
+assert.sameValue(typeof String.prototype.trimEnd, 'function');
 assert.throws(TypeError, function() {
   String.prototype.trimEnd.call(thisVal);
 });

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/trimEnd/this-value-object-valueof-meth-priority.js

@@ -76,7 +76,7 @@ assert.sameValue(
 assert.sameValue(
   result,
   '42',
-  'thisVal.valueOf expected to have been called.',
+  'thisVal.valueOf expected to have been called.'
 );
 
 // Test that thisVal[toPrimitive] and thisVal.toString has been accessed.

+ 1 - 0
Jint.Tests.Test262/test/built-ins/String/prototype/trimEnd/this-value-object-valueof-returns-object-err.js

@@ -52,6 +52,7 @@ var thisVal = {
   },
 };
 
+assert.sameValue(typeof String.prototype.trimEnd, 'function');
 assert.throws(TypeError, function() {
   String.prototype.trimEnd.call(thisVal);
 });

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/trimEnd/this-value-whitespace.js

@@ -29,5 +29,5 @@ var expected = wspc + 'a' + wspc + 'b';
 
 assert.sameValue(
   trimEnd.call(str),
-  expected,
+  expected
 );

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/trimStart/this-value-line-terminator.js

@@ -26,5 +26,5 @@ var expected = 'a' + lt + 'b' + lt;
 
 assert.sameValue(
   trimStart.call(str),
-  expected,
+  expected
 );

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/trimStart/this-value-object-cannot-convert-to-primitive-err.js

@@ -55,5 +55,5 @@ assert.throws(
   TypeError,
   function() {
     String.prototype.trimStart.call(thisVal);
-  },
+  }
 );

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/trimStart/this-value-object-toprimitive-meth-priority.js

@@ -63,7 +63,7 @@ assert.sameValue(
 assert.sameValue(
   result,
   '42',
-  'thisVal[Symbol.toPrimitive] expected to have been called.',
+  'thisVal[Symbol.toPrimitive] expected to have been called.'
 );
 
 // Test that thisVal.toString and thisVal.valueOf have not been accessedo

+ 1 - 0
Jint.Tests.Test262/test/built-ins/String/prototype/trimStart/this-value-object-toprimitive-returns-object-err.js

@@ -33,6 +33,7 @@ var thisVal = {
   },
 };
 
+assert.sameValue(typeof String.prototype.trimStart, 'function');
 assert.throws(TypeError, function() {
   String.prototype.trimStart.call(thisVal);
 });

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/trimStart/this-value-object-tostring-meth-priority.js

@@ -77,7 +77,7 @@ assert.sameValue(
 assert.sameValue(
   result,
   '42',
-  'thisVal.toString expected to have been called.',
+  'thisVal.toString expected to have been called.'
 );
 
 // Test that thisVal[toPrimitive] has been accessed.

+ 1 - 0
Jint.Tests.Test262/test/built-ins/String/prototype/trimStart/this-value-object-tostring-returns-object-err.js

@@ -51,6 +51,7 @@ var thisVal = {
   },
 };
 
+assert.sameValue(typeof String.prototype.trimStart, 'function');
 assert.throws(TypeError, function() {
   String.prototype.trimStart.call(thisVal);
 });

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/trimStart/this-value-object-valueof-meth-priority.js

@@ -76,7 +76,7 @@ assert.sameValue(
 assert.sameValue(
   result,
   '42',
-  'thisVal.valueOf expected to have been called.',
+  'thisVal.valueOf expected to have been called.'
 );
 
 // Test that thisVal[toPrimitive] and thisVal.toString has been accessed.

+ 1 - 0
Jint.Tests.Test262/test/built-ins/String/prototype/trimStart/this-value-object-valueof-returns-object-err.js

@@ -52,6 +52,7 @@ var thisVal = {
   },
 };
 
+assert.sameValue(typeof String.prototype.trimStart, 'function');
 assert.throws(TypeError, function() {
   String.prototype.trimStart.call(thisVal);
 });

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/trimStart/this-value-whitespace.js

@@ -29,5 +29,5 @@ var expected = 'a' + wspc + 'b' + wspc;
 
 assert.sameValue(
   trimStart.call(str),
-  expected,
+  expected
 );

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/valueOf/S15.5.4.3_A1_T1.js

@@ -4,7 +4,7 @@
 /*---
 info: String.prototype.valueOf() returns this string value
 es5id: 15.5.4.3_A1_T1
-description: Create String object as new String(1) and check it`s valueOf()
+description: Create String object as new String(1) and check its valueOf()
 ---*/
 
 var __string__obj = new String(1);

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/valueOf/S15.5.4.3_A1_T2.js

@@ -4,7 +4,7 @@
 /*---
 info: String.prototype.valueOf() returns this string value
 es5id: 15.5.4.3_A1_T2
-description: Create String object as new String(true) and check it`s valueOf()
+description: Create String object as new String(true) and check its valueOf()
 ---*/
 
 var __string__obj = new String(true);

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/valueOf/S15.5.4.3_A1_T3.js

@@ -4,7 +4,7 @@
 /*---
 info: String.prototype.valueOf() returns this string value
 es5id: 15.5.4.3_A1_T3
-description: Create String object as new String(string) and check it`s valueOf()
+description: Create String object as new String(string) and check its valueOf()
 ---*/
 
 var __string__obj = new String("metal");

+ 1 - 1
Jint.Tests.Test262/test/built-ins/String/prototype/valueOf/S15.5.4.3_A1_T4.js

@@ -5,7 +5,7 @@
 info: String.prototype.valueOf() returns this string value
 es5id: 15.5.4.3_A1_T4
 description: >
-    Create String object as new String(function(){}()) and check it`s
+    Create String object as new String(function(){}()) and check its
     valueOf()
 ---*/
 

+ 12 - 0
Jint.Tests.Test262/test/skipped.json

@@ -277,5 +277,17 @@
   {
     "source": "built-ins/Array/prototype/flatMap/thisArg-argument.js",
     "reason": "experimental"
+  },
+  {
+    "source": "built-ins/String/prototype/padEnd/observable-operations.js",
+    "reason": "observables not implemetned"
+  },
+  {
+    "source": "built-ins/String/prototype/padStart/observable-operations.js",
+    "reason": "observables not implemetned"
+  },
+  {
+    "source": "built-ins/StringIteratorPrototype/next/next-iteration-surrogate-pairs.js",
+    "reason": "code point iteration not implemented"
   }
 ]

+ 9 - 2
Jint/EsprimaExtensions.cs

@@ -24,9 +24,16 @@ namespace Jint
                 var obj = staticMemberExpression.Object.GetKey();
                 var property = staticMemberExpression.Property.GetKey();
 
-                if (obj == "Symbol" && property == "iterator")
+                if (obj == "Symbol")
                 {
-                    return GlobalSymbolRegistry.Iterator._value;
+                    if (property == "iterator")
+                    {
+                        return GlobalSymbolRegistry.Iterator._value;
+                    }
+                    if (property == "toPrimitive")
+                    {
+                        return GlobalSymbolRegistry.ToPrimitive._value;
+                    }
                 }
             }
 

+ 2 - 2
Jint/Native/Global/GlobalObject.cs

@@ -77,7 +77,7 @@ namespace Jint.Native.Global
         public static JsValue ParseInt(JsValue thisObject, JsValue[] arguments)
         {
             string inputString = TypeConverter.ToString(arguments.At(0));
-            var s = StringPrototype.TrimEx(inputString, acceptMongolianVowelSeparator: false);
+            var s = StringPrototype.TrimEx(inputString);
 
             var sign = 1;
             if (!System.String.IsNullOrEmpty(s))
@@ -178,7 +178,7 @@ namespace Jint.Native.Global
         public static JsValue ParseFloat(JsValue thisObject, JsValue[] arguments)
         {
             var inputString = TypeConverter.ToString(arguments.At(0));
-            var trimmedString = StringPrototype.TrimStartEx(inputString, acceptMongolianVowelSeparator: false);
+            var trimmedString = StringPrototype.TrimStartEx(inputString);
 
             var sign = 1;
             if (trimmedString.Length > 0)

+ 19 - 17
Jint/Native/Iterator/IteratorConstructor.cs

@@ -52,8 +52,7 @@ namespace Jint.Native.Iterator
         {
             var instance = new IteratorInstance(Engine, enumerable)
             {
-                Prototype = PrototypeObject,
-                Extensible = true
+                Prototype = PrototypeObject, Extensible = true
             };
 
             return instance;
@@ -63,8 +62,7 @@ namespace Jint.Native.Iterator
         {
             var instance = new IteratorInstance.ListIterator(Engine, enumerable)
             {
-                Prototype = PrototypeObject,
-                Extensible = true
+                Prototype = PrototypeObject, Extensible = true
             };
 
             return instance;
@@ -74,8 +72,7 @@ namespace Jint.Native.Iterator
         {
             var instance = new IteratorInstance.ArrayLikeIterator(Engine, array)
             {
-                Prototype = PrototypeObject,
-                Extensible = true
+                Prototype = PrototypeObject, Extensible = true
             };
 
             return instance;
@@ -85,8 +82,7 @@ namespace Jint.Native.Iterator
         {
             var instance = new IteratorInstance.MapIterator(Engine, map)
             {
-                Prototype = PrototypeObject,
-                Extensible = true
+                Prototype = PrototypeObject, Extensible = true
             };
 
             return instance;
@@ -96,8 +92,7 @@ namespace Jint.Native.Iterator
         {
             var instance = new IteratorInstance.SetIterator(Engine, set)
             {
-                Prototype = PrototypeObject,
-                Extensible = true
+                Prototype = PrototypeObject, Extensible = true
             };
 
             return instance;
@@ -107,8 +102,7 @@ namespace Jint.Native.Iterator
         {
             var instance = new IteratorInstance.SetEntryIterator(Engine, set)
             {
-                Prototype = PrototypeObject,
-                Extensible = true
+                Prototype = PrototypeObject, Extensible = true
             };
 
             return instance;
@@ -118,8 +112,7 @@ namespace Jint.Native.Iterator
         {
             var instance = new IteratorInstance.ArrayLikeKeyIterator(Engine, array)
             {
-                Prototype = PrototypeObject,
-                Extensible = true
+                Prototype = PrototypeObject, Extensible = true
             };
 
             return instance;
@@ -129,11 +122,20 @@ namespace Jint.Native.Iterator
         {
             var instance = new IteratorInstance.ArrayLikeValueIterator(Engine, array)
             {
-                Prototype = PrototypeObject,
-                Extensible = true
+                Prototype = PrototypeObject, Extensible = true
+            };
+
+            return instance;
+        }
+
+        public ObjectInstance Construct(string str)
+        {
+            var instance = new IteratorInstance.StringIterator(Engine, str)
+            {
+                Prototype = PrototypeObject, Extensible = true
             };
 
             return instance;
         }
     }
-}
+}

+ 25 - 0
Jint/Native/Iterator/IteratorInstance.cs

@@ -293,5 +293,30 @@ namespace Jint.Native.Iterator
                 }
             }
         }
+
+        internal class StringIterator : IteratorInstance
+        {
+            private readonly string _str;
+            private int _position;
+            private bool _closed;
+
+            public StringIterator(Engine engine, string str) : base(engine)
+            {
+                _str = str;
+                _position = 0;
+            }
+
+            public override ObjectInstance Next()
+            {
+                var length = _str.Length;
+                if (!_closed && _position < length)
+                {
+                    return new ValueIteratorPosition(_engine, _str[_position++]);
+                }
+
+                _closed = true;
+                return ValueIteratorPosition.Done;
+            }
+        }
     }
 }

+ 1 - 1
Jint/Native/Iterator/IteratorPrototype.cs

@@ -23,7 +23,7 @@ namespace Jint.Native.Iterator
 
         public void Configure()
         {
-            FastAddProperty("next", new ClrFunctionInstance(Engine, "next", Next, 0), true, false, true);
+            FastAddProperty("next", new ClrFunctionInstance(Engine, "next", Next, 0, PropertyFlag.Configurable), true, false, true);
         }
 
         private JsValue Next(JsValue thisObj, JsValue[] arguments)

+ 1 - 1
Jint/Native/JsString.cs

@@ -135,7 +135,7 @@ namespace Jint.Native
                 return true;
             }
 
-            return _value == other._value;
+            return _value == other.ToString();
         }
 
         internal sealed class ConcatenatedString : JsString

+ 5 - 0
Jint/Native/JsSymbol.cs

@@ -16,6 +16,11 @@ namespace Jint.Native
             _value = value;
         }
 
+        internal JsSymbol(JsValue value) : base(Types.Symbol)
+        {
+            _value = value.IsUndefined() ? "" : TypeConverter.ToString(value);
+        }
+
         public override object ToObject()
         {
             return _value;

+ 2 - 2
Jint/Native/Object/ObjectConstructor.cs

@@ -139,7 +139,7 @@ namespace Jint.Native.Object
             }
 
             var p = arguments.At(1);
-            var name = TypeConverter.ToString(p);
+            var name = TypeConverter.ToPropertyKey(p);
 
             var desc = o.GetOwnProperty(name);
             return PropertyDescriptor.FromPropertyDescriptor(Engine, desc);
@@ -217,7 +217,7 @@ namespace Jint.Native.Object
             }
 
             var p = arguments.At(1);
-            var name = TypeConverter.ToString(p);
+            var name = TypeConverter.ToPropertyKey(p);
 
             var attributes = arguments.At(2);
             var desc = PropertyDescriptor.ToPropertyDescriptor(Engine, attributes);

+ 44 - 4
Jint/Native/Object/ObjectInstance.cs

@@ -20,6 +20,8 @@ namespace Jint.Native.Object
 {
     public class ObjectInstance : JsValue, IEquatable<ObjectInstance>
     {
+        private static readonly string ToPrimitiveSymbolName = GlobalSymbolRegistry.ToPrimitive._value;
+
         internal StringDictionarySlim<PropertyDescriptor> _properties;
 
         private readonly string _class;
@@ -388,17 +390,34 @@ namespace Jint.Native.Object
         }
 
         /// <summary>
-        /// Hint is a String. Returns a default value for the
-        /// object.
+        /// Hint is a String. Returns a default value for the object.
         /// </summary>
-        /// <param name="hint"></param>
-        /// <returns></returns>
         public JsValue DefaultValue(Types hint)
         {
             EnsureInitialized();
 
             if (hint == Types.String || (hint == Types.None && Class == "Date"))
             {
+                var jsValue = Get(ToPrimitiveSymbolName);
+                if (!jsValue.IsNullOrUndefined())
+                {
+                    if (jsValue is ICallable toPrimitive)
+                    {
+                        var str = toPrimitive.Call(this, Arguments.Empty);
+                        if (str.IsPrimitive())
+                        {
+                            return str;
+                        }
+
+                        if (str.IsObject())
+                        {
+                            return ExceptionHelper.ThrowTypeError<JsValue>(_engine, "Cannot convert object to primitive value");
+                        }
+                    }
+
+                    const string message = "'Value returned for property 'Symbol(Symbol.toPrimitive)' of object is not a function";
+                    return ExceptionHelper.ThrowTypeError<JsValue>(_engine, message);
+                }
                 if (Get("toString") is ICallable toString)
                 {
                     var str = toString.Call(this, Arguments.Empty);
@@ -422,6 +441,27 @@ namespace Jint.Native.Object
 
             if (hint == Types.Number || hint == Types.None)
             {
+                var jsValue = Get(ToPrimitiveSymbolName);
+                if (!jsValue.IsNullOrUndefined())
+                {
+                    if (jsValue is ICallable toPrimitive)
+                    {
+                        var val = toPrimitive.Call(this, Arguments.Empty);
+                        if (val.IsPrimitive())
+                        {
+                            return val;
+                        }
+
+                        if (val.IsObject())
+                        {
+                            return ExceptionHelper.ThrowTypeError<JsValue>(_engine, "Cannot convert object to primitive value");
+                        }
+                    }
+
+                    const string message = "'Value returned for property 'Symbol(Symbol.toPrimitive)' of object is not a function";
+                    return ExceptionHelper.ThrowTypeError<JsValue>(_engine, message);
+                }
+
                 if (Get("valueOf") is ICallable valueOf)
                 {
                     var val = valueOf.Call(this, Arguments.Empty);

+ 2 - 5
Jint/Native/Object/ObjectPrototype.cs

@@ -31,7 +31,7 @@ namespace Jint.Native.Object
 
         private JsValue PropertyIsEnumerable(JsValue thisObject, JsValue[] arguments)
         {
-            var p = TypeConverter.ToString(arguments[0]);
+            var p = TypeConverter.ToPropertyKey(arguments[0]);
             var o = TypeConverter.ToObject(Engine, thisObject);
             var desc = o.GetOwnProperty(p);
             if (desc == PropertyDescriptor.Undefined)
@@ -111,12 +111,9 @@ namespace Jint.Native.Object
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.2.4.5
         /// </summary>
-        /// <param name="thisObject"></param>
-        /// <param name="arguments"></param>
-        /// <returns></returns>
         public JsValue HasOwnProperty(JsValue thisObject, JsValue[] arguments)
         {
-            var p = TypeConverter.ToString(arguments[0]);
+            var p = TypeConverter.ToPropertyKey(arguments[0]);
             var o = TypeConverter.ToObject(Engine, thisObject);
             var desc = o.GetOwnProperty(p);
             return desc != PropertyDescriptor.Undefined;

+ 57 - 5
Jint/Native/String/StringConstructor.cs

@@ -1,4 +1,5 @@
-using Jint.Native.Function;
+using System.Collections.Generic;
+using Jint.Native.Function;
 using Jint.Native.Object;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
@@ -33,6 +34,7 @@ namespace Jint.Native.String
         public void Configure()
         {
             SetOwnProperty("fromCharCode", new PropertyDescriptor(new ClrFunctionInstance(Engine, "fromCharCode", FromCharCode, 1), PropertyFlag.NonEnumerable));
+            SetOwnProperty("fromCodePoint", new PropertyDescriptor(new ClrFunctionInstance(Engine, "fromCodePoint", FromCodePoint, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable));
         }
 
         private static JsValue FromCharCode(JsValue thisObj, JsValue[] arguments)
@@ -43,17 +45,62 @@ namespace Jint.Native.String
                 chars[i] = (char)TypeConverter.ToUint16(arguments[i]);
             }
 
-            return new System.String(chars);
+            return JsString.Create(new string(chars));
+        }
+
+        private static JsValue FromCodePoint(JsValue thisObj, JsValue[] arguments)
+        {
+            var codeUnits = new List<JsValue>();
+            string result = "";
+            for (var i = 0; i < arguments.Length; i++ )
+            {
+                var codePoint = TypeConverter.ToNumber(arguments[i]);
+                if (codePoint < 0
+                    || codePoint > 0x10FFFF
+                    || double.IsInfinity(codePoint)
+                    || double.IsNaN(codePoint)
+                    || TypeConverter.ToInt32(codePoint) != codePoint)
+                {
+                    return ExceptionHelper.ThrowRangeErrorNoEngine<JsValue>("Invalid code point " + codePoint);
+                }
+
+                var point = (uint) codePoint;
+                if (point <= 0xFFFF)
+                {
+                    // BMP code point
+                    codeUnits.Add(JsNumber.Create(point));
+                }
+                else
+                {
+                    // Astral code point; split in surrogate halves
+                    // https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
+                    point -= 0x10000;
+                    codeUnits.Add(JsNumber.Create((point >> 10) + 0xD800)); // highSurrogate
+                    codeUnits.Add(JsNumber.Create((point % 0x400) + 0xDC00)); // lowSurrogate
+                }
+                if (codeUnits.Count >= 0x3fff)
+                {
+                    result += FromCharCode(null, codeUnits.ToArray());
+                    codeUnits.Clear();
+                }
+            }
+
+            return result + FromCharCode(null, codeUnits.ToArray());
         }
 
         public override JsValue Call(JsValue thisObject, JsValue[] arguments)
         {
             if (arguments.Length == 0)
             {
-                return "";
+                return JsString.Empty;
             }
 
-            return TypeConverter.ToString(arguments[0]);
+            var arg = arguments[0];
+            var str = arg is JsSymbol s
+                ? s.ToString()
+                : TypeConverter.ToString(arg);
+
+            return JsString.Create(str);
         }
 
         /// <summary>
@@ -63,7 +110,12 @@ namespace Jint.Native.String
         /// <returns></returns>
         public ObjectInstance Construct(JsValue[] arguments)
         {
-            return Construct(arguments.Length > 0 ? TypeConverter.ToString(arguments[0]) : "");
+            string value = "";
+            if (arguments.Length > 0)
+            {
+                value = TypeConverter.ToString(arguments[0]);
+            }
+            return Construct(value);
         }
 
         public StringPrototype PrototypeObject { get; private set; }

+ 252 - 43
Jint/Native/String/StringPrototype.cs

@@ -1,10 +1,12 @@
 using System;
 using System.Linq;
 using System.Runtime.CompilerServices;
+using System.Text;
 using Jint.Native.Array;
 using Jint.Native.Function;
 using Jint.Native.Object;
 using Jint.Native.RegExp;
+using Jint.Native.Symbol;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Interop;
@@ -35,29 +37,45 @@ namespace Jint.Native.String
 
         public void Configure()
         {
-            FastAddProperty("toString", new ClrFunctionInstance(Engine, "toString", ToStringString), true, false, true);
-            FastAddProperty("valueOf", new ClrFunctionInstance(Engine, "valueOF", ValueOf), true, false, true);
-            FastAddProperty("charAt", new ClrFunctionInstance(Engine, "charAt", CharAt, 1), true, false, true);
-            FastAddProperty("charCodeAt", new ClrFunctionInstance(Engine, "charCodeAt", CharCodeAt, 1), true, false, true);
-            FastAddProperty("concat", new ClrFunctionInstance(Engine, "concat", Concat, 1), true, false, true);
-            FastAddProperty("indexOf", new ClrFunctionInstance(Engine, "indexOf", IndexOf, 1), true, false, true);
-            FastAddProperty("startsWith", new ClrFunctionInstance(Engine, "startsWith", StartsWith, 1), true, false, true);
-            FastAddProperty("lastIndexOf", new ClrFunctionInstance(Engine, "lastIndexOf", LastIndexOf, 1), true, false, true);
-            FastAddProperty("localeCompare", new ClrFunctionInstance(Engine, "localeCompare", LocaleCompare, 1), true, false, true);
-            FastAddProperty("match", new ClrFunctionInstance(Engine, "match", Match, 1), true, false, true);
-            FastAddProperty("replace", new ClrFunctionInstance(Engine, "replace", Replace, 2), true, false, true);
-            FastAddProperty("search", new ClrFunctionInstance(Engine, "search", Search, 1), true, false, true);
-            FastAddProperty("slice", new ClrFunctionInstance(Engine, "slice", Slice, 2), true, false, true);
-            FastAddProperty("split", new ClrFunctionInstance(Engine, "split", Split, 2), true, false, true);
+            FastAddProperty("toString", new ClrFunctionInstance(Engine, "toString", ToStringString, 0, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("valueOf", new ClrFunctionInstance(Engine, "valueOf", ValueOf, 0, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("charAt", new ClrFunctionInstance(Engine, "charAt", CharAt, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("charCodeAt", new ClrFunctionInstance(Engine, "charCodeAt", CharCodeAt, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("codePointAt", new ClrFunctionInstance(Engine, "codePointAt", CodePointAt, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("concat", new ClrFunctionInstance(Engine, "concat", Concat, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("indexOf", new ClrFunctionInstance(Engine, "indexOf", IndexOf, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("endsWith", new ClrFunctionInstance(Engine, "endsWith", EndsWith, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("startsWith", new ClrFunctionInstance(Engine, "startsWith", StartsWith, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("lastIndexOf", new ClrFunctionInstance(Engine, "lastIndexOf", LastIndexOf, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("localeCompare", new ClrFunctionInstance(Engine, "localeCompare", LocaleCompare, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("match", new ClrFunctionInstance(Engine, "match", Match, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("replace", new ClrFunctionInstance(Engine, "replace", Replace, 2, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("search", new ClrFunctionInstance(Engine, "search", Search, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("slice", new ClrFunctionInstance(Engine, "slice", Slice, 2, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("split", new ClrFunctionInstance(Engine, "split", Split, 2, PropertyFlag.Configurable), true, false, true);
             FastAddProperty("substr", new ClrFunctionInstance(Engine, "substr", Substr, 2), true, false, true);
-            FastAddProperty("substring", new ClrFunctionInstance(Engine, "substring", Substring, 2), true, false, true);
-            FastAddProperty("toLowerCase", new ClrFunctionInstance(Engine, "toLowerCase", ToLowerCase), true, false, true);
-            FastAddProperty("toLocaleLowerCase", new ClrFunctionInstance(Engine, "toLocaleLowerCase", ToLocaleLowerCase), true, false, true);
-            FastAddProperty("toUpperCase", new ClrFunctionInstance(Engine, "toUpperCase", ToUpperCase), true, false, true);
-            FastAddProperty("toLocaleUpperCase", new ClrFunctionInstance(Engine, "toLocaleUpperCase", ToLocaleUpperCase), true, false, true);
-            FastAddProperty("trim", new ClrFunctionInstance(Engine, "trim", Trim), true, false, true);
-            FastAddProperty("padStart", new ClrFunctionInstance(Engine, "padStart", PadStart), true, false, true);
-            FastAddProperty("padEnd", new ClrFunctionInstance(Engine, "padEnd", PadEnd), true, false, true);
+            FastAddProperty("substring", new ClrFunctionInstance(Engine, "substring", Substring, 2, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("toLowerCase", new ClrFunctionInstance(Engine, "toLowerCase", ToLowerCase, 0, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("toLocaleLowerCase", new ClrFunctionInstance(Engine, "toLocaleLowerCase", ToLocaleLowerCase, 0, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("toUpperCase", new ClrFunctionInstance(Engine, "toUpperCase", ToUpperCase, 0, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("toLocaleUpperCase", new ClrFunctionInstance(Engine, "toLocaleUpperCase", ToLocaleUpperCase, 0, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("trim", new ClrFunctionInstance(Engine, "trim", Trim, 0, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("trimStart", new ClrFunctionInstance(Engine, "trimStart", TrimStart, 0, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("trimEnd", new ClrFunctionInstance(Engine, "trimEnd", TrimEnd, 0, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("padStart", new ClrFunctionInstance(Engine, "padStart", PadStart, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("padEnd", new ClrFunctionInstance(Engine, "padEnd", PadEnd, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("includes", new ClrFunctionInstance(Engine, "includes", Includes, 1, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("normalize", new ClrFunctionInstance(Engine, "normalize", Normalize, 0, PropertyFlag.Configurable), true, false, true);
+            FastAddProperty("repeat", new ClrFunctionInstance(Engine, "repeat", Repeat, 1, PropertyFlag.Configurable), true, false, true);
+
+            FastAddProperty(GlobalSymbolRegistry.Iterator._value, new ClrFunctionInstance(Engine, "[Symbol.iterator]", Iterator, 0, PropertyFlag.Configurable), true, false, true);
+        }
+
+        private ObjectInstance Iterator(JsValue thisObj, JsValue[] arguments)
+        {
+            TypeConverter.CheckObjectCoercible(_engine, thisObj);
+            var str = TypeConverter.ToString(thisObj);
+            return _engine.Iterator.Construct(str);
         }
 
         private JsValue ToStringString(JsValue thisObj, JsValue[] arguments)
@@ -74,16 +92,11 @@ namespace Jint.Native.String
         // http://msdn.microsoft.com/en-us/library/system.char.iswhitespace(v=vs.110).aspx
         // http://en.wikipedia.org/wiki/Byte_order_mark
         const char BOM_CHAR = '\uFEFF';
-        const char MONGOLIAN_VOWEL_SEPARATOR = '\u180E';
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        internal static bool IsWhiteSpaceEx(char c, bool acceptMongolianVowelSeparator = true)
+        internal static bool IsWhiteSpaceEx(char c)
         {
-            return
-                char.IsWhiteSpace(c) ||
-                c == BOM_CHAR ||
-                // In .NET 4.6 this was removed from WS based on Unicode 6.3 changes
-                (acceptMongolianVowelSeparator && c == MONGOLIAN_VOWEL_SEPARATOR);
+            return char.IsWhiteSpace(c) || c == BOM_CHAR;
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -113,12 +126,12 @@ namespace Jint.Native.String
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public static string TrimStartEx(string s, bool acceptMongolianVowelSeparator = true)
+        public static string TrimStartEx(string s)
         {
             if (s.Length == 0)
                 return string.Empty;
 
-            if (!IsWhiteSpaceEx(s[0], acceptMongolianVowelSeparator))
+            if (!IsWhiteSpaceEx(s[0]))
                 return s;
 
             return TrimStart(s);
@@ -139,9 +152,9 @@ namespace Jint.Native.String
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public static string TrimEx(string s, bool acceptMongolianVowelSeparator = true)
+        public static string TrimEx(string s)
         {
-            return TrimEndEx(TrimStartEx(s, acceptMongolianVowelSeparator));
+            return TrimEndEx(TrimStartEx(s));
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -152,26 +165,44 @@ namespace Jint.Native.String
             return TrimEx(s);
         }
 
-        private static JsValue ToLocaleUpperCase(JsValue thisObj, JsValue[] arguments)
+        private JsValue TrimStart(JsValue thisObj, JsValue[] arguments)
         {
+            TypeConverter.CheckObjectCoercible(Engine, thisObj);
+            var s = TypeConverter.ToString(thisObj);
+            return TrimStartEx(s);
+        }
+
+        private JsValue TrimEnd(JsValue thisObj, JsValue[] arguments)
+        {
+            TypeConverter.CheckObjectCoercible(Engine, thisObj);
+            var s = TypeConverter.ToString(thisObj);
+            return TrimEndEx(s);
+        }
+
+        private JsValue ToLocaleUpperCase(JsValue thisObj, JsValue[] arguments)
+        {
+            TypeConverter.CheckObjectCoercible(_engine, thisObj);
             var s = TypeConverter.ToString(thisObj);
             return s.ToUpper();
         }
 
-        private static JsValue ToUpperCase(JsValue thisObj, JsValue[] arguments)
+        private JsValue ToUpperCase(JsValue thisObj, JsValue[] arguments)
         {
+            TypeConverter.CheckObjectCoercible(_engine, thisObj);
             var s = TypeConverter.ToString(thisObj);
             return s.ToUpperInvariant();
         }
 
-        private static JsValue ToLocaleLowerCase(JsValue thisObj, JsValue[] arguments)
+        private JsValue ToLocaleLowerCase(JsValue thisObj, JsValue[] arguments)
         {
+            TypeConverter.CheckObjectCoercible(_engine, thisObj);
             var s = TypeConverter.ToString(thisObj);
             return s.ToLower();
         }
 
-        private static JsValue ToLowerCase(JsValue thisObj, JsValue[] arguments)
+        private JsValue ToLowerCase(JsValue thisObj, JsValue[] arguments)
         {
+            TypeConverter.CheckObjectCoercible(_engine, thisObj);
             var s = TypeConverter.ToString(thisObj);
             return s.ToLowerInvariant();
         }
@@ -799,6 +830,30 @@ namespace Jint.Native.String
             return (double) s[position];
         }
 
+        private JsValue CodePointAt(JsValue thisObj, JsValue[] arguments)
+        {
+            TypeConverter.CheckObjectCoercible(Engine, thisObj);
+
+            JsValue pos = arguments.Length > 0 ? arguments[0] : 0;
+            var s = TypeConverter.ToString(thisObj);
+            var position = (int)TypeConverter.ToInteger(pos);
+            if (position < 0 || position >= s.Length)
+            {
+                return Undefined;
+            }
+
+            var first = (double) s[position];
+            if (first >= 0xD800 && first <= 0xDBFF && s.Length > position + 1)
+            {
+                double second = s[position + 1];
+                if (second >= 0xDC00 && second <= 0xDFFF)
+                {
+                    return (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000;
+                }
+            }
+            return first;
+        }
+
         private JsValue CharAt(JsValue thisObj, JsValue[] arguments)
         {
             TypeConverter.CheckObjectCoercible(Engine, thisObj);
@@ -860,10 +915,14 @@ namespace Jint.Native.String
         {
             TypeConverter.CheckObjectCoercible(Engine, thisObj);
             var targetLength = TypeConverter.ToInt32(arguments.At(0));
-            var padString = TypeConverter.ToString(arguments.At(1, " "));
+            var padStringValue = arguments.At(1);
+
+            var padString = padStringValue.IsUndefined()
+                ? " "
+                : TypeConverter.ToString(padStringValue);
 
             var s = TypeConverter.ToString(thisObj);
-            if (s.Length > targetLength)
+            if (s.Length > targetLength || padString.Length == 0)
             {
                 return s;
             }
@@ -874,15 +933,14 @@ namespace Jint.Native.String
                 padString = string.Join("", Enumerable.Repeat(padString, (targetLength / padString.Length) + 1));
             }
 
-            return padStart ? $"{padString.Substring(0, targetLength)}{s}" : $"{s}{padString.Substring(0, targetLength)}";
+            return padStart
+                ? $"{padString.Substring(0, targetLength)}{s}"
+                : $"{s}{padString.Substring(0, targetLength)}";
         }
 
         /// <summary>
         /// https://www.ecma-international.org/ecma-262/6.0/#sec-string.prototype.startswith
         /// </summary>
-        /// <param name="thisObj"></param>
-        /// <param name="arguments"></param>
-        /// <returns></returns>
         private JsValue StartsWith(JsValue thisObj, JsValue[] arguments)
         {
             TypeConverter.CheckObjectCoercible(Engine, thisObj);
@@ -924,5 +982,156 @@ namespace Jint.Native.String
 
             return true;
         }
+
+        /// <summary>
+        /// https://www.ecma-international.org/ecma-262/6.0/#sec-string.prototype.endswith
+        /// </summary>
+        private JsValue EndsWith(JsValue thisObj, JsValue[] arguments)
+        {
+            TypeConverter.CheckObjectCoercible(Engine, thisObj);
+
+            var s = TypeConverter.ToString(thisObj);
+
+            var searchString = arguments.At(0);
+            if (ReferenceEquals(searchString, Null))
+            {
+                searchString = Native.Null.Text;
+            }
+            else
+            {
+                if (searchString.IsRegExp())
+                {
+                    ExceptionHelper.ThrowTypeError(Engine);
+                }
+            }
+
+            var searchStr = TypeConverter.ToString(searchString);
+
+            var len = s.Length;
+            var pos = TypeConverter.ToInt32(arguments.At(1, len));
+            var end = System.Math.Min(System.Math.Max(pos, 0), len);
+            var searchLength = searchStr.Length;
+            var start = end - searchLength;
+
+            if (start < 0)
+            {
+                return false;
+            }
+
+            for (var i = 0; i < searchLength; i++)
+            {
+                if (s[start + i] != searchStr[i])
+                {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        private JsValue Includes(JsValue thisObj, JsValue[] arguments)
+        {
+            TypeConverter.CheckObjectCoercible(Engine, thisObj);
+
+            var s1 = TypeConverter.ToString(thisObj);
+            var searchString = arguments.At(0);
+
+            if (searchString.IsRegExp())
+            {
+                return ExceptionHelper.ThrowTypeError<JsValue>(_engine, "First argument to String.prototype.includes must not be a regular expression");
+            }
+
+            var searchStr = TypeConverter.ToString(searchString);
+            double pos = 0;
+            if (arguments.Length > 1 && !arguments[1].IsUndefined())
+            {
+                pos = TypeConverter.ToInteger(arguments[1]);
+            }
+
+            if (searchStr.Length == 0)
+            {
+                return true;
+            }
+
+            if (pos >= s1.Length)
+            {
+                return false;
+            }
+
+            if (pos < 0)
+            {
+                pos = 0;
+            }
+
+            return s1.IndexOf(searchStr, (int) pos, StringComparison.Ordinal) > -1;
+        }
+
+        private JsValue Normalize(JsValue thisObj, JsValue[] arguments)
+        {
+            TypeConverter.CheckObjectCoercible(Engine, thisObj);
+            var str = TypeConverter.ToString(thisObj);
+
+            var param = arguments.At(0);
+
+            var form = "NFC";
+           if (!param.IsUndefined())
+           {
+               form = TypeConverter.ToString(param);
+           }
+
+            var nf = NormalizationForm.FormC;
+            switch (form)
+            {
+                case "NFC":
+                    nf = NormalizationForm.FormC;
+                    break;
+                case "NFD":
+                    nf = NormalizationForm.FormD;
+                    break;
+                case "NFKC":
+                    nf = NormalizationForm.FormKC;
+                    break;
+                case "NFKD":
+                    nf = NormalizationForm.FormKD;
+                    break;
+                default:
+                    ExceptionHelper.ThrowRangeError(
+                        _engine,
+                        "The normalization form should be one of NFC, NFD, NFKC, NFKD.");
+                    break;
+            }
+
+            return str.Normalize(nf);
+        }
+
+        private JsValue Repeat(JsValue thisObj, JsValue[] arguments)
+        {
+            TypeConverter.CheckObjectCoercible(Engine, thisObj);
+            var str = TypeConverter.ToString(thisObj);
+            var n = (int) TypeConverter.ToInteger(arguments.At(0));
+
+            if (n < 0)
+            {
+                return ExceptionHelper.ThrowRangeError<JsValue>(_engine, "Invalid count value");
+            }
+
+            if (n == 0 || str.Length == 0)
+            {
+                return JsString.Empty;
+            }
+
+            if (str.Length == 1)
+            {
+                return new string(str[0], n);
+            }
+
+            var sb = new StringBuilder(n * str.Length);
+            for (var i = 0; i < n; ++i)
+            {
+                sb.Append(str);
+            }
+
+            return sb.ToString();
+        }
     }
 }

+ 1 - 1
Jint/Native/Symbol/SymbolConstructor.cs

@@ -71,7 +71,7 @@ namespace Jint.Native.Symbol
                 return descString;
             }
 
-            var value = new JsSymbol(TypeConverter.ToString(description));
+            var value = new JsSymbol(description);
             return value;
         }
 

+ 5 - 0
Jint/Runtime/ExceptionHelper.cs

@@ -58,6 +58,11 @@ namespace Jint.Runtime
             throw new JavaScriptException(engine.RangeError, message);
         }
 
+        public static T ThrowRangeErrorNoEngine<T>(string message)
+        {
+            throw new RangeErrorException(message);
+        }
+
         public static void ThrowRangeError(Engine engine, string message = null)
         {
             throw new JavaScriptException(engine.RangeError, message);

+ 1 - 1
Jint/Runtime/ExpressionIntepreter.cs

@@ -747,7 +747,7 @@ namespace Jint.Runtime
             {
                 var propertyNameReference = _engine.EvaluateExpression(memberExpression.Property);
                 var propertyNameValue = _engine.GetValue(propertyNameReference, true);
-                propertyNameString = TypeConverter.ToString(propertyNameValue);
+                propertyNameString = TypeConverter.ToPropertyKey(propertyNameValue);
             }
 
             TypeConverter.CheckObjectCoercible(_engine, baseValue, memberExpression, baseReference);

+ 14 - 0
Jint/Runtime/RangeErrorException.cs

@@ -0,0 +1,14 @@
+using System;
+
+namespace Jint.Runtime
+{
+    /// <summary>
+    /// Workaround for situation where engine is not easily accessible.
+    /// </summary>
+    internal sealed class RangeErrorException : Exception
+    {
+        public RangeErrorException(string message) : base(message)
+        {
+        }
+    }
+}

+ 15 - 0
Jint/Runtime/StatementInterpreter.cs

@@ -348,6 +348,11 @@ namespace Jint.Runtime
                 var error = _engine.TypeError.Construct(new JsValue[] {e.Message});
                 c = new Completion(CompletionType.Throw, error, null, withStatement.Location);
             }
+            catch (RangeErrorException e)
+            {
+                var error = _engine.RangeError.Construct(new JsValue[] {e.Message});
+                c = new Completion(CompletionType.Throw, error, null, withStatement.Location);
+            }
             finally
             {
                 _engine.UpdateLexicalEnvironment(oldEnv);
@@ -466,6 +471,11 @@ namespace Jint.Runtime
                 var error = _engine.TypeError.Construct(new JsValue[] {e.Message});
                 c = new Completion(CompletionType.Throw, error, null, s?.Location);
             }
+            catch (RangeErrorException e)
+            {
+                var error = _engine.RangeError.Construct(new JsValue[] {e.Message});
+                c = new Completion(CompletionType.Throw, error, null, s?.Location);
+            }
 
             return new Completion(c.Type, c.GetValueOrDefault(), c.Identifier);
         }
@@ -497,6 +507,11 @@ namespace Jint.Runtime
                 var error = _engine.TypeError.Construct(new JsValue[] {e.Message});
                 return new Completion(CompletionType.Throw, error, null, s?.Location);
             }
+            catch (RangeErrorException e)
+            {
+                var error = _engine.RangeError.Construct(new JsValue[] {e.Message});
+                return new Completion(CompletionType.Throw, error, null, s?.Location);
+            }
         }
 
         /// <summary>

+ 16 - 4
Jint/Runtime/TypeConverter.cs

@@ -294,10 +294,22 @@ namespace Jint.Runtime
         }
 
         /// <summary>
-        /// http://www.ecma-international.org/ecma-262/5.1/#sec-9.8
+        /// http://www.ecma-international.org/ecma-262/6.0/#sec-topropertykey
+        /// </summary>
+        public static string ToPropertyKey(JsValue o)
+        {
+            var key = ToPrimitive(o, Types.String);
+            if (key is JsSymbol s)
+            {
+                return s._value;
+            }
+
+            return ToString(key);
+        }
+
+        /// <summary>
+        /// http://www.ecma-international.org/ecma-262/6.0/#sec-tostring
         /// </summary>
-        /// <param name="o"></param>
-        /// <returns></returns>
         public static string ToString(JsValue o)
         {
             switch (o._type)
@@ -309,7 +321,7 @@ namespace Jint.Runtime
                 case Types.Number:
                     return ToString(((JsNumber) o)._value);
                 case Types.Symbol:
-                    return o.AsSymbol();
+                    return ExceptionHelper.ThrowTypeErrorNoEngine<string>("Cannot convert a Symbol value to a string");
                 case Types.Undefined:
                     return Undefined.Text;
                 case Types.Null:

+ 1 - 1
README.md

@@ -172,7 +172,7 @@ ES6 features which are being implemented:
 - [ ] [promises](https://github.com/lukehoban/es6features/blob/master/README.md#promises)
 - [ ] [math APIs](https://github.com/lukehoban/es6features/blob/master/README.md#math--number--string--array--object-apis)
 - [ ] [number APIs](https://github.com/lukehoban/es6features/blob/master/README.md#math--number--string--array--object-apis)
-- [ ] [string APIs](https://github.com/lukehoban/es6features/blob/master/README.md#math--number--string--array--object-apis)
+- [x] [string APIs](https://github.com/lukehoban/es6features/blob/master/README.md#math--number--string--array--object-apis)
 - [x] [array APIs](https://github.com/lukehoban/es6features/blob/master/README.md#math--number--string--array--object-apis)
 - [ ] [object APIs](https://github.com/lukehoban/es6features/blob/master/README.md#math--number--string--array--object-apis)
 - [x] [binary and octal literals](https://github.com/lukehoban/es6features/blob/master/README.md#binary-and-octal-literals)