/// 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) { if (!CanPut(propertyName)) { if (throwOnError) { ExceptionHelper.ThrowTypeError(Engine); } return; } var ownDesc = GetOwnProperty(propertyName); if (ownDesc.IsDataDescriptor()) { var valueDesc = new PropertyDescriptor(value, PropertyFlag.None); DefineOwnProperty(propertyName, valueDesc, throwOnError); return; } // property is an accessor or inherited var desc = GetProperty(propertyName); if (desc.IsAccessorDescriptor()) { var setter = desc.Set.TryCast <ICallable>(); setter.Call(this, new[] { value }); } else { var newDesc = new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable); DefineOwnProperty(propertyName, newDesc, throwOnError); } }
public ArrayInstance(Engine engine, Dictionary <uint, PropertyDescriptor> items) : base(engine, objectClass: "Array") { _sparse = items; var length = items?.Count ?? 0; _length = new PropertyDescriptor(length, PropertyFlag.OnlyWritable); }
internal void WriteArrayValue(uint index, PropertyDescriptor desc) { // calculate eagerly so we know if we outgrow var newSize = _dense != null && index >= (uint)_dense.Length ? System.Math.Max(index, System.Math.Max(_dense.Length, 2)) * 2 : 0; bool canUseDense = _dense != null && index < MaxDenseArrayLength && newSize < MaxDenseArrayLength && index < _dense.Length + 50; // looks sparse if (canUseDense) { var temp = _dense; if (index >= (uint)temp.Length) { EnsureCapacity((uint)newSize); } _dense[index] = desc; } else { if (_dense != null) { ConvertToSparse(); } _sparse[index] = desc; } }
/// 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) { 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.Value.TryCast<ICallable>(); setter.Call(new JsValue(this), new[] { value }); } else { var newDesc = new PropertyDescriptor(value, true, true, true); DefineOwnProperty(propertyName, newDesc, throwOnError); } }
internal ArrayInstance Map(JsValue[] arguments) { var callbackfn = arguments.At(0); var thisArg = arguments.At(1); var len = GetLength(); var callable = GetCallable(callbackfn); var a = Engine.Array.ConstructFast(len); var args = _engine._jsValueArrayPool.RentArray(3); args[2] = this; for (uint k = 0; k < len; k++) { var kvalue = args[0]; if (TryGetValue(k, out kvalue)) { args[0] = kvalue; args[1] = k; var mappedValue = callable.Call(thisArg, args); var desc = new PropertyDescriptor(mappedValue, PropertyFlag.ConfigurableEnumerableWritable); if (a._dense != null && k < (uint)a._dense.Length) { a._dense[k] = desc; } else { a.WriteArrayValue(k, desc); } } } _engine._jsValueArrayPool.ReturnArray(args); return(a); }
internal void EnsureCapacity(uint capacity) { if (capacity > (uint)_dense.Length) { // need to grow var newArray = new PropertyDescriptor[capacity]; System.Array.Copy(_dense, newArray, _dense.Length); _dense = newArray; } }
protected override void AddProperty(string propertyName, PropertyDescriptor descriptor) { if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength) { _length = descriptor; return; } base.AddProperty(propertyName, descriptor); }
protected override bool TryGetProperty(string propertyName, out PropertyDescriptor descriptor) { if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength) { descriptor = _length; return(_length != null); } return(base.TryGetProperty(propertyName, out descriptor)); }
private bool TryGetDescriptor(uint index, out PropertyDescriptor descriptor) { if (_dense != null) { descriptor = null; if (index < (uint)_dense.Length) { descriptor = _dense[index]; } return(descriptor != null); } return(_sparse.TryGetValue(index, out descriptor)); }
public override void RemoveOwnProperty(string p) { uint index; if (IsArrayIndex(p, out index)) { DeleteAt(index); } if (p == PropertyNameLength) { _length = null; } base.RemoveOwnProperty(p); }
public ArrayInstance(Engine engine, PropertyDescriptor[] items) : base(engine, objectClass: "Array") { int length = 0; if (items == null || items.Length == 0) { _dense = System.ArrayExt.Empty <PropertyDescriptor>(); length = 0; } else { _dense = items; length = items.Length; } _length = new PropertyDescriptor(length, PropertyFlag.OnlyWritable); }
internal uint Push(JsValue[] arguments) { var initialLength = GetLength(); var newLength = initialLength + arguments.Length; // if we see that we are bringing more than normal growth algorithm handles, ensure capacity eagerly if (_dense != null && initialLength != 0 && arguments.Length > initialLength * 2 && newLength <= MaxDenseArrayLength) { EnsureCapacity((uint)newLength); } double n = initialLength; for (var i = 0; i < arguments.Length; i++) { var desc = new PropertyDescriptor(arguments[i], PropertyFlag.ConfigurableEnumerableWritable); if (_dense != null && n < _dense.Length) { _dense[(int)n] = desc; } else if (n < uint.MaxValue) { WriteArrayValue((uint)n, desc); } else { DefineOwnProperty(TypeConverter.ToString((uint)n), desc, true); } n++; } // check if we can set length fast without breaking ECMA specification if (n < uint.MaxValue && CanPut(PropertyNameLength)) { _length.Value = (uint)n; } else { Put(PropertyNameLength, newLength, true); } return((uint)n); }
protected internal override void SetOwnProperty(string propertyName, PropertyDescriptor desc) { uint index; if (IsArrayIndex(propertyName, out index)) { WriteArrayValue(index, desc); } else if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength) { _length = desc; } else { base.SetOwnProperty(propertyName, desc); } }
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.HasValue && desc.Value.Value != Undefined.Instance) { map.Put(propertyName, desc.Value.Value, throwOnError); } if (desc.Writable.HasValue && desc.Writable.Value == false) { map.Delete(propertyName, false); } } } return true; } return base.DefineOwnProperty(propertyName, desc, throwOnError); }
protected virtual void SetOwnProperty(string propertyName, PropertyDescriptor desc) { Properties[propertyName] = desc; }
/// <summary> /// Creates or alters the named own property to /// have the state described by a Property /// Descriptor. The flag controls failure handling. /// </summary> /// <param name="propertyName"></param> /// <param name="desc"></param> /// <param name="throwOnError"></param> /// <returns></returns> public virtual bool DefineOwnProperty(string propertyName, PropertyDescriptor desc, bool throwOnError) { var current = GetOwnProperty(propertyName); if (current == PropertyDescriptor.Undefined) { if (!Extensible) { if (throwOnError) { throw new JavaScriptException(Engine.TypeError); } return false; } else { if (desc.IsGenericDescriptor() || desc.IsDataDescriptor()) { Properties[propertyName] = new PropertyDescriptor(desc) { Value = desc.Value.HasValue ? desc.Value : JsValue.Undefined, Writable = desc.Writable.HasValue ? desc.Writable : false }; } else { Properties[propertyName] = new PropertyDescriptor(desc) { Enumerable = desc.Enumerable.HasValue ? desc.Enumerable : false, Configurable = desc.Configurable.HasValue ? desc.Configurable : false, }; } } return true; } // Step 5 if (!current.Configurable.HasValue && !current.Enumerable.HasValue && !current.Writable.HasValue && !current.Get.HasValue && !current.Set.HasValue && !current.Value.HasValue) { return true; } // Step 6 var configurableIsSame = current.Configurable.HasValue ? desc.Configurable.HasValue && (current.Configurable.Value == desc.Configurable.Value) : !desc.Configurable.HasValue; var enumerableIsSame = current.Enumerable.HasValue ? desc.Enumerable.HasValue && (current.Enumerable.Value == desc.Enumerable.Value) : !desc.Enumerable.HasValue; var writableIsSame = true; var valueIsSame = true; if (current.IsDataDescriptor() && desc.IsDataDescriptor()) { var currentDataDescriptor = current; var descDataDescriptor = desc; writableIsSame = currentDataDescriptor.Writable.HasValue ? descDataDescriptor.Writable.HasValue && (currentDataDescriptor.Writable.Value == descDataDescriptor.Writable.Value) : !descDataDescriptor.Writable.HasValue; var valueA = currentDataDescriptor.Value.HasValue ? currentDataDescriptor.Value.Value : Undefined.Instance; var valueB = descDataDescriptor.Value.HasValue ? descDataDescriptor.Value.Value : Undefined.Instance; valueIsSame = ExpressionInterpreter.SameValue(valueA, valueB); } else if (current.IsAccessorDescriptor() && desc.IsAccessorDescriptor()) { var currentAccessorDescriptor = current; var descAccessorDescriptor = desc; var getValueA = currentAccessorDescriptor.Get.HasValue ? currentAccessorDescriptor.Get.Value : Undefined.Instance; var getValueB = descAccessorDescriptor.Get.HasValue ? descAccessorDescriptor.Get.Value : Undefined.Instance; var setValueA = currentAccessorDescriptor.Set.HasValue ? currentAccessorDescriptor.Set.Value : Undefined.Instance; var setValueB = descAccessorDescriptor.Set.HasValue ? descAccessorDescriptor.Set.Value : Undefined.Instance; valueIsSame = ExpressionInterpreter.SameValue(getValueA, getValueB) && ExpressionInterpreter.SameValue(setValueA, setValueB); } else { valueIsSame = false; } if (configurableIsSame && enumerableIsSame && writableIsSame && valueIsSame) { return true; } if (!current.Configurable.HasValue || !current.Configurable.Value.AsBoolean()) { if (desc.Configurable.HasValue && desc.Configurable.Value.AsBoolean()) { if (throwOnError) { throw new JavaScriptException(Engine.TypeError); } return false; } if (desc.Enumerable.HasValue && (!current.Enumerable.HasValue || desc.Enumerable.Value != current.Enumerable.Value)) { if (throwOnError) { throw new JavaScriptException(Engine.TypeError); } return false; } } if (!desc.IsGenericDescriptor()) { if (current.IsDataDescriptor() != desc.IsDataDescriptor()) { if (!current.Configurable.HasValue || !current.Configurable.Value.AsBoolean()) { if (throwOnError) { throw new JavaScriptException(Engine.TypeError); } return false; } if (current.IsDataDescriptor()) { Properties[propertyName] = current = new PropertyDescriptor( get: Undefined.Instance, set: Undefined.Instance, enumerable: current.Enumerable.HasValue && current.Enumerable.Value.AsBoolean(), configurable: current.Configurable.HasValue && current.Configurable.Value.AsBoolean() ); } else { Properties[propertyName] = current = new PropertyDescriptor( value: Undefined.Instance, writable: null, enumerable: current.Enumerable.HasValue && current.Enumerable.Value.AsBoolean(), configurable: current.Configurable.HasValue && current.Configurable.Value.AsBoolean() ); } } else if (current.IsDataDescriptor() && desc.IsDataDescriptor()) { if (!current.Configurable.HasValue || current.Configurable.Value.AsBoolean() == false) { if (!current.Writable.HasValue || !current.Writable.Value.AsBoolean() && desc.Writable.HasValue && desc.Writable.Value.AsBoolean()) { if (throwOnError) { throw new JavaScriptException(Engine.TypeError); } return false; } if (!current.Writable.Value.AsBoolean()) { if (desc.Value.HasValue && !valueIsSame) { if (throwOnError) { throw new JavaScriptException(Engine.TypeError); } return false; } } } } else if (current.IsAccessorDescriptor() && desc.IsAccessorDescriptor()) { if (!current.Configurable.HasValue || !current.Configurable.Value.AsBoolean()) { if ((desc.Set.HasValue && !ExpressionInterpreter.SameValue(desc.Set.Value, current.Set.HasValue ? current.Set.Value : Undefined.Instance)) || (desc.Get.HasValue && !ExpressionInterpreter.SameValue(desc.Get.Value, current.Get.HasValue ? current.Get.Value : Undefined.Instance))) { if (throwOnError) { throw new JavaScriptException(Engine.TypeError); } return false; } } } } if (desc.Value.HasValue) { current.Value = desc.Value; } if (desc.Writable.HasValue) { current.Writable = desc.Writable; } if (desc.Enumerable.HasValue) { current.Enumerable = desc.Enumerable; } if (desc.Configurable.HasValue) { current.Configurable = desc.Configurable; } if (desc.Get.HasValue) { current.Get = desc.Get; } if (desc.Set.HasValue) { current.Set = desc.Set; } return true; }
/// <summary> /// Optimized version of [[Put]] when the property is known to be already declared /// </summary> /// <param name="name"></param> /// <param name="value"></param> public void FastSetProperty(string name, PropertyDescriptor value) { Properties[name] = value; }
/// <summary> /// Creates or alters the named own property to /// have the state described by a Property /// Descriptor. The flag controls failure handling. /// </summary> /// <param name="propertyName"></param> /// <param name="desc"></param> /// <param name="throwOnError"></param> /// <returns></returns> public virtual bool DefineOwnProperty(string propertyName, PropertyDescriptor desc, bool throwOnError) { var current = GetOwnProperty(propertyName); if (current == desc) { return true; } if (current == PropertyDescriptor.Undefined) { if (!Extensible) { if (throwOnError) { throw new JavaScriptException(Engine.TypeError); } return false; } else { if (desc.IsGenericDescriptor() || desc.IsDataDescriptor()) { SetOwnProperty(propertyName, new PropertyDescriptor(desc) { Value = desc.Value.HasValue ? desc.Value : JsValue.Undefined, Writable = desc.Writable.HasValue ? desc.Writable.Value : false, Enumerable = desc.Enumerable.HasValue ? desc.Enumerable.Value : false, Configurable = desc.Configurable.HasValue ? desc.Configurable.Value : false }); } else { SetOwnProperty(propertyName, new PropertyDescriptor(desc) { Get = desc.Get, Set = desc.Set, Enumerable = desc.Enumerable.HasValue ? desc.Enumerable : false, Configurable = desc.Configurable.HasValue ? desc.Configurable : false, }); } } return true; } // Step 5 if (!current.Configurable.HasValue && !current.Enumerable.HasValue && !current.Writable.HasValue && !current.Get.HasValue && !current.Set.HasValue && !current.Value.HasValue) { return true; } // Step 6 if ( current.Configurable == desc.Configurable && current.Writable == desc.Writable && current.Enumerable == desc.Enumerable && ((!current.Get.HasValue && !desc.Get.HasValue) || (current.Get.HasValue && desc.Get.HasValue && ExpressionInterpreter.SameValue(current.Get.Value, desc.Get.Value))) && ((!current.Set.HasValue && !desc.Set.HasValue) || (current.Set.HasValue && desc.Set.HasValue && ExpressionInterpreter.SameValue(current.Set.Value, desc.Set.Value))) && ((!current.Value.HasValue && !desc.Value.HasValue) || (current.Value.HasValue && desc.Value.HasValue && ExpressionInterpreter.StrictlyEqual(current.Value.Value, desc.Value.Value))) ) { return true; } if (!current.Configurable.HasValue || !current.Configurable.Value) { if (desc.Configurable.HasValue && desc.Configurable.Value) { if (throwOnError) { throw new JavaScriptException(Engine.TypeError); } return false; } if (desc.Enumerable.HasValue && (!current.Enumerable.HasValue || desc.Enumerable.Value != current.Enumerable.Value)) { if (throwOnError) { throw new JavaScriptException(Engine.TypeError); } return false; } } if (!desc.IsGenericDescriptor()) { if (current.IsDataDescriptor() != desc.IsDataDescriptor()) { if (!current.Configurable.HasValue || !current.Configurable.Value) { if (throwOnError) { throw new JavaScriptException(Engine.TypeError); } return false; } if (current.IsDataDescriptor()) { SetOwnProperty(propertyName, current = new PropertyDescriptor( get: Undefined.Instance, set: Undefined.Instance, enumerable: current.Enumerable, configurable: current.Configurable )); } else { SetOwnProperty(propertyName, current = new PropertyDescriptor( value: Undefined.Instance, writable: null, enumerable: current.Enumerable, configurable: current.Configurable )); } } else if (current.IsDataDescriptor() && desc.IsDataDescriptor()) { if (!current.Configurable.HasValue || current.Configurable.Value == false) { if (!current.Writable.HasValue || !current.Writable.Value && desc.Writable.HasValue && desc.Writable.Value) { if (throwOnError) { throw new JavaScriptException(Engine.TypeError); } return false; } if (!current.Writable.Value) { if (desc.Value.HasValue && !ExpressionInterpreter.SameValue(desc.Value.Value, current.Value.Value)) { if (throwOnError) { throw new JavaScriptException(Engine.TypeError); } return false; } } } } else if (current.IsAccessorDescriptor() && desc.IsAccessorDescriptor()) { if (!current.Configurable.HasValue || !current.Configurable.Value) { if ((desc.Set.HasValue && !ExpressionInterpreter.SameValue(desc.Set.Value, current.Set.HasValue ? current.Set.Value : Undefined.Instance)) || (desc.Get.HasValue && !ExpressionInterpreter.SameValue(desc.Get.Value, current.Get.HasValue ? current.Get.Value : Undefined.Instance))) { if (throwOnError) { throw new JavaScriptException(Engine.TypeError); } return false; } } } } if (desc.Value.HasValue) { current.Value = desc.Value; } if (desc.Writable.HasValue) { current.Writable = desc.Writable; } if (desc.Enumerable.HasValue) { current.Enumerable = desc.Enumerable; } if (desc.Configurable.HasValue) { current.Configurable = desc.Configurable; } if (desc.Get.HasValue) { current.Get = desc.Get; } if (desc.Set.HasValue) { current.Set = desc.Set; } return true; }
/// <summary> /// Optimized version of [[Put]] when the property is known to be already declared /// </summary> /// <param name="name"></param> /// <param name="value"></param> public void FastSetProperty(string name, PropertyDescriptor value) { SetOwnProperty(name, value); }
protected virtual void SetOwnProperty(string propertyName, PropertyDescriptor desc) { EnsureInitialized(); Properties[propertyName] = desc; }
public override bool DefineOwnProperty(string propertyName, PropertyDescriptor desc, bool throwOnError) { var oldLenDesc = _length; var oldLen = (uint)TypeConverter.ToNumber(oldLenDesc.Value); uint index; if (propertyName.Length == 6 && propertyName == "length") { var value = desc.Value; if (ReferenceEquals(value, null)) { return(base.DefineOwnProperty("length", desc, throwOnError)); } var newLenDesc = new PropertyDescriptor(desc); uint newLen = TypeConverter.ToUint32(value); if (newLen != TypeConverter.ToNumber(value)) { ExceptionHelper.ThrowRangeError(_engine); } newLenDesc.Value = newLen; if (newLen >= oldLen) { return(base.DefineOwnProperty("length", newLenDesc, throwOnError)); } if (!oldLenDesc.Writable) { if (throwOnError) { ExceptionHelper.ThrowTypeError(_engine); } return(false); } bool newWritable; if (!newLenDesc.WritableSet || newLenDesc.Writable) { newWritable = true; } else { newWritable = false; newLenDesc.Writable = true; } var succeeded = base.DefineOwnProperty("length", newLenDesc, throwOnError); if (!succeeded) { return(false); } var count = _dense?.Length ?? _sparse.Count; if (count < oldLen - newLen) { if (_dense != null) { for (uint keyIndex = 0; keyIndex < _dense.Length; ++keyIndex) { if (_dense[keyIndex] == null) { continue; } // is it the index of the array if (keyIndex >= newLen && keyIndex < oldLen) { var deleteSucceeded = DeleteAt(keyIndex); if (!deleteSucceeded) { newLenDesc.Value = keyIndex + 1; if (!newWritable) { newLenDesc.Writable = false; } base.DefineOwnProperty("length", newLenDesc, false); if (throwOnError) { ExceptionHelper.ThrowTypeError(_engine); } return(false); } } } } else { // in the case of sparse arrays, treat each concrete element instead of // iterating over all indexes var keys = new List <uint>(_sparse.Keys); var keysCount = keys.Count; for (var i = 0; i < keysCount; i++) { var keyIndex = keys[i]; // is it the index of the array if (keyIndex >= newLen && keyIndex < oldLen) { var deleteSucceeded = Delete(TypeConverter.ToString(keyIndex), false); if (!deleteSucceeded) { newLenDesc.Value = JsNumber.Create(keyIndex + 1); if (!newWritable) { newLenDesc.Writable = false; } base.DefineOwnProperty("length", newLenDesc, false); if (throwOnError) { ExceptionHelper.ThrowTypeError(_engine); } return(false); } } } } } else { while (newLen < oldLen) { // algorithm as per the spec oldLen--; var deleteSucceeded = Delete(TypeConverter.ToString(oldLen), false); if (!deleteSucceeded) { newLenDesc.Value = oldLen + 1; if (!newWritable) { newLenDesc.Writable = false; } base.DefineOwnProperty("length", newLenDesc, false); if (throwOnError) { ExceptionHelper.ThrowTypeError(_engine); } return(false); } } } if (!newWritable) { DefineOwnProperty("length", new PropertyDescriptor(value: null, flags: PropertyFlag.WritableSet), false); } return(true); } else if (IsArrayIndex(propertyName, out index)) { if (index >= oldLen && !oldLenDesc.Writable) { if (throwOnError) { ExceptionHelper.ThrowTypeError(_engine); } return(false); } var succeeded = base.DefineOwnProperty(propertyName, desc, false); if (!succeeded) { if (throwOnError) { ExceptionHelper.ThrowTypeError(_engine); } return(false); } if (index >= oldLen) { oldLenDesc.Value = index + 1; base.DefineOwnProperty("length", oldLenDesc, false); } return(true); } return(base.DefineOwnProperty(propertyName, desc, throwOnError)); }
public static PropertyDescriptor ToPropertyDescriptor(Engine engine, JsValue o) { var obj = o.TryCast <ObjectInstance>(); if (obj == null) { throw new JavaScriptException(engine.TypeError); } if ((obj.HasProperty("value") || obj.HasProperty("writable")) && (obj.HasProperty("get") || obj.HasProperty("set"))) { throw new JavaScriptException(engine.TypeError); } var desc = new PropertyDescriptor(); if (obj.HasProperty("enumerable")) { desc.Enumerable = TypeConverter.ToBoolean(obj.Get("enumerable")); } if (obj.HasProperty("configurable")) { desc.Configurable = TypeConverter.ToBoolean(obj.Get("configurable")); } if (obj.HasProperty("value")) { var value = obj.Get("value"); desc.Value = value; } if (obj.HasProperty("writable")) { desc.Writable = TypeConverter.ToBoolean(obj.Get("writable")); } if (obj.HasProperty("get")) { var getter = obj.Get("get"); if (getter != JsValue.Undefined && getter.TryCast <ICallable>() == null) { throw new JavaScriptException(engine.TypeError); } desc.Get = getter; } if (obj.HasProperty("set")) { var setter = obj.Get("set"); if (setter != Native.Undefined.Instance && setter.TryCast <ICallable>() == null) { throw new JavaScriptException(engine.TypeError); } desc.Set = setter; } if (desc.Get.HasValue || desc.Get.HasValue) { if (desc.Value.HasValue || desc.Writable.HasValue) { throw new JavaScriptException(engine.TypeError); } } return(desc); }
protected override void SetOwnProperty(string propertyName, PropertyDescriptor desc) { uint index; if (IsArrayIndex(propertyName, out index)) { _array[index] = desc; } else { if(propertyName == "length") { _length = desc; } base.SetOwnProperty(propertyName, desc); } }
public override bool DefineOwnProperty(string propertyName, PropertyDescriptor desc, bool throwOnError) { var oldLenDesc = GetOwnProperty("length"); var oldLen = TypeConverter.ToNumber(oldLenDesc.Value.Value); if (propertyName == "length") { if (!desc.Value.HasValue) { return base.DefineOwnProperty("length", desc, throwOnError); } var newLenDesc = new PropertyDescriptor(desc); uint newLen = TypeConverter.ToUint32(desc.Value.Value); if (newLen != TypeConverter.ToNumber(desc.Value.Value)) { throw new JavaScriptException(_engine.RangeError); } newLenDesc.Value = newLen; if (newLen >= oldLen) { return base.DefineOwnProperty("length", newLenDesc, throwOnError); } if (!oldLenDesc.Writable.Value.AsBoolean()) { if (throwOnError) { throw new JavaScriptException(_engine.TypeError); } return false; } bool newWritable; if (!newLenDesc.Writable.HasValue || newLenDesc.Writable.Value.AsBoolean()) { newWritable = true; } else { newWritable = false; newLenDesc.Writable = true; } var succeeded = base.DefineOwnProperty("length", newLenDesc, throwOnError); if (!succeeded) { return false; } // in the case of sparse arrays, treat each concrete element instead of // iterating over all indexes if (Properties.Count < oldLen - newLen) { var keys = Properties.Keys.ToArray(); foreach (var key in keys) { uint index; // is it the index of the array if (uint.TryParse(key, out index) && index >= newLen && index < oldLen) { var deleteSucceeded = Delete(key, false); if (!deleteSucceeded) { newLenDesc.Value = new JsValue(index + 1); if (!newWritable) { newLenDesc.Writable = JsValue.False; } base.DefineOwnProperty("length", newLenDesc, false); if (throwOnError) { throw new JavaScriptException(_engine.TypeError); } return false; } } } } else { while (newLen < oldLen) { // algorithm as per the spec oldLen--; var deleteSucceeded = Delete(TypeConverter.ToString(oldLen), false); if (!deleteSucceeded) { newLenDesc.Value = oldLen + 1; if (!newWritable) { newLenDesc.Writable = false; } base.DefineOwnProperty("length", newLenDesc, false); if (throwOnError) { throw new JavaScriptException(_engine.TypeError); } return false; } } } if (!newWritable) { DefineOwnProperty("length", new PropertyDescriptor(value: null, writable: false, enumerable: null, configurable: null), false); } return true; } else if (IsArrayIndex(propertyName)) { var index = TypeConverter.ToUint32(propertyName); if (index >= oldLen && !oldLenDesc.Writable.Value.AsBoolean()) { if (throwOnError) { throw new JavaScriptException(_engine.TypeError); } return false; } var succeeded = base.DefineOwnProperty(propertyName, desc, false); if (!succeeded) { if (throwOnError) { throw new JavaScriptException(_engine.TypeError); } return false; } if (index >= oldLen) { oldLenDesc.Value = index + 1; base.DefineOwnProperty("length", oldLenDesc, false); } return true; } return base.DefineOwnProperty(propertyName, desc, throwOnError); }
/// 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) { if (!CanPut(propertyName)) { if (throwOnError) { throw new JavaScriptException(Engine.TypeError); } return; } if (InternalArray != null) { int ind; if (int.TryParse(propertyName, out ind)) { object val; switch (value.Type) { case Types.Undefined: val = null; break; case Types.Null: val = null; break; case Types.Boolean: val = value.AsBoolean(); break; case Types.String: val = value.AsString(); break; case Types.Number: val = value.AsNumber(); break; case Types.Object: val = value; var ow = ((JsValue)val).AsObject() as ObjectWrapper; if (ow != null) { val = ow.Target; } break; default: throw new ArgumentOutOfRangeException(); } if (ind >= InternalArray.Count) { for (int i = InternalArray.Count; i <= ind; i++) { if (val != null) InternalArray.Add(GetDefault(val.GetType())); else InternalArray.Add(null); } } InternalArray[ind] = val; } } 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.Value.TryCast<ICallable>(); setter.Call(new JsValue(this), new[] { value }); } else { var newDesc = new PropertyDescriptor(value, true, true, true); DefineOwnProperty(propertyName, newDesc, throwOnError); } }