diff --git a/Jint.Tests/Runtime/FunctionTests.cs b/Jint.Tests/Runtime/FunctionTests.cs index cf38ad7cf5..5d9522310f 100644 --- a/Jint.Tests/Runtime/FunctionTests.cs +++ b/Jint.Tests/Runtime/FunctionTests.cs @@ -1,5 +1,4 @@ using Jint.Native; -using Jint.Native.Array; using Jint.Native.Function; using Jint.Runtime; using Jint.Runtime.Interop; diff --git a/Jint.Tests/Runtime/RegExpTests.cs b/Jint.Tests/Runtime/RegExpTests.cs index 91598be931..20d3d3dc2d 100644 --- a/Jint.Tests/Runtime/RegExpTests.cs +++ b/Jint.Tests/Runtime/RegExpTests.cs @@ -1,6 +1,5 @@ using System.Text.RegularExpressions; using Jint.Native; -using Jint.Native.Array; namespace Jint.Tests.Runtime; diff --git a/Jint/Native/AggregateError/AggregateErrorConstructor.cs b/Jint/Native/AggregateError/AggregateErrorConstructor.cs index 717dfcb534..3443d917bc 100644 --- a/Jint/Native/AggregateError/AggregateErrorConstructor.cs +++ b/Jint/Native/AggregateError/AggregateErrorConstructor.cs @@ -49,7 +49,7 @@ public override ObjectInstance Construct(JsValue[] arguments, JsValue newTarget) if (!message.IsUndefined()) { var msg = TypeConverter.ToString(message); - o.CreateNonEnumerableDataPropertyOrThrow("message", msg); + o.CreateNonEnumerableDataPropertyOrThrow(CommonProperties.Message, msg); } o.InstallErrorCause(options); diff --git a/Jint/Native/Array/ArrayConstructor.cs b/Jint/Native/Array/ArrayConstructor.cs index be3cbe5317..920ee82639 100644 --- a/Jint/Native/Array/ArrayConstructor.cs +++ b/Jint/Native/Array/ArrayConstructor.cs @@ -82,22 +82,21 @@ private JsValue From(JsValue thisObject, JsValue[] arguments) return instance; } - var objectInstance = TypeConverter.ToObject(_realm, items); - if (objectInstance is IObjectWrapper { Target: IEnumerable enumerable }) + if (items is IObjectWrapper { Target: IEnumerable enumerable }) { return ConstructArrayFromIEnumerable(enumerable); } - return ConstructArrayFromArrayLike(thisObject, objectInstance, callable, thisArg); + var source = ArrayOperations.For(_realm, items, forWrite: false); + return ConstructArrayFromArrayLike(thisObject, source, callable, thisArg); } private ObjectInstance ConstructArrayFromArrayLike( JsValue thisObj, - ObjectInstance objectInstance, + ArrayOperations source, ICallable? callable, JsValue thisArg) { - var source = ArrayOperations.For(objectInstance); var length = source.GetLength(); ObjectInstance a; @@ -311,7 +310,7 @@ private JsArray Construct(JsValue[] arguments, ulong capacity, ObjectInstance pr break; case JsArray array: // direct copy - instance = (JsArray) ConstructArrayFromArrayLike(Undefined, array, null, this); + instance = (JsArray) ConstructArrayFromArrayLike(Undefined, ArrayOperations.For(array), callable: null, this); break; default: instance = ArrayCreate(capacity, prototypeObject); diff --git a/Jint/Native/Array/ArrayOperations.cs b/Jint/Native/Array/ArrayOperations.cs index 3834353f52..8b8d626d44 100644 --- a/Jint/Native/Array/ArrayOperations.cs +++ b/Jint/Native/Array/ArrayOperations.cs @@ -1,6 +1,7 @@ using System.Collections; using Jint.Native.Number; using Jint.Native.Object; +using Jint.Native.String; using Jint.Native.TypedArray; using Jint.Runtime; @@ -11,25 +12,37 @@ internal abstract class ArrayOperations : IEnumerable protected internal const ulong MaxArrayLength = 4294967295; protected internal const ulong MaxArrayLikeLength = NumberConstructor.MaxSafeInteger; + public static ArrayOperations For(Realm realm, JsValue value, bool forWrite) + { + if (!forWrite) + { + if (value.IsString()) + { + return new JsStringOperations(realm, (JsString) value); + } + + if (value is StringInstance stringInstance) + { + return new JsStringOperations(realm, stringInstance); + } + } + + return For(TypeConverter.ToObject(realm, value)); + } + public static ArrayOperations For(ObjectInstance instance) { if (instance is JsArray { CanUseFastAccess: true } arrayInstance) { - return new ArrayInstanceOperations(arrayInstance); + return new JsArrayOperations(arrayInstance); } if (instance is JsTypedArray typedArrayInstance) { - return new TypedArrayInstanceOperations(typedArrayInstance); + return new JsTypedArrayOperations(typedArrayInstance); } - return new ObjectInstanceOperations(instance); - } - - public static ArrayOperations For(Realm realm, JsValue thisObj) - { - var instance = TypeConverter.ToObject(realm, thisObj); - return For(instance); + return new ObjectOperations(instance); } public abstract ObjectInstance Target { get; } @@ -135,9 +148,9 @@ public void Reset() } } - private sealed class ObjectInstanceOperations : ArrayOperations + private sealed class ObjectOperations : ArrayOperations { - public ObjectInstanceOperations(ObjectInstance target) : base(target) + public ObjectOperations(ObjectInstance target) : base(target) { } @@ -204,9 +217,9 @@ public override void DeletePropertyOrThrow(ulong index) public override bool HasProperty(ulong index) => Target.HasProperty(index); } - private sealed class ArrayInstanceOperations : ArrayOperations + private sealed class JsArrayOperations : ArrayOperations { - public ArrayInstanceOperations(JsArray target) : base(target) + public JsArrayOperations(JsArray target) : base(target) { } @@ -274,11 +287,11 @@ public override void Set(ulong index, JsValue value, bool updateLength = false, public override bool HasProperty(ulong index) => _target.HasProperty(index); } - private sealed class TypedArrayInstanceOperations : ArrayOperations + private sealed class JsTypedArrayOperations : ArrayOperations { private readonly JsTypedArray _target; - public TypedArrayInstanceOperations(JsTypedArray target) + public JsTypedArrayOperations(JsTypedArray target) { _target = target; } @@ -339,6 +352,59 @@ public override void DeletePropertyOrThrow(ulong index) public override bool HasProperty(ulong index) => _target.HasProperty(index); } + private sealed class JsStringOperations : ArrayOperations + { + private readonly Realm _realm; + private readonly JsString _target; + private ObjectInstance? _wrappedTarget; + + public JsStringOperations(Realm realm, JsString target) + { + _realm = realm; + _target = target; + } + + public JsStringOperations(Realm realm, StringInstance stringInstance) : this(realm, stringInstance.StringData) + { + _wrappedTarget = stringInstance; + } + + public override ObjectInstance Target => _wrappedTarget ??= _realm.Intrinsics.String.Construct(_target); + + public override ulong GetSmallestIndex(ulong length) => 0; + + public override uint GetLength() => (uint) _target.Length; + + public override ulong GetLongLength() => GetLength(); + + public override void SetLength(ulong length) => throw new NotSupportedException(); + + public override void EnsureCapacity(ulong capacity) + { + } + + public override JsValue Get(ulong index) => index < (ulong) _target.Length ? _target[(int) index] : JsValue.Undefined; + + public override bool TryGetValue(ulong index, out JsValue value) + { + if (index < (ulong) _target.Length) + { + value = _target[(int) index]; + return true; + } + + value = JsValue.Undefined; + return false; + } + + public override bool HasProperty(ulong index) => index < (ulong) _target.Length; + + public override void CreateDataPropertyOrThrow(ulong index, JsValue value) => throw new NotSupportedException(); + + public override void Set(ulong index, JsValue value, bool updateLength = false, bool throwOnError = true) => throw new NotSupportedException(); + + public override void DeletePropertyOrThrow(ulong index) => throw new NotSupportedException(); + } } /// diff --git a/Jint/Native/Array/ArrayPrototype.cs b/Jint/Native/Array/ArrayPrototype.cs index 9e5920f150..d23cb30d99 100644 --- a/Jint/Native/Array/ArrayPrototype.cs +++ b/Jint/Native/Array/ArrayPrototype.cs @@ -323,7 +323,7 @@ private JsValue CopyWithin(JsValue thisObject, JsValue[] arguments) /// private JsValue LastIndexOf(JsValue thisObject, JsValue[] arguments) { - var o = ArrayOperations.For(_realm, thisObject); + var o = ArrayOperations.For(_realm, thisObject, forWrite: false); var len = o.GetLongLength(); if (len == 0) { @@ -379,7 +379,7 @@ private JsValue Reduce(JsValue thisObject, JsValue[] arguments) var callbackfn = arguments.At(0); var initialValue = arguments.At(1); - var o = ArrayOperations.For(_realm, thisObject); + var o = ArrayOperations.For(_realm, thisObject, forWrite: false); var len = o.GetLength(); var callable = GetCallable(callbackfn); @@ -441,7 +441,7 @@ private JsValue Filter(JsValue thisObject, JsValue[] arguments) var callbackfn = arguments.At(0); var thisArg = arguments.At(1); - var o = ArrayOperations.For(_realm, thisObject); + var o = ArrayOperations.For(_realm, thisObject, forWrite: false); var len = o.GetLength(); var callable = GetCallable(callbackfn); @@ -484,7 +484,7 @@ private JsValue Map(JsValue thisObject, JsValue[] arguments) return arrayInstance.Map(arguments); } - var o = ArrayOperations.For(_realm, thisObject); + var o = ArrayOperations.For(_realm, thisObject, forWrite: false); var len = o.GetLongLength(); if (len > ArrayOperations.MaxArrayLength) @@ -639,7 +639,7 @@ private JsValue ForEach(JsValue thisObject, JsValue[] arguments) var callbackfn = arguments.At(0); var thisArg = arguments.At(1); - var o = ArrayOperations.For(_realm, thisObject); + var o = ArrayOperations.For(_realm, thisObject, forWrite: false); var len = o.GetLength(); var callable = GetCallable(callbackfn); @@ -665,7 +665,7 @@ private JsValue ForEach(JsValue thisObject, JsValue[] arguments) /// private JsValue Includes(JsValue thisObject, JsValue[] arguments) { - var o = ArrayOperations.For(_realm, thisObject); + var o = ArrayOperations.For(_realm, thisObject, forWrite: false); var len = (long) o.GetLongLength(); if (len == 0) @@ -722,7 +722,7 @@ private JsValue Some(JsValue thisObject, JsValue[] arguments) /// private JsValue Every(JsValue thisObject, JsValue[] arguments) { - var o = ArrayOperations.For(_realm, thisObject); + var o = ArrayOperations.For(_realm, thisObject, forWrite: false); ulong len = o.GetLongLength(); if (len == 0) @@ -759,7 +759,7 @@ private JsValue Every(JsValue thisObject, JsValue[] arguments) /// private JsValue IndexOf(JsValue thisObject, JsValue[] arguments) { - var o = ArrayOperations.For(_realm, thisObject); + var o = ArrayOperations.For(_realm, thisObject, forWrite: false); var len = o.GetLongLength(); if (len == 0) { @@ -896,7 +896,7 @@ private JsValue Splice(JsValue thisObject, JsValue[] arguments) var deleteCount = arguments.At(1); var obj = TypeConverter.ToObject(_realm, thisObject); - var o = ArrayOperations.For(_realm, obj); + var o = ArrayOperations.For(_realm, obj, forWrite: false); var len = o.GetLongLength(); var relativeStart = TypeConverter.ToInteger(start); @@ -1012,7 +1012,7 @@ private JsValue Splice(JsValue thisObject, JsValue[] arguments) /// private JsValue Unshift(JsValue thisObject, JsValue[] arguments) { - var o = ArrayOperations.For(_realm, thisObject); + var o = ArrayOperations.For(_realm, thisObject, forWrite: true); var len = o.GetLongLength(); var argCount = (uint) arguments.Length; @@ -1104,7 +1104,7 @@ private JsValue Slice(JsValue thisObject, JsValue[] arguments) var start = arguments.At(0); var end = arguments.At(1); - var o = ArrayOperations.For(_realm, thisObject); + var o = ArrayOperations.For(_realm, thisObject, forWrite: false); var len = o.GetLongLength(); var relativeStart = TypeConverter.ToInteger(start); @@ -1164,7 +1164,7 @@ private JsValue Slice(JsValue thisObject, JsValue[] arguments) private JsValue Shift(JsValue thisObject, JsValue[] arg2) { - var o = ArrayOperations.For(_realm, thisObject); + var o = ArrayOperations.For(_realm, thisObject, forWrite: true); var len = o.GetLength(); if (len == 0) { @@ -1197,7 +1197,7 @@ private JsValue Shift(JsValue thisObject, JsValue[] arg2) /// private JsValue Reverse(JsValue thisObject, JsValue[] arguments) { - var o = ArrayOperations.For(_realm, thisObject); + var o = ArrayOperations.For(_realm, thisObject, forWrite: true); var len = o.GetLongLength(); var middle = (ulong) System.Math.Floor(len / 2.0); uint lower = 0; @@ -1241,7 +1241,7 @@ private JsValue Reverse(JsValue thisObject, JsValue[] arguments) private JsValue Join(JsValue thisObject, JsValue[] arguments) { var separator = arguments.At(0); - var o = ArrayOperations.For(_realm, thisObject); + var o = ArrayOperations.For(_realm, thisObject, forWrite: false); var len = o.GetLength(); var sep = TypeConverter.ToString(separator.IsUndefined() ? JsString.CommaString : separator); @@ -1281,7 +1281,7 @@ static string StringFromJsValue(JsValue value) private JsValue ToLocaleString(JsValue thisObject, JsValue[] arguments) { - var array = ArrayOperations.For(_realm, thisObject); + var array = ArrayOperations.For(_realm, thisObject, forWrite: false); var len = array.GetLength(); const string Separator = ","; if (len == 0) @@ -1434,7 +1434,7 @@ private JsValue ToSpliced(JsValue thisObject, JsValue[] arguments) var start = arguments.At(0); var deleteCount = arguments.At(1); - var o = ArrayOperations.For(_realm, TypeConverter.ToObject(_realm, thisObject)); + var o = ArrayOperations.For(_realm, TypeConverter.ToObject(_realm, thisObject), forWrite: false); var len = o.GetLongLength(); var relativeStart = TypeConverter.ToIntegerOrInfinity(start); @@ -1554,7 +1554,7 @@ private JsValue ReduceRight(JsValue thisObject, JsValue[] arguments) var callbackfn = arguments.At(0); var initialValue = arguments.At(1); - var o = ArrayOperations.For(TypeConverter.ToObject(_realm, thisObject)); + var o = ArrayOperations.For(_realm, thisObject, forWrite: false); var len = o.GetLongLength(); var callable = GetCallable(callbackfn); @@ -1640,7 +1640,7 @@ public JsValue Pop(JsValue thisObject, JsValue[] arguments) return array.Pop(); } - var o = ArrayOperations.For(_realm, thisObject); + var o = ArrayOperations.For(_realm, thisObject, forWrite: true); ulong len = o.GetLongLength(); if (len == 0) { diff --git a/Jint/Native/Constructor.cs b/Jint/Native/Constructor.cs index 5bacaf7837..1afc8c9b18 100644 --- a/Jint/Native/Constructor.cs +++ b/Jint/Native/Constructor.cs @@ -1,4 +1,3 @@ -using Jint.Native.Function; using Jint.Native.Object; using Jint.Runtime; diff --git a/Jint/Native/Error/ErrorConstructor.cs b/Jint/Native/Error/ErrorConstructor.cs index bac3ab9df2..a03b7382d4 100644 --- a/Jint/Native/Error/ErrorConstructor.cs +++ b/Jint/Native/Error/ErrorConstructor.cs @@ -48,9 +48,8 @@ public override ObjectInstance Construct(JsValue[] arguments, JsValue newTarget) var jsValue = arguments.At(0); if (!jsValue.IsUndefined()) { - var msg = TypeConverter.ToString(jsValue); - var msgDesc = new PropertyDescriptor(msg, true, false, true); - o.DefinePropertyOrThrow("message", msgDesc); + var msg = TypeConverter.ToJsString(jsValue); + o.CreateNonEnumerableDataPropertyOrThrow(CommonProperties.Message, msg); } var stackString = BuildStackString(); diff --git a/Jint/Native/JsString.cs b/Jint/Native/JsString.cs index 6d493d7f26..3f4134d58c 100644 --- a/Jint/Native/JsString.cs +++ b/Jint/Native/JsString.cs @@ -1,6 +1,9 @@ using System.Collections.Concurrent; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Text; +using Jint.Native.Generator; +using Jint.Native.Iterator; using Jint.Runtime; namespace Jint.Native; @@ -286,6 +289,21 @@ internal string Substring(int startIndex) return ToString().Substring(startIndex); } + internal override bool TryGetIterator( + Realm realm, + [NotNullWhen(true)] out IteratorInstance? iterator, + GeneratorKind hint = GeneratorKind.Sync, + ICallable? method = null) + { + if (realm.Intrinsics.String.PrototypeObject.HasOriginalIterator) + { + iterator = new IteratorInstance.StringIterator(realm.GlobalEnv._engine, ToString()); + return true; + } + + return base.TryGetIterator(realm, out iterator, hint, method); + } + public sealed override bool Equals(object? obj) => Equals(obj as JsString); public sealed override bool Equals(JsValue? other) => Equals(other as JsString); diff --git a/Jint/Native/JsValue.cs b/Jint/Native/JsValue.cs index f191b4224e..23e7973285 100644 --- a/Jint/Native/JsValue.cs +++ b/Jint/Native/JsValue.cs @@ -68,7 +68,11 @@ internal IteratorInstance GetIteratorFromMethod(Realm realm, ICallable method) } [Pure] - internal bool TryGetIterator(Realm realm, [NotNullWhen(true)] out IteratorInstance? iterator, GeneratorKind hint = GeneratorKind.Sync, ICallable? method = null) + internal virtual bool TryGetIterator( + Realm realm, + [NotNullWhen(true)] out IteratorInstance? iterator, + GeneratorKind hint = GeneratorKind.Sync, + ICallable? method = null) { var obj = TypeConverter.ToObject(realm, this); diff --git a/Jint/Native/RegExp/RegExpPrototype.cs b/Jint/Native/RegExp/RegExpPrototype.cs index 9ddfcbf2e6..e2fa5c8316 100644 --- a/Jint/Native/RegExp/RegExpPrototype.cs +++ b/Jint/Native/RegExp/RegExpPrototype.cs @@ -24,6 +24,11 @@ internal sealed class RegExpPrototype : Prototype private static readonly JsString DefaultSource = new("(?:)"); internal static readonly JsString PropertyFlags = new("flags"); private static readonly JsString PropertyGroups = new("groups"); + private static readonly JsString PropertyIgnoreCase = new("ignoreCase"); + private static readonly JsString PropertyMultiline = new("multiline"); + private static readonly JsString PropertyDotAll = new("dotAll"); + private static readonly JsString PropertyUnicode = new("unicode"); + private static readonly JsString PropertyUnicodeSets = new("unicodeSets"); private readonly RegExpConstructor _constructor; private readonly Func _defaultExec; @@ -591,11 +596,11 @@ static string AddFlagIfPresent(JsValue o, JsValue p, char flag, string s) var result = AddFlagIfPresent(r, "hasIndices", 'd', ""); result = AddFlagIfPresent(r, PropertyGlobal, 'g', result); - result = AddFlagIfPresent(r, "ignoreCase", 'i', result); - result = AddFlagIfPresent(r, "multiline", 'm', result); - result = AddFlagIfPresent(r, "dotAll", 's', result); - result = AddFlagIfPresent(r, "unicode", 'u', result); - result = AddFlagIfPresent(r, "unicodeSets", 'v', result); + result = AddFlagIfPresent(r, PropertyIgnoreCase, 'i', result); + result = AddFlagIfPresent(r, PropertyMultiline, 'm', result); + result = AddFlagIfPresent(r, PropertyDotAll, 's', result); + result = AddFlagIfPresent(r, PropertyUnicode, 'u', result); + result = AddFlagIfPresent(r, PropertyUnicodeSets, 'v', result); result = AddFlagIfPresent(r, PropertySticky, 'y', result); return result; diff --git a/Jint/Native/String/StringConstructor.cs b/Jint/Native/String/StringConstructor.cs index 1eaed87b87..b693a5f076 100644 --- a/Jint/Native/String/StringConstructor.cs +++ b/Jint/Native/String/StringConstructor.cs @@ -204,11 +204,6 @@ public override ObjectInstance Construct(JsValue[] arguments, JsValue newTarget) return StringCreate(s, GetPrototypeFromConstructor(newTarget, static intrinsics => intrinsics.String.PrototypeObject)); } - public StringInstance Construct(string value) - { - return Construct(JsString.Create(value)); - } - public StringInstance Construct(JsString value) { return StringCreate(value, PrototypeObject); diff --git a/Jint/Native/String/StringPrototype.cs b/Jint/Native/String/StringPrototype.cs index 1273d36f6b..7f636a4852 100644 --- a/Jint/Native/String/StringPrototype.cs +++ b/Jint/Native/String/StringPrototype.cs @@ -22,6 +22,7 @@ internal sealed class StringPrototype : StringInstance { private readonly Realm _realm; private readonly StringConstructor _constructor; + internal ClrFunction? _originalIteratorFunction; internal StringPrototype( Engine engine, @@ -86,13 +87,16 @@ protected override void Initialize() }; SetProperties(properties); + _originalIteratorFunction = new ClrFunction(Engine, "[Symbol.iterator]", Iterator, 0, lengthFlags); var symbols = new SymbolDictionary(1) { - [GlobalSymbolRegistry.Iterator] = new PropertyDescriptor(new ClrFunction(Engine, "[Symbol.iterator]", Iterator, 0, lengthFlags), propertyFlags) + [GlobalSymbolRegistry.Iterator] = new PropertyDescriptor(_originalIteratorFunction, propertyFlags) }; SetSymbols(symbols); } + internal override bool HasOriginalIterator => ReferenceEquals(Get(GlobalSymbolRegistry.Iterator), _originalIteratorFunction); + private ObjectInstance Iterator(JsValue thisObject, JsValue[] arguments) { TypeConverter.CheckObjectCoercible(_engine, thisObject); diff --git a/Jint/Native/TypedArray/IntrinsicTypedArrayPrototype.cs b/Jint/Native/TypedArray/IntrinsicTypedArrayPrototype.cs index dc49065246..7b85bf1ce2 100644 --- a/Jint/Native/TypedArray/IntrinsicTypedArrayPrototype.cs +++ b/Jint/Native/TypedArray/IntrinsicTypedArrayPrototype.cs @@ -1100,7 +1100,7 @@ private void SetTypedArrayFromArrayLike(JsTypedArray target, int targetOffset, J targetBuffer.AssertNotDetached(); var targetLength = target._arrayLength; - var src = ArrayOperations.For(TypeConverter.ToObject(_realm, source)); + var src = ArrayOperations.For(_realm, source, forWrite: false); var srcLength = src.GetLength(); if (double.IsNegativeInfinity(targetOffset)) diff --git a/Jint/Runtime/Interop/ClrFunction.cs b/Jint/Runtime/Interop/ClrFunction.cs index b44600aec9..fadb94c3b2 100644 --- a/Jint/Runtime/Interop/ClrFunction.cs +++ b/Jint/Runtime/Interop/ClrFunction.cs @@ -20,7 +20,7 @@ public ClrFunction( Func func, int length = 0, PropertyFlag lengthFlags = PropertyFlag.AllForbidden) - : base(engine, engine.Realm, new JsString(name)) + : base(engine, engine.Realm, JsString.CachedCreate(name)) { _func = func; diff --git a/Jint/Runtime/TypeConverter.cs b/Jint/Runtime/TypeConverter.cs index c8745c3864..94f19a1c8e 100644 --- a/Jint/Runtime/TypeConverter.cs +++ b/Jint/Runtime/TypeConverter.cs @@ -1009,19 +1009,20 @@ internal static bool IsIntegralNumber(double value) private static ObjectInstance ToObjectNonObject(Realm realm, JsValue value) { var type = value._type & ~InternalTypes.InternalFlags; + var intrinsics = realm.Intrinsics; switch (type) { case InternalTypes.Boolean: - return realm.Intrinsics.Boolean.Construct((JsBoolean) value); + return intrinsics.Boolean.Construct((JsBoolean) value); case InternalTypes.Number: case InternalTypes.Integer: - return realm.Intrinsics.Number.Construct((JsNumber) value); + return intrinsics.Number.Construct((JsNumber) value); case InternalTypes.BigInt: - return realm.Intrinsics.BigInt.Construct((JsBigInt) value); + return intrinsics.BigInt.Construct((JsBigInt) value); case InternalTypes.String: - return realm.Intrinsics.String.Construct(value.ToString()); + return intrinsics.String.Construct(value as JsString ?? JsString.Create(value.ToString())); case InternalTypes.Symbol: - return realm.Intrinsics.Symbol.Construct((JsSymbol) value); + return intrinsics.Symbol.Construct((JsSymbol) value); case InternalTypes.Null: case InternalTypes.Undefined: ExceptionHelper.ThrowTypeError(realm, "Cannot convert undefined or null to object");