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); }
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 JsValue EvaluateAssignmentExpression(AssignmentExpression assignmentExpression) { var lref = EvaluateExpression(assignmentExpression.Left) as Reference; JsValue rval = _engine.GetValue(EvaluateExpression(assignmentExpression.Right)); if (lref == null) { throw new JavaScriptException(_engine.ReferenceError); } if (assignmentExpression.Operator == AssignmentOperator.Assign) // "=" { if (lref.IsStrict() && lref.GetBase().TryCast <EnvironmentRecord>() != null && (lref.GetReferencedName() == "eval" || lref.GetReferencedName() == "arguments")) { throw new JavaScriptException(_engine.SyntaxError); } _engine.PutValue(lref, rval); return(rval); } JsValue lval = _engine.GetValue(lref); switch (assignmentExpression.Operator) { case AssignmentOperator.PlusAssign: var lprim = TypeConverter.ToPrimitive(lval); var rprim = TypeConverter.ToPrimitive(rval); if (lprim.IsString() || rprim.IsString()) { lval = TypeConverter.ToString(lprim) + TypeConverter.ToString(rprim); } else { lval = TypeConverter.ToNumber(lprim) + TypeConverter.ToNumber(rprim); } break; case AssignmentOperator.MinusAssign: lval = TypeConverter.ToNumber(lval) - TypeConverter.ToNumber(rval); break; case AssignmentOperator.TimesAssign: if (lval == Undefined.Instance || rval == Undefined.Instance) { lval = Undefined.Instance; } else { lval = TypeConverter.ToNumber(lval) * TypeConverter.ToNumber(rval); } break; case AssignmentOperator.DivideAssign: lval = Divide(lval, rval); break; case AssignmentOperator.ModuloAssign: if (lval == Undefined.Instance || rval == Undefined.Instance) { lval = Undefined.Instance; } else { lval = TypeConverter.ToNumber(lval) % TypeConverter.ToNumber(rval); } break; case AssignmentOperator.BitwiseAndAssign: lval = TypeConverter.ToInt32(lval) & TypeConverter.ToInt32(rval); break; case AssignmentOperator.BitwiseOrAssign: lval = TypeConverter.ToInt32(lval) | TypeConverter.ToInt32(rval); break; case AssignmentOperator.BitwiseXOrAssign: lval = TypeConverter.ToInt32(lval) ^ TypeConverter.ToInt32(rval); break; case AssignmentOperator.LeftShiftAssign: lval = TypeConverter.ToInt32(lval) << (int)(TypeConverter.ToUint32(rval) & 0x1F); break; case AssignmentOperator.RightShiftAssign: lval = TypeConverter.ToInt32(lval) >> (int)(TypeConverter.ToUint32(rval) & 0x1F); break; case AssignmentOperator.UnsignedRightShiftAssign: lval = (uint)TypeConverter.ToInt32(lval) >> (int)(TypeConverter.ToUint32(rval) & 0x1F); break; default: throw new NotImplementedException(); } _engine.PutValue(lref, lval); return(lval); }
public static bool Equal(JsValue x, JsValue y) { var typex = x.Type; var typey = y.Type; if (typex == typey) { if (typex == Types.Undefined || typex == Types.Null) { return(true); } if (typex == Types.Number) { var nx = TypeConverter.ToNumber(x); var ny = TypeConverter.ToNumber(y); if (double.IsNaN(nx) || double.IsNaN(ny)) { return(false); } if (nx.Equals(ny)) { return(true); } return(false); } if (typex == Types.String) { return(TypeConverter.ToString(x) == TypeConverter.ToString(y)); } if (typex == Types.Boolean) { return(x.AsBoolean() == y.AsBoolean()); } return(x == y); } if (x == Null.Instance && y == Undefined.Instance) { return(true); } if (x == Undefined.Instance && y == Null.Instance) { return(true); } if (typex == Types.Number && typey == Types.String) { return(Equal(x, TypeConverter.ToNumber(y))); } if (typex == Types.String && typey == Types.Number) { return(Equal(TypeConverter.ToNumber(x), y)); } if (typex == Types.Boolean) { return(Equal(TypeConverter.ToNumber(x), y)); } if (typey == Types.Boolean) { return(Equal(x, TypeConverter.ToNumber(y))); } if (typey == Types.Object && (typex == Types.String || typex == Types.Number)) { return(Equal(x, TypeConverter.ToPrimitive(y))); } if (typex == Types.Object && (typey == Types.String || typey == Types.Number)) { return(Equal(TypeConverter.ToPrimitive(x), y)); } return(false); }
public JsValue EvaluateBinaryExpression(BinaryExpression expression) { var leftExpression = EvaluateExpression(expression.Left); JsValue left = _engine.GetValue(leftExpression); var rightExpression = EvaluateExpression(expression.Right); JsValue right = _engine.GetValue(rightExpression); JsValue value; switch (expression.Operator) { case BinaryOperator.Plus: var lprim = TypeConverter.ToPrimitive(left); var rprim = TypeConverter.ToPrimitive(right); if (lprim.IsString() || rprim.IsString()) { value = TypeConverter.ToString(lprim) + TypeConverter.ToString(rprim); } else { value = TypeConverter.ToNumber(lprim) + TypeConverter.ToNumber(rprim); } break; case BinaryOperator.Minus: value = TypeConverter.ToNumber(left) - TypeConverter.ToNumber(right); break; case BinaryOperator.Times: if (left == Undefined.Instance || right == Undefined.Instance) { value = Undefined.Instance; } else { value = TypeConverter.ToNumber(left) * TypeConverter.ToNumber(right); } break; case BinaryOperator.Divide: value = Divide(left, right); break; case BinaryOperator.Modulo: if (left == Undefined.Instance || right == Undefined.Instance) { value = Undefined.Instance; } else { value = TypeConverter.ToNumber(left) % TypeConverter.ToNumber(right); } break; case BinaryOperator.Equal: value = Equal(left, right); break; case BinaryOperator.NotEqual: value = !Equal(left, right); break; case BinaryOperator.Greater: value = Compare(right, left, false); if (value == Undefined.Instance) { value = false; } break; case BinaryOperator.GreaterOrEqual: value = Compare(left, right); if (value == Undefined.Instance || value.AsBoolean()) { value = false; } else { value = true; } break; case BinaryOperator.Less: value = Compare(left, right); if (value == Undefined.Instance) { value = false; } break; case BinaryOperator.LessOrEqual: value = Compare(right, left, false); if (value == Undefined.Instance || value.AsBoolean()) { value = false; } else { value = true; } break; case BinaryOperator.StrictlyEqual: return(StrictlyEqual(left, right)); case BinaryOperator.StricltyNotEqual: return(!StrictlyEqual(left, right)); case BinaryOperator.BitwiseAnd: return(TypeConverter.ToInt32(left) & TypeConverter.ToInt32(right)); case BinaryOperator.BitwiseOr: return(TypeConverter.ToInt32(left) | TypeConverter.ToInt32(right)); case BinaryOperator.BitwiseXOr: return(TypeConverter.ToInt32(left) ^ TypeConverter.ToInt32(right)); case BinaryOperator.LeftShift: return(TypeConverter.ToInt32(left) << (int)(TypeConverter.ToUint32(right) & 0x1F)); case BinaryOperator.RightShift: return(TypeConverter.ToInt32(left) >> (int)(TypeConverter.ToUint32(right) & 0x1F)); case BinaryOperator.UnsignedRightShift: return((uint)TypeConverter.ToInt32(left) >> (int)(TypeConverter.ToUint32(right) & 0x1F)); case BinaryOperator.InstanceOf: var f = right.TryCast <FunctionInstance>(); if (f == null) { throw new JavaScriptException(_engine.TypeError, "instanceof can only be used with a function object"); } value = f.HasInstance(left); break; case BinaryOperator.In: if (!right.IsObject()) { throw new JavaScriptException(_engine.TypeError, "in can only be used with an object"); } value = right.AsObject().HasProperty(TypeConverter.ToString(left)); break; default: throw new NotImplementedException(); } return(value); }