using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using Jint.Native; namespace Jint.Runtime.Environments; /// /// Base implementation of an Environment Record /// https://tc39.es/ecma262/#sec-environment-records /// [DebuggerTypeProxy(typeof(EnvironmentDebugView))] internal abstract class Environment : JsValue { protected internal readonly Engine _engine; protected internal Environment? _outerEnv; protected Environment(Engine engine) : base(InternalTypes.ObjectEnvironmentRecord) { _engine = engine; } /// /// Determines if an environment record has a binding for an identifier. /// /// The identifier of the binding /// true if it does and false if it does not. internal abstract bool HasBinding(Key name); internal abstract bool HasBinding(BindingName name); internal abstract bool TryGetBinding(BindingName name, bool strict, [NotNullWhen(true)] out JsValue? value); /// /// Creates a new mutable binding in an environment record. /// /// The identifier of the binding. /// true if the binding may be subsequently deleted. internal abstract void CreateMutableBinding(Key name, bool canBeDeleted = false); /// /// Creates a new but uninitialized immutable binding in an environment record. /// /// The identifier of the binding. /// false if the binding may used before it's been initialized. internal abstract void CreateImmutableBinding(Key name, bool strict = true); /// /// Set the value of an already existing but uninitialized binding in an Environment Record. /// /// The text of the bound name /// The value for the binding. /// Disposal type hint internal abstract void InitializeBinding(Key name, JsValue value, DisposeHint hint); /// /// Sets the value of an already existing mutable binding in an environment record. /// /// The identifier of the binding /// The value of the binding. /// The identify strict mode references. internal abstract void SetMutableBinding(Key name, JsValue value, bool strict); internal abstract void SetMutableBinding(BindingName name, JsValue value, bool strict); /// /// Returns the value of an already existing binding from an environment record. /// /// The identifier of the binding /// The identify strict mode references. /// The value of an already existing binding from an environment record. internal abstract JsValue GetBindingValue(Key name, bool strict); /// /// Delete a binding from an environment record. The String value N is the text of the bound name If a binding for N exists, remove the binding and return true. If the binding exists but cannot be removed return false. If the binding does not exist return true. /// /// The identifier of the binding /// true if the deletion is successfull. internal abstract bool DeleteBinding(Key name); internal abstract bool HasThisBinding(); internal abstract bool HasSuperBinding(); internal abstract JsValue WithBaseObject(); internal abstract bool HasBindings(); /// /// Returns an array of all the defined binding names /// /// The array of all defined bindings internal abstract string[] GetAllBindingNames(); public override object ToObject() { Throw.NotSupportedException(); return null; } public override bool Equals(JsValue? other) { Throw.NotSupportedException(); return false; } internal abstract JsValue GetThisBinding(); internal JsValue? NewTarget { get; set; } internal virtual Completion DisposeResources(Completion c) => c; /// /// Helper to cache JsString/Key when environments use different lookups. /// [DebuggerDisplay("\"{Key.Name}\"")] internal sealed class BindingName { public readonly Key Key; public readonly JsString Value; public readonly JsValue? CalculatedValue; public BindingName(string value) { var key = (Key) value; Key = key; Value = JsString.Create(value); if (key == KnownKeys.Undefined) { CalculatedValue = Undefined; } } public BindingName(JsString value) { var key = (Key) value.ToString(); Key = key; Value = value; if (key == KnownKeys.Undefined) { CalculatedValue = Undefined; } } } private sealed class EnvironmentDebugView { private readonly Environment _record; public EnvironmentDebugView(Environment record) { _record = record; } [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] public KeyValuePair[] Entries { get { var bindingNames = _record.GetAllBindingNames(); var bindings = new KeyValuePair[bindingNames.Length]; var i = 0; foreach (var key in bindingNames) { bindings[i++] = new KeyValuePair(key, _record.GetBindingValue(key, false)); } return bindings; } } } }