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(EnvironmentRecordDebugView))] public abstract class EnvironmentRecord : JsValue { protected internal readonly Engine _engine; protected internal EnvironmentRecord? _outerEnv; protected EnvironmentRecord(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. public abstract bool HasBinding(string name); internal abstract bool HasBinding(BindingName name); internal abstract bool TryGetBinding( BindingName name, bool strict, out Binding binding, [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. public abstract void CreateMutableBinding(string 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. public abstract void CreateImmutableBinding(string 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. public abstract void InitializeBinding(string name, JsValue value); /// /// 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. public abstract void SetMutableBinding(string 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. public abstract JsValue GetBindingValue(string 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. public abstract bool DeleteBinding(string name); public abstract bool HasThisBinding(); public abstract bool HasSuperBinding(); public abstract JsValue WithBaseObject(); public 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() { ExceptionHelper.ThrowNotSupportedException(); return null; } public override bool Equals(JsValue? other) { ExceptionHelper.ThrowNotSupportedException(); return false; } public abstract JsValue GetThisBinding(); public JsValue? NewTarget { get; protected set; } /// /// 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 EnvironmentRecordDebugView { private readonly EnvironmentRecord _record; public EnvironmentRecordDebugView(EnvironmentRecord 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; } } } } }