123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 |
- using System;
- using System.Collections.Generic;
- using Esprima.Ast;
- using Jint.Collections;
- using Jint.Native;
- using Jint.Native.Argument;
- using Jint.Native.Function;
- namespace Jint.Runtime.Environments
- {
- /// <summary>
- /// Represents a declarative environment record
- /// http://www.ecma-international.org/ecma-262/5.1/#sec-10.2.1.1
- /// </summary>
- public sealed class DeclarativeEnvironmentRecord : EnvironmentRecord
- {
- private StringDictionarySlim<Binding> _dictionary;
- private bool _set;
- private string _key;
- private Binding _value;
- private const string BindingNameArguments = "arguments";
- private Binding _argumentsBinding;
- public DeclarativeEnvironmentRecord(Engine engine) : base(engine)
- {
- }
- private void SetItem(string key, in Binding value)
- {
- if (_set && _key != key)
- {
- if (_dictionary == null)
- {
- _dictionary = new StringDictionarySlim<Binding>();
- }
- _dictionary[_key] = _value;
- }
- _set = true;
- _key = key;
- _value = value;
- if (_dictionary != null)
- {
- _dictionary[key] = value;
- }
- }
- private ref Binding GetExistingItem(string key)
- {
- if (_set && _key == key)
- {
- return ref _value;
- }
- if (key.Length == 9 && key == BindingNameArguments)
- {
- return ref _argumentsBinding;
- }
- return ref _dictionary[key];
- }
- private bool ContainsKey(string key)
- {
- if (key.Length == 9 && key == BindingNameArguments)
- {
- return !ReferenceEquals(_argumentsBinding.Value, null);
- }
- if (_set && key == _key)
- {
- return true;
- }
- return _dictionary?.ContainsKey(key) == true;
- }
- private void Remove(string key)
- {
- if (_set && key == _key)
- {
- _set = false;
- _key = null;
- _value = default;
- }
-
- if (key == BindingNameArguments)
- {
- _argumentsBinding.Value = null;
- }
- else
- {
- _dictionary?.Remove(key);
- }
- }
- private bool TryGetValue(string key, out Binding value)
- {
- value = default;
- if (_set && _key == key)
- {
- value = _value;
- return true;
- }
- return _dictionary != null && _dictionary.TryGetValue(key, out value);
- }
- public override bool HasBinding(string name)
- {
- return ContainsKey(name);
- }
- public override void CreateMutableBinding(string name, JsValue value, bool canBeDeleted = false)
- {
- SetItem(name, new Binding(value, canBeDeleted, mutable: true));
- }
- public override void SetMutableBinding(string name, JsValue value, bool strict)
- {
- ref var binding = ref GetExistingItem(name);
- if (binding.Mutable)
- {
- binding.Value = value;
- }
- else
- {
- if (strict)
- {
- ExceptionHelper.ThrowTypeError(_engine, "Can't update the value of an immutable binding.");
- }
- }
- }
- public override JsValue GetBindingValue(string name, bool strict)
- {
- ref var binding = ref GetExistingItem(name);
- if (!binding.Mutable && binding.Value._type == Types.Undefined)
- {
- if (strict)
- {
- ExceptionHelper.ThrowReferenceError(_engine, "Can't access an uninitialized immutable binding.");
- }
- return Undefined;
- }
- return binding.Value;
- }
- public override bool DeleteBinding(string name)
- {
- ref Binding binding = ref GetExistingItem(name);
- if (ReferenceEquals(binding.Value, null))
- {
- return true;
- }
- if (!binding.CanBeDeleted)
- {
- return false;
- }
- Remove(name);
- return true;
- }
- public override JsValue ImplicitThisValue()
- {
- return Undefined;
- }
- /// <inheritdoc />
- public override string[] GetAllBindingNames()
- {
- int size = _set ? 1 : 0;
- if (!ReferenceEquals(_argumentsBinding.Value, null))
- {
- size += 1;
- }
- if (_dictionary != null)
- {
- size += _dictionary.Count;
- }
- var keys = size > 0 ? new string[size] : ArrayExt.Empty<string>();
- int n = 0;
- if (_set)
- {
- keys[n++] = _key;
- }
- if (!ReferenceEquals(_argumentsBinding.Value, null))
- {
- keys[n++] = BindingNameArguments;
- }
- _dictionary?.Keys.CopyTo(keys, n);
- return keys;
- }
- internal void ReleaseArguments()
- {
- _engine._argumentsInstancePool.Return(_argumentsBinding.Value as ArgumentsInstance);
- _argumentsBinding = default;
- }
- /// <summary>
- /// Optimized version for function calls.
- /// </summary>
- internal void AddFunctionParameters(
- FunctionInstance functionInstance,
- JsValue[] arguments,
- ArgumentsInstance argumentsInstance)
- {
- var parameters = functionInstance._formalParameters;
- bool empty = _dictionary == null && !_set;
- if (empty && parameters.Length == 1 && parameters[0].Length != BindingNameArguments.Length)
- {
- var jsValue = arguments.Length == 0 ? Undefined : arguments[0];
- var binding = new Binding(jsValue, false, true);
- _set = true;
- _key = parameters[0];
- _value = binding;
- }
- else
- {
- AddMultipleParameters(arguments, parameters);
- }
- if (ReferenceEquals(_argumentsBinding.Value, null))
- {
- _argumentsBinding = new Binding(argumentsInstance, canBeDeleted: false, mutable: true);
- }
- }
- private void AddMultipleParameters(JsValue[] arguments, string[] parameters)
- {
- bool empty = _dictionary == null && !_set;
- for (var i = 0; i < parameters.Length; i++)
- {
- var argName = parameters[i];
- var jsValue = i + 1 > arguments.Length ? Undefined : arguments[i];
- if (empty || !TryGetValue(argName, out var existing))
- {
- var binding = new Binding(jsValue, false, true);
- if (argName.Length == 9 && argName == BindingNameArguments)
- {
- _argumentsBinding = binding;
- }
- else
- {
- SetItem(argName, binding);
- }
- }
- else
- {
- if (existing.Mutable)
- {
- ref var b = ref GetExistingItem(argName);
- b.Value = jsValue;
- }
- else
- {
- ExceptionHelper.ThrowTypeError(_engine, "Can't update the value of an immutable binding.");
- }
- }
- }
- }
- internal void AddVariableDeclarations(List<VariableDeclaration> variableDeclarations)
- {
- var variableDeclarationsCount = variableDeclarations.Count;
- for (var i = 0; i < variableDeclarationsCount; i++)
- {
- var variableDeclaration = variableDeclarations[i];
- var declarationsCount = variableDeclaration.Declarations.Count;
- for (var j = 0; j < declarationsCount; j++)
- {
- var d = variableDeclaration.Declarations[j];
- var dn = ((Identifier) d.Id).Name;
- if (!ContainsKey(dn))
- {
- var binding = new Binding(Undefined, canBeDeleted: false, mutable: true);
- SetItem(dn, binding);
- }
- }
- }
- }
- }
- }
|