using System; using System.Collections.Generic; using Jint.Native.Function; using Jint.Native.Object; using Jint.Runtime; using Jint.Runtime.Descriptors; using Jint.Runtime.Descriptors.Specialized; using Jint.Runtime.Environments; namespace Jint.Native.Argument { /// /// http://www.ecma-international.org/ecma-262/5.1/#sec-10.6 /// public class ArgumentsInstance : ObjectInstance { private ArgumentsInstance(Engine engine, Action initializer) : base(engine) { _initializer = initializer; _initialized = false; } public bool Strict { get; set; } private Action _initializer; private bool _initialized; protected override void EnsureInitialized() { if(_initialized) { return; } _initialized = true; _initializer(this); } public static ArgumentsInstance CreateArgumentsObject(Engine engine, FunctionInstance func, string[] names, JsValue[] args, EnvironmentRecord env, bool strict) { var obj = new ArgumentsInstance(engine, self => { var len = args.Length; self.FastAddProperty("length", len, true, false, true); var map = engine.Object.Construct(Arguments.Empty); var mappedNamed = new List(); var indx = 0; while (indx <= len - 1) { var indxStr = TypeConverter.ToString(indx); var val = args[indx]; self.FastAddProperty(indxStr, val, true, true, true); if (indx < names.Length) { var name = names[indx]; if (!strict && !mappedNamed.Contains(name)) { mappedNamed.Add(name); Func g = n => env.GetBindingValue(name, false); var p = new Action((n, o) => env.SetMutableBinding(name, o, true)); map.DefineOwnProperty(indxStr, new ClrAccessDescriptor(engine, g, p) { Configurable = true }, false); } } indx++; } // step 12 if (mappedNamed.Count > 0) { self.ParameterMap = map; } // step 13 if (!strict) { self.FastAddProperty("callee", func, true, false, true); } // step 14 else { var thrower = engine.Function.ThrowTypeError; self.DefineOwnProperty("caller", new PropertyDescriptor(get: thrower, set: thrower, enumerable: false, configurable: false), false); self.DefineOwnProperty("callee", new PropertyDescriptor(get: thrower, set: thrower, enumerable: false, configurable: false), false); } }); // These properties are pre-initialized as their don't trigger // the EnsureInitialized() event and are cheap obj.Prototype = engine.Object.PrototypeObject; obj.Extensible = true; obj.Strict = strict; return obj; } public ObjectInstance ParameterMap { get; set; } public override string Class { get { return "Arguments"; } } public override PropertyDescriptor GetOwnProperty(string propertyName) { EnsureInitialized(); if (!Strict && ParameterMap != null) { var desc = base.GetOwnProperty(propertyName); if (desc == PropertyDescriptor.Undefined) { return desc; } var isMapped = ParameterMap.GetOwnProperty(propertyName); if (isMapped != PropertyDescriptor.Undefined) { desc.Value = ParameterMap.Get(propertyName); } return desc; } return base.GetOwnProperty(propertyName); } /// Implementation from ObjectInstance official specs as the one /// in ObjectInstance is optimized for the general case and wouldn't work /// for arrays public override void Put(string propertyName, JsValue value, bool throwOnError) { EnsureInitialized(); if (!CanPut(propertyName)) { if (throwOnError) { throw new JavaScriptException(Engine.TypeError); } return; } var ownDesc = GetOwnProperty(propertyName); if (ownDesc.IsDataDescriptor()) { var valueDesc = new PropertyDescriptor(value: value, writable: null, enumerable: null, configurable: null); DefineOwnProperty(propertyName, valueDesc, throwOnError); return; } // property is an accessor or inherited var desc = GetProperty(propertyName); if (desc.IsAccessorDescriptor()) { var setter = desc.Set.TryCast(); setter.Call(new JsValue(this), new[] { value }); } else { var newDesc = new PropertyDescriptor(value, true, true, true); DefineOwnProperty(propertyName, newDesc, throwOnError); } } public override bool DefineOwnProperty(string propertyName, PropertyDescriptor desc, bool throwOnError) { EnsureInitialized(); if (!Strict && ParameterMap != null) { var map = ParameterMap; var isMapped = map.GetOwnProperty(propertyName); var allowed = base.DefineOwnProperty(propertyName, desc, false); if (!allowed) { if (throwOnError) { throw new JavaScriptException(Engine.TypeError); } } if (isMapped != PropertyDescriptor.Undefined) { if (desc.IsAccessorDescriptor()) { map.Delete(propertyName, false); } else { if (desc.Value != null && desc.Value != Undefined.Instance) { map.Put(propertyName, desc.Value, throwOnError); } if (desc.Writable.HasValue && desc.Writable.Value == false) { map.Delete(propertyName, false); } } } return true; } return base.DefineOwnProperty(propertyName, desc, throwOnError); } public override bool Delete(string propertyName, bool throwOnError) { EnsureInitialized(); if (!Strict && ParameterMap != null) { var map = ParameterMap; var isMapped = map.GetOwnProperty(propertyName); var result = base.Delete(propertyName, throwOnError); if (result && isMapped != PropertyDescriptor.Undefined) { map.Delete(propertyName, false); } return result; } return base.Delete(propertyName, throwOnError); } } }