123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462 |
- using System.Diagnostics;
- using System.Runtime.CompilerServices;
- using Jint.Native;
- using Jint.Native.Object;
- namespace Jint.Runtime.Descriptors;
- [DebuggerDisplay("Value: {Value}, Flags: {Flags}")]
- public class PropertyDescriptor
- {
- public static readonly PropertyDescriptor Undefined = new UndefinedPropertyDescriptor();
- internal PropertyFlag _flags;
- internal JsValue? _value;
- public PropertyDescriptor() : this(PropertyFlag.None)
- {
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- protected PropertyDescriptor(PropertyFlag flags)
- {
- _flags = flags & ~PropertyFlag.NonData;
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- protected internal PropertyDescriptor(JsValue? value, PropertyFlag flags) : this(flags)
- {
- if ((_flags & PropertyFlag.CustomJsValue) != PropertyFlag.None)
- {
- #pragma warning disable MA0056
- CustomValue = value;
- #pragma warning restore MA0056
- }
- _value = value;
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public PropertyDescriptor(JsValue? value, bool? writable, bool? enumerable, bool? configurable)
- {
- if ((_flags & PropertyFlag.CustomJsValue) != PropertyFlag.None)
- {
- #pragma warning disable MA0056
- CustomValue = value;
- #pragma warning restore MA0056
- }
- _value = value;
- if (writable != null)
- {
- Writable = writable.Value;
- WritableSet = true;
- }
- if (enumerable != null)
- {
- Enumerable = enumerable.Value;
- EnumerableSet = true;
- }
- if (configurable != null)
- {
- Configurable = configurable.Value;
- ConfigurableSet = true;
- }
- }
- public PropertyDescriptor(PropertyDescriptor descriptor)
- {
- Value = descriptor.Value;
- Enumerable = descriptor.Enumerable;
- EnumerableSet = descriptor.EnumerableSet;
- Configurable = descriptor.Configurable;
- ConfigurableSet = descriptor.ConfigurableSet;
- Writable = descriptor.Writable;
- WritableSet = descriptor.WritableSet;
- }
- public virtual JsValue? Get => null;
- public virtual JsValue? Set => null;
- public bool Enumerable
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => (_flags & PropertyFlag.Enumerable) != PropertyFlag.None;
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- set
- {
- _flags |= PropertyFlag.EnumerableSet;
- if (value)
- {
- _flags |= PropertyFlag.Enumerable;
- }
- else
- {
- _flags &= ~(PropertyFlag.Enumerable);
- }
- }
- }
- public bool EnumerableSet
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => (_flags & (PropertyFlag.EnumerableSet | PropertyFlag.Enumerable)) != PropertyFlag.None;
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private set
- {
- if (value)
- {
- _flags |= PropertyFlag.EnumerableSet;
- }
- else
- {
- _flags &= ~(PropertyFlag.EnumerableSet);
- }
- }
- }
- public bool Writable
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => (_flags & PropertyFlag.Writable) != PropertyFlag.None;
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- set
- {
- _flags |= PropertyFlag.WritableSet;
- if (value)
- {
- _flags |= PropertyFlag.Writable;
- }
- else
- {
- _flags &= ~(PropertyFlag.Writable);
- }
- }
- }
- public bool WritableSet
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => (_flags & (PropertyFlag.WritableSet | PropertyFlag.Writable)) != PropertyFlag.None;
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private set
- {
- if (value)
- {
- _flags |= PropertyFlag.WritableSet;
- }
- else
- {
- _flags &= ~(PropertyFlag.WritableSet);
- }
- }
- }
- public bool Configurable
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => (_flags & PropertyFlag.Configurable) != PropertyFlag.None;
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- set
- {
- _flags |= PropertyFlag.ConfigurableSet;
- if (value)
- {
- _flags |= PropertyFlag.Configurable;
- }
- else
- {
- _flags &= ~(PropertyFlag.Configurable);
- }
- }
- }
- public bool ConfigurableSet
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => (_flags & (PropertyFlag.ConfigurableSet | PropertyFlag.Configurable)) != PropertyFlag.None;
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private set
- {
- if (value)
- {
- _flags |= PropertyFlag.ConfigurableSet;
- }
- else
- {
- _flags &= ~(PropertyFlag.ConfigurableSet);
- }
- }
- }
- public JsValue Value
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get
- {
- if ((_flags & PropertyFlag.CustomJsValue) != PropertyFlag.None)
- {
- return CustomValue!;
- }
- return _value!;
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- set
- {
- if ((_flags & PropertyFlag.CustomJsValue) != PropertyFlag.None)
- {
- CustomValue = value;
- }
- _value = value;
- }
- }
- protected internal virtual JsValue? CustomValue
- {
- get => null;
- set => Throw.NotImplementedException();
- }
- internal PropertyFlag Flags
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => _flags;
- }
- /// <summary>
- /// https://tc39.es/ecma262/#sec-topropertydescriptor
- /// </summary>
- public static PropertyDescriptor ToPropertyDescriptor(Realm realm, JsValue o)
- {
- if (o is not ObjectInstance obj)
- {
- Throw.TypeError(realm);
- return null;
- }
- bool? enumerable = null;
- var hasEnumerable = obj.HasProperty(CommonProperties.Enumerable);
- if (hasEnumerable)
- {
- enumerable = TypeConverter.ToBoolean(obj.Get(CommonProperties.Enumerable));
- }
- bool? configurable = null;
- var hasConfigurable = obj.HasProperty(CommonProperties.Configurable);
- if (hasConfigurable)
- {
- configurable = TypeConverter.ToBoolean(obj.Get(CommonProperties.Configurable));
- }
- JsValue? value = null;
- var hasValue = obj.HasProperty(CommonProperties.Value);
- if (hasValue)
- {
- value = obj.Get(CommonProperties.Value);
- }
- bool? writable = null;
- var hasWritable = obj.HasProperty(CommonProperties.Writable);
- if (hasWritable)
- {
- writable = TypeConverter.ToBoolean(obj.Get(CommonProperties.Writable));
- }
- JsValue? get = null;
- var hasGet = obj.HasProperty(CommonProperties.Get);
- if (hasGet)
- {
- get = obj.Get(CommonProperties.Get);
- }
- JsValue? set = null;
- var hasSet = obj.HasProperty(CommonProperties.Set);
- if (hasSet)
- {
- set = obj.Get(CommonProperties.Set);
- }
- if ((hasValue || hasWritable) && (hasGet || hasSet))
- {
- Throw.TypeError(realm, "Invalid property descriptor. Cannot both specify accessors and a value or writable attribute");
- }
- var desc = hasGet || hasSet
- ? new GetSetPropertyDescriptor(null, null, PropertyFlag.None)
- : new PropertyDescriptor(PropertyFlag.None);
- if (hasEnumerable)
- {
- desc.Enumerable = enumerable!.Value;
- desc.EnumerableSet = true;
- }
- if (hasConfigurable)
- {
- desc.Configurable = configurable!.Value;
- desc.ConfigurableSet = true;
- }
- if (hasValue)
- {
- desc.Value = value!;
- }
- if (hasWritable)
- {
- desc.Writable = TypeConverter.ToBoolean(writable!.Value);
- desc.WritableSet = true;
- }
- if (hasGet)
- {
- if (!get!.IsUndefined() && get!.TryCast<ICallable>() == null)
- {
- Throw.TypeError(realm);
- }
- ((GetSetPropertyDescriptor) desc).SetGet(get!);
- }
- if (hasSet)
- {
- if (!set!.IsUndefined() && set!.TryCast<ICallable>() is null)
- {
- Throw.TypeError(realm);
- }
- ((GetSetPropertyDescriptor) desc).SetSet(set!);
- }
- if ((hasSet || hasGet) && (hasValue || hasWritable))
- {
- Throw.TypeError(realm);
- }
- return desc;
- }
- /// <summary>
- /// https://tc39.es/ecma262/#sec-frompropertydescriptor
- /// </summary>
- public static JsValue FromPropertyDescriptor(Engine engine, PropertyDescriptor desc, bool strictUndefined = false)
- {
- if (ReferenceEquals(desc, Undefined))
- {
- return JsValue.Undefined;
- }
- var obj = engine.Realm.Intrinsics.Object.Construct(Arguments.Empty);
- var properties = new PropertyDictionary(4, checkExistingKeys: false);
- // TODO should not check for strictUndefined, but needs a bigger cleanup
- // we should have possibility to leave out the properties in property descriptors as newer tests
- // also assert properties to be undefined
- if (desc.IsDataDescriptor())
- {
- properties["value"] = new PropertyDescriptor(desc.Value ?? JsValue.Undefined, PropertyFlag.ConfigurableEnumerableWritable);
- if (desc._flags != PropertyFlag.None || desc.WritableSet)
- {
- properties["writable"] = new PropertyDescriptor(desc.Writable, PropertyFlag.ConfigurableEnumerableWritable);
- }
- }
- else
- {
- properties["get"] = new PropertyDescriptor(desc.Get ?? JsValue.Undefined, PropertyFlag.ConfigurableEnumerableWritable);
- properties["set"] = new PropertyDescriptor(desc.Set ?? JsValue.Undefined, PropertyFlag.ConfigurableEnumerableWritable);
- }
- if (!strictUndefined || desc.EnumerableSet)
- {
- properties["enumerable"] = new PropertyDescriptor(desc.Enumerable, PropertyFlag.ConfigurableEnumerableWritable);
- }
- if (!strictUndefined || desc.ConfigurableSet)
- {
- properties["configurable"] = new PropertyDescriptor(desc.Configurable, PropertyFlag.ConfigurableEnumerableWritable);
- }
- obj.SetProperties(properties);
- return obj;
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public bool IsAccessorDescriptor()
- {
- return Get is not null || Set is not null;
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public bool IsDataDescriptor()
- {
- if ((_flags & PropertyFlag.NonData) != PropertyFlag.None)
- {
- return false;
- }
- return (_flags & (PropertyFlag.WritableSet | PropertyFlag.Writable)) != PropertyFlag.None
- || (_flags & PropertyFlag.CustomJsValue) != PropertyFlag.None && CustomValue is not null
- || _value is not null;
- }
- /// <summary>
- /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.10.3
- /// </summary>
- /// <returns></returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public bool IsGenericDescriptor()
- {
- return !IsDataDescriptor() && !IsAccessorDescriptor();
- }
- private sealed class UndefinedPropertyDescriptor : PropertyDescriptor
- {
- public UndefinedPropertyDescriptor() : base(PropertyFlag.None | PropertyFlag.CustomJsValue)
- {
- }
- protected internal override JsValue? CustomValue
- {
- set => Throw.InvalidOperationException("making changes to undefined property's descriptor is not allowed");
- }
- }
- internal sealed class AllForbiddenDescriptor : PropertyDescriptor
- {
- private static readonly PropertyDescriptor[] _cache;
- public static readonly AllForbiddenDescriptor NumberZero = new AllForbiddenDescriptor(JsNumber.Create(0));
- public static readonly AllForbiddenDescriptor NumberOne = new AllForbiddenDescriptor(JsNumber.Create(1));
- public static readonly AllForbiddenDescriptor BooleanFalse = new AllForbiddenDescriptor(JsBoolean.False);
- public static readonly AllForbiddenDescriptor BooleanTrue = new AllForbiddenDescriptor(JsBoolean.True);
- static AllForbiddenDescriptor()
- {
- _cache = new PropertyDescriptor[10];
- for (int i = 0; i < _cache.Length; ++i)
- {
- _cache[i] = new AllForbiddenDescriptor(JsNumber.Create(i));
- }
- }
- private AllForbiddenDescriptor(JsValue value)
- : base(PropertyFlag.AllForbidden)
- {
- _value = value;
- }
- public static PropertyDescriptor ForNumber(int number)
- {
- var temp = _cache;
- return (uint) number < temp.Length
- ? temp[number]
- : new PropertyDescriptor(number, PropertyFlag.AllForbidden);
- }
- }
- }
|