StringConstructor.cs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. using System.Collections.Generic;
  2. using Jint.Collections;
  3. using Jint.Native.Array;
  4. using Jint.Native.Function;
  5. using Jint.Native.Object;
  6. using Jint.Pooling;
  7. using Jint.Runtime;
  8. using Jint.Runtime.Descriptors;
  9. using Jint.Runtime.Interop;
  10. namespace Jint.Native.String
  11. {
  12. public sealed class StringConstructor : FunctionInstance, IConstructor
  13. {
  14. private static readonly JsString _functionName = new JsString("String");
  15. public StringConstructor(Engine engine)
  16. : base(engine, _functionName, strict: false)
  17. {
  18. }
  19. public static StringConstructor CreateStringConstructor(Engine engine)
  20. {
  21. var obj = new StringConstructor(engine)
  22. {
  23. Extensible = true,
  24. Prototype = engine.Function.PrototypeObject
  25. };
  26. // The value of the [[Prototype]] internal property of the String constructor is the Function prototype object
  27. obj.PrototypeObject = StringPrototype.CreatePrototypeObject(engine, obj);
  28. obj._length = PropertyDescriptor.AllForbiddenDescriptor.NumberOne;
  29. // The initial value of String.prototype is the String prototype object
  30. obj._prototype = new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden);
  31. return obj;
  32. }
  33. protected override void Initialize()
  34. {
  35. _properties = new StringDictionarySlim<PropertyDescriptor>(3)
  36. {
  37. ["fromCharCode"] = new PropertyDescriptor(new PropertyDescriptor(new ClrFunctionInstance(Engine, "fromCharCode", FromCharCode, 1), PropertyFlag.NonEnumerable)),
  38. ["fromCodePoint"] = new PropertyDescriptor(new PropertyDescriptor(new ClrFunctionInstance(Engine, "fromCodePoint", FromCodePoint, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable)),
  39. ["raw"] = new PropertyDescriptor(new PropertyDescriptor(new ClrFunctionInstance(Engine, "raw", Raw, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable))
  40. };
  41. }
  42. private static JsValue FromCharCode(JsValue thisObj, JsValue[] arguments)
  43. {
  44. var chars = new char[arguments.Length];
  45. for (var i = 0; i < chars.Length; i++ )
  46. {
  47. chars[i] = (char)TypeConverter.ToUint16(arguments[i]);
  48. }
  49. return JsString.Create(new string(chars));
  50. }
  51. private static JsValue FromCodePoint(JsValue thisObj, JsValue[] arguments)
  52. {
  53. var codeUnits = new List<JsValue>();
  54. string result = "";
  55. for (var i = 0; i < arguments.Length; i++ )
  56. {
  57. var codePoint = TypeConverter.ToNumber(arguments[i]);
  58. if (codePoint < 0
  59. || codePoint > 0x10FFFF
  60. || double.IsInfinity(codePoint)
  61. || double.IsNaN(codePoint)
  62. || TypeConverter.ToInt32(codePoint) != codePoint)
  63. {
  64. return ExceptionHelper.ThrowRangeErrorNoEngine<JsValue>("Invalid code point " + codePoint);
  65. }
  66. var point = (uint) codePoint;
  67. if (point <= 0xFFFF)
  68. {
  69. // BMP code point
  70. codeUnits.Add(JsNumber.Create(point));
  71. }
  72. else
  73. {
  74. // Astral code point; split in surrogate halves
  75. // https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
  76. point -= 0x10000;
  77. codeUnits.Add(JsNumber.Create((point >> 10) + 0xD800)); // highSurrogate
  78. codeUnits.Add(JsNumber.Create((point % 0x400) + 0xDC00)); // lowSurrogate
  79. }
  80. if (codeUnits.Count >= 0x3fff)
  81. {
  82. result += FromCharCode(null, codeUnits.ToArray());
  83. codeUnits.Clear();
  84. }
  85. }
  86. return result + FromCharCode(null, codeUnits.ToArray());
  87. }
  88. /// <summary>
  89. /// https://www.ecma-international.org/ecma-262/6.0/#sec-string.raw
  90. /// </summary>
  91. private JsValue Raw(JsValue thisObj, JsValue[] arguments)
  92. {
  93. var cooked = TypeConverter.ToObject(_engine, arguments.At(0));
  94. var raw = TypeConverter.ToObject(_engine, cooked.Get("raw"));
  95. var operations = ArrayPrototype.ArrayOperations.For(raw);
  96. var length = operations.GetLength();
  97. if (length <= 0)
  98. {
  99. return JsString.Empty;
  100. }
  101. using (var result = StringBuilderPool.Rent())
  102. {
  103. for (var i = 0; i < length; i++)
  104. {
  105. if (i > 0)
  106. {
  107. if (i < arguments.Length && !arguments[i].IsUndefined())
  108. {
  109. result.Builder.Append(TypeConverter.ToString(arguments[i]));
  110. }
  111. }
  112. result.Builder.Append(TypeConverter.ToString(operations.Get((ulong) i)));
  113. }
  114. return result.ToString();
  115. }
  116. }
  117. public override JsValue Call(JsValue thisObject, JsValue[] arguments)
  118. {
  119. if (arguments.Length == 0)
  120. {
  121. return JsString.Empty;
  122. }
  123. var arg = arguments[0];
  124. var str = arg is JsSymbol s
  125. ? s.ToString()
  126. : TypeConverter.ToString(arg);
  127. return JsString.Create(str);
  128. }
  129. /// <summary>
  130. /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.7.2.1
  131. /// </summary>
  132. /// <param name="arguments"></param>
  133. /// <returns></returns>
  134. public ObjectInstance Construct(JsValue[] arguments)
  135. {
  136. string value = "";
  137. if (arguments.Length > 0)
  138. {
  139. value = TypeConverter.ToString(arguments[0]);
  140. }
  141. return Construct(value);
  142. }
  143. public StringPrototype PrototypeObject { get; private set; }
  144. public StringInstance Construct(string value)
  145. {
  146. return Construct(JsString.Create(value));
  147. }
  148. public StringInstance Construct(JsString value)
  149. {
  150. var instance = new StringInstance(Engine)
  151. {
  152. Prototype = PrototypeObject,
  153. PrimitiveValue = value,
  154. Extensible = true,
  155. _length = PropertyDescriptor.AllForbiddenDescriptor.ForNumber(value.Length)
  156. };
  157. return instance;
  158. }
  159. }
  160. }