StringConstructor.cs 6.6 KB

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