public override IEnumerable <KeyValuePair <string, PropertyDescriptor> > GetOwnProperties() { if (_length != null) { yield return(new KeyValuePair <string, PropertyDescriptor>(PropertyNameLength, _length)); } if (_dense != null) { var length = System.Math.Min(_dense.Length, GetLength()); for (var i = 0; i < length; i++) { if (_dense[i] != null) { yield return(new KeyValuePair <string, PropertyDescriptor>(TypeConverter.ToString(i), _dense[i])); } } } else { foreach (var entry in _sparse) { yield return(new KeyValuePair <string, PropertyDescriptor>(TypeConverter.ToString(entry.Key), entry.Value)); } } foreach (var entry in base.GetOwnProperties()) { yield return(entry); } }
public bool TryGetValue(uint index, out JsValue value) { value = Undefined; PropertyDescriptor desc; TryGetDescriptor(index, out desc); desc = desc ?? GetProperty(TypeConverter.ToString(index)) ?? PropertyDescriptor.Undefined; return(desc.TryGetValue(this, out value)); }
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); }
/// <inheritdoc /> internal override bool FindWithCallback( JsValue[] arguments, out uint index, out JsValue value) { var len = GetLength(); if (len == 0) { index = 0; value = Undefined; return(false); } var callbackfn = arguments.At(0); var thisArg = arguments.At(1); var callable = GetCallable(callbackfn); 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 testResult = callable.Call(thisArg, args); if (TypeConverter.ToBoolean(testResult)) { index = k; value = kvalue; return(true); } } } _engine._jsValueArrayPool.ReturnArray(args); index = 0; value = Undefined; return(false); }
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)); }
/// <summary> /// http://www.ecma-international.org/ecma-262/5.1/#sec-12.6.4 /// </summary> /// <param name="forInStatement"></param> /// <returns></returns> public Completion ExecuteForInStatement(ForInStatement forInStatement) { Identifier identifier = forInStatement.Left.Type == SyntaxNodes.VariableDeclaration ? forInStatement.Left.As <VariableDeclaration>().Declarations.First().Id : forInStatement.Left.As <Identifier>(); var varRef = _engine.EvaluateExpression(identifier) as Reference; var exprRef = _engine.EvaluateExpression(forInStatement.Right); var experValue = _engine.GetValue(exprRef); if (experValue == Undefined.Instance || experValue == Null.Instance) { return(new Completion(Completion.Normal, null, null)); } var obj = TypeConverter.ToObject(_engine, experValue); JsValue v = Null.Instance; // keys are constructed using the prototype chain var cursor = obj; var processedKeys = new HashSet <string>(); while (cursor != null) { var keys = cursor.GetOwnProperties().Select(x => x.Key).ToArray(); foreach (var p in keys) { if (processedKeys.Contains(p)) { continue; } processedKeys.Add(p); // collection might be modified by inner statement if (!cursor.HasOwnProperty(p)) { continue; } var value = cursor.GetOwnProperty(p); if (!value.Enumerable.HasValue || !value.Enumerable.Value) { continue; } _engine.PutValue(varRef, p); var stmt = ExecuteStatement(forInStatement.Body); if (stmt.Value.HasValue) { v = stmt.Value.Value; } if (stmt.Type == Completion.Break) { return(new Completion(Completion.Normal, v, null)); } if (stmt.Type != Completion.Continue) { if (stmt.Type != Completion.Normal) { return(stmt); } } } cursor = cursor.Prototype; } return(new Completion(Completion.Normal, v, null)); }