123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401 |
- using System.Diagnostics.CodeAnalysis;
- using System.Runtime.CompilerServices;
- using Jint.Native;
- using Jint.Native.Global;
- using Jint.Native.Object;
- using Jint.Runtime.Descriptors;
- namespace Jint.Runtime.Environments;
- /// <summary>
- /// https://tc39.es/ecma262/#sec-global-environment-records
- /// </summary>
- internal sealed class GlobalEnvironment : Environment
- {
- /// <summary>
- /// A sealed class for global usage.
- /// </summary>
- internal sealed class GlobalDeclarativeEnvironment : DeclarativeEnvironment
- {
- public GlobalDeclarativeEnvironment(Engine engine) : base(engine)
- {
- }
- }
- internal readonly ObjectInstance _global;
- // we expect it to be GlobalObject, but need to allow to something host-defined, like Window
- private readonly GlobalObject? _globalObject;
- // Environment records are needed by debugger
- internal readonly GlobalDeclarativeEnvironment _declarativeRecord;
- public GlobalEnvironment(Engine engine, ObjectInstance global) : base(engine)
- {
- _global = global;
- _globalObject = global as GlobalObject;
- _declarativeRecord = new GlobalDeclarativeEnvironment(engine);
- }
- public ObjectInstance GlobalThisValue => _global;
- internal override bool HasBinding(Key name)
- {
- if (_declarativeRecord.HasBinding(name))
- {
- return true;
- }
- if (_globalObject is not null)
- {
- return _globalObject.HasProperty(name);
- }
- return _global.HasProperty(new JsString(name));
- }
- internal override bool HasBinding(BindingName name)
- {
- if (_declarativeRecord.HasBinding(name))
- {
- return true;
- }
- if (_globalObject is not null)
- {
- return _globalObject.HasProperty(name.Key);
- }
- return _global.HasProperty(name.Value);
- }
- internal override bool TryGetBinding(BindingName name, bool strict, [NotNullWhen(true)] out JsValue? value)
- {
- if (_declarativeRecord._dictionary is not null && _declarativeRecord.TryGetBinding(name, strict, out value))
- {
- return true;
- }
- // we unwrap by name
- value = default;
- // normal case is to find
- if (_global._properties!._dictionary.TryGetValue(name.Key, out var property)
- && property != PropertyDescriptor.Undefined)
- {
- value = ObjectInstance.UnwrapJsValue(property, _global);
- return true;
- }
- if (_global._prototype is not null)
- {
- return TryGetBindingForGlobalParent(name, out value);
- }
- return false;
- }
- [MethodImpl(MethodImplOptions.NoInlining)]
- private bool TryGetBindingForGlobalParent(
- BindingName name,
- [NotNullWhen(true)] out JsValue? value)
- {
- value = default;
- var parent = _global._prototype!;
- var property = parent.GetOwnProperty(name.Value);
- if (property == PropertyDescriptor.Undefined)
- {
- return false;
- }
- value = ObjectInstance.UnwrapJsValue(property, _global);
- return true;
- }
- /// <summary>
- /// https://tc39.es/ecma262/#sec-global-environment-records-createmutablebinding-n-d
- /// </summary>
- internal override void CreateMutableBinding(Key name, bool canBeDeleted = false)
- {
- if (_declarativeRecord.HasBinding(name))
- {
- ThrowAlreadyDeclaredException(name);
- }
- _declarativeRecord.CreateMutableBinding(name, canBeDeleted);
- }
- /// <summary>
- /// https://tc39.es/ecma262/#sec-global-environment-records-createimmutablebinding-n-s
- /// </summary>
- internal override void CreateImmutableBinding(Key name, bool strict = true)
- {
- if (_declarativeRecord.HasBinding(name))
- {
- ThrowAlreadyDeclaredException(name);
- }
- _declarativeRecord.CreateImmutableBinding(name, strict);
- }
- [MethodImpl(MethodImplOptions.NoInlining)]
- private void ThrowAlreadyDeclaredException(Key name)
- {
- Throw.TypeError(_engine.Realm, $"{name} has already been declared");
- }
- internal override void InitializeBinding(Key name, JsValue value, DisposeHint hint)
- {
- if (_declarativeRecord.HasBinding(name))
- {
- _declarativeRecord.InitializeBinding(name, value, hint);
- }
- else
- {
- _global._properties![name].Value = value;
- }
- }
- internal override void SetMutableBinding(Key name, JsValue value, bool strict)
- {
- if (_declarativeRecord.HasBinding(name))
- {
- _declarativeRecord.SetMutableBinding(name, value, strict);
- }
- else
- {
- if (_globalObject is not null)
- {
- // fast inlined path as we know we target global
- if (!_globalObject.SetFromMutableBinding(name, value, strict) && strict)
- {
- Throw.TypeError(_engine.Realm);
- }
- }
- else
- {
- SetMutableBindingUnlikely(name, value, strict);
- }
- }
- }
- internal override void SetMutableBinding(BindingName name, JsValue value, bool strict)
- {
- if (_declarativeRecord.HasBinding(name))
- {
- _declarativeRecord.SetMutableBinding(name, value, strict);
- }
- else
- {
- if (_globalObject is not null)
- {
- // fast inlined path as we know we target global
- if (!_globalObject.SetFromMutableBinding(name.Key, value, strict) && strict)
- {
- Throw.TypeError(_engine.Realm);
- }
- }
- else
- {
- SetMutableBindingUnlikely(name.Key, value, strict);
- }
- }
- }
- private void SetMutableBindingUnlikely(Key name, JsValue value, bool strict)
- {
- // see ObjectEnvironmentRecord.SetMutableBinding
- var jsString = new JsString(name.Name);
- if (strict && !_global.HasProperty(jsString))
- {
- Throw.ReferenceNameError(_engine.Realm, name);
- }
- _global.Set(jsString, value);
- }
- internal override JsValue GetBindingValue(Key name, bool strict)
- {
- if (_declarativeRecord.HasBinding(name))
- {
- return _declarativeRecord.GetBindingValue(name, strict);
- }
- // see ObjectEnvironmentRecord.GetBindingValue
- var desc = PropertyDescriptor.Undefined;
- if (_globalObject is not null)
- {
- if (_globalObject._properties?.TryGetValue(name, out desc) == false)
- {
- desc = PropertyDescriptor.Undefined;
- }
- }
- else
- {
- desc = _global.GetOwnProperty(name.Name);
- }
- if (strict && desc == PropertyDescriptor.Undefined)
- {
- Throw.ReferenceNameError(_engine.Realm, name);
- }
- return ObjectInstance.UnwrapJsValue(desc, _global);
- }
- internal override bool DeleteBinding(Key name)
- {
- if (_declarativeRecord.HasBinding(name))
- {
- return _declarativeRecord.DeleteBinding(name);
- }
- var n = JsString.Create(name.Name);
- if (_global.HasOwnProperty(n))
- {
- return _global.Delete(n);
- }
- return true;
- }
- internal override bool HasThisBinding() => true;
- internal override bool HasSuperBinding() => false;
- internal override JsValue WithBaseObject() => Undefined;
- internal override JsValue GetThisBinding() => _global;
- internal bool HasLexicalDeclaration(Key name) => _declarativeRecord.HasBinding(name);
- internal bool HasRestrictedGlobalProperty(Key name)
- {
- if (_globalObject is not null)
- {
- return _globalObject._properties?.TryGetValue(name, out var desc) == true
- && !desc.Configurable;
- }
- var existingProp = _global.GetOwnProperty(name.Name);
- if (existingProp == PropertyDescriptor.Undefined)
- {
- return false;
- }
- return !existingProp.Configurable;
- }
- public bool CanDeclareGlobalVar(Key name)
- {
- if (_global._properties!.ContainsKey(name))
- {
- return true;
- }
- return _global.Extensible;
- }
- public bool CanDeclareGlobalFunction(Key name)
- {
- if (!_global._properties!.TryGetValue(name, out var existingProp)
- || existingProp == PropertyDescriptor.Undefined)
- {
- return _global.Extensible;
- }
- if (existingProp.Configurable)
- {
- return true;
- }
- if (existingProp.IsDataDescriptor() && existingProp.Writable && existingProp.Enumerable)
- {
- return true;
- }
- return false;
- }
- public void CreateGlobalVarBinding(Key name, bool canBeDeleted)
- {
- if (!_global.Extensible)
- {
- return;
- }
- _global._properties!.TryAdd(name, new PropertyDescriptor(Undefined, canBeDeleted
- ? PropertyFlag.ConfigurableEnumerableWritable | PropertyFlag.MutableBinding
- : PropertyFlag.NonConfigurable | PropertyFlag.MutableBinding));
- }
- internal void CreateGlobalVarBindings(List<Key> names, bool canBeDeleted)
- {
- if (!_global.Extensible)
- {
- return;
- }
- for (var i = 0; i < names.Count; i++)
- {
- var name = names[i];
- _global._properties!.TryAdd(name, new PropertyDescriptor(Undefined, canBeDeleted
- ? PropertyFlag.ConfigurableEnumerableWritable | PropertyFlag.MutableBinding
- : PropertyFlag.NonConfigurable | PropertyFlag.MutableBinding));
- }
- }
- /// <summary>
- /// https://tc39.es/ecma262/#sec-createglobalfunctionbinding
- /// </summary>
- public void CreateGlobalFunctionBinding(Key name, JsValue value, bool canBeDeleted)
- {
- var jsString = new JsString(name);
- var existingProp = _global.GetOwnProperty(jsString);
- PropertyDescriptor desc;
- if (existingProp == PropertyDescriptor.Undefined || existingProp.Configurable)
- {
- desc = new PropertyDescriptor(value, true, true, canBeDeleted);
- }
- else
- {
- desc = new PropertyDescriptor(value, PropertyFlag.None);
- }
- _global.DefinePropertyOrThrow(jsString, desc);
- _global.Set(jsString, value, false);
- }
- internal override bool HasBindings()
- {
- return _declarativeRecord.HasBindings() || _globalObject?._properties?.Count > 0 || _global._properties?.Count > 0;
- }
- internal override string[] GetAllBindingNames()
- {
- // JT: Rather than introduce a new method for the debugger, I'm reusing this one,
- // which - in spite of the very general name - is actually only used by the debugger
- // at this point.
- var names = new List<string>(_global._properties?.Count ?? 0 + _declarativeRecord._dictionary?.Count ?? 0);
- foreach (var name in _global.GetOwnProperties())
- {
- names.Add(name.Key.ToString());
- }
- foreach (var name in _declarativeRecord.GetAllBindingNames())
- {
- names.Add(name);
- }
- return names.ToArray();
- }
- public override bool Equals(JsValue? other)
- {
- return ReferenceEquals(this, other);
- }
- }
|