public virtual string getString(int i) { Object o = getObject(i); return((o is string) ? ((String)o) : JsSystem.toString(o)); }
/** * Returns the numerical value at array index i. If the actual value is not * numeric, it is converted automatically. */ public double getNumber(int i) { if (i >= size_) { return(0); } Object o = objects[i]; return(o == NUMBER_MARKER ? numbers[i] : JsSystem.toNumber(o)); }
/** * Returns a string representation of this array. This method does not check for * circular references, so if the array contains itself it will result in an * Exception. */ public override string ToString() { StringBuilder buf = new StringBuilder(); if (size_ > 0) { buf.Append(JsSystem.toString(getObject(0))); for (int i = 1; i < size_; i++) { buf.Append(","); buf.Append(JsSystem.toString(getObject(i))); } } return(buf.ToString()); }
/** * Creates a new function from the given function literal and context. */ public JsFunction(JsFunction literal, JsObject context) : base(literal.__proto__) { this.byteCode = literal.byteCode; this.context = context; this.functionLiterals = literal.functionLiterals; this.localNames = literal.localNames; this.numberLiterals = literal.numberLiterals; this.expectedParameterCount = literal.expectedParameterCount; this.prototype = literal.prototype; this.stringLiterals = literal.stringLiterals; this.varCount = literal.varCount; this.factory = JsSystem.getInstance(); this.factoryTypeId = JsSystem.FACTORY_ID_OBJECT; this.lineNumbers = literal.lineNumbers; }
/** * Performs multiplications for integer exponents. In all other cases, * exp(y * ln(x)) is calculated. */ public static double pow(double x, double y) { long n = (long)y; if (y > 0 && y == n) { double result = 1; while (n > 0) { if ((n & 1) != 0) { result *= x; n--; } x *= x; n >>= 1; } return(result); } return(exp(y * JsSystem.ln(x))); }
/** * Returns the boolean value at array index i. If the actual value is not * boolean, it is converted automatically according to the ECMA conversion * rules. This method handles Boolean.TRUE, Boolean.FALSE and numbers. All * other values are delegated to JsSystem.toBoolean() */ public bool getBoolean(int i) { if (i >= size_) { return(false); } Object o = objects[i]; if (o == NUMBER_MARKER) { double d = numbers[i]; return(d != 0 && !Double.IsNaN(d)); } if ((bool)o == true) { return(true); } if ((bool)o == false) { return(false); } return(JsSystem.toBoolean(o)); }
/** * Execute java member implementation. Parameters for functions start at * stack[sp+2]. Function and getter results are returned at stack[sp+0]. * The assignement value for a setter is stored at stack[sp+0]. */ public virtual void evalNative(int index, JsArray stack, int sp, int parCount) { Object obj; switch (index) { // object methods case ID_NOOP: break; case ID_INIT_OBJECT: obj = stack.getObject(sp + 2); if (isConstruction(stack, sp)) { if (obj is Boolean || obj is Double || obj is String) { value = obj; } else if (obj is JsObject) { stack.setObject(sp - 1, obj); } // otherwise, don't do anything -- regular constructor call } else { if (obj == null || obj == JsSystem.JS_NULL) { stack.setObject(sp, new JsObject(OBJECT_PROTOTYPE)); } else { stack.setObject(sp, JsSystem.toJsObject(obj)); } } break; case ID_TO_STRING: case ID_TO_LOCALE_STRING: stack.setObject(sp, JsSystem.toString(stack.getObject(sp))); break; case ID_HAS_OWN_PROPERTY: stack.setBoolean(sp, data != null && data[stack.getString(sp + 2)] != null); break; case ID_IS_PROTOTYPE_OF: obj = stack.getObject(sp + 2); stack.setBoolean(sp, false); while (obj is JsObject) { if (obj == this) { stack.setBoolean(sp, true); break; } } break; case ID_PROPERTY_IS_ENUMERABLE: obj = getRawInPrototypeChain(stack.getString(sp + 2)); stack.setBoolean(sp, obj != null && !(obj is JsFunction)); break; case ID_VALUE_OF: stack.setObject(sp, value == null ? this : value); break; // Number methods case ID_INIT_NUMBER: if (isConstruction(stack, sp)) { value = (Double)(stack.getNumber(sp + 2)); } else { stack.setNumber(sp, stack.getNumber(sp + 2)); } break; // Boolean methods case ID_INIT_BOOLEAN: if (isConstruction(stack, sp)) { value = stack.getBoolean(sp + 2) ? true : false; } else { stack.setObject(sp, stack.getBoolean(sp + 2) ? true : false); } break; case ID_INIT_STRING: if (isConstruction(stack, sp)) { value = stack.getString(sp + 2); } else { stack.setObject(sp, parCount == 0 ? "" : stack.getString(sp + 2)); } break; // initializers that can be used as functions need to be covered in Object case ID_INIT_ARRAY: JsArray array = (isConstruction(stack, sp) ? (JsArray)this : new JsArray()); if (parCount == 1 && stack.isNumber(sp + 2)) { array.setSize(stack.getInt(sp + 2)); } else { for (int i2 = 0; i2 < parCount; i2++) { stack.copy(sp + i2 + 2, array, i2); } } stack.setObject(sp, array); break; case ID_INIT_ERROR: if (isConstruction(stack, sp)) { setObject("message", stack.getString(sp + 2)); } else { stack.setObject(sp, new JsError(stack.getJsObject(sp), stack.getString(sp + 2))); } break; case ID_INIT_FUNCTION: // Note: this will exchange the "this" object at sp-1 if it is used as constructor bool construction = isConstruction(stack, sp); StringBuilder buf = new StringBuilder("(function("); for (int i3 = 0; i3 < parCount - 1; i3++) { if (i3 != 0) { buf.Append(','); } buf.Append(stack.getString(sp + 2 + i3)); } buf.Append("){"); if (parCount != 0) { buf.Append(stack.getString(sp + 2 + parCount - 1)); } buf.Append("});"); Console.WriteLine("eval: " + buf); JsObject global = (JsObject)stack.getObject(0); JsFunction eval = (JsFunction)global.getObject("eval"); stack.setObject(sp, global); // global stack.setObject(sp + 1, eval); stack.setObject(sp + 2, buf.ToString()); eval.eval(stack, sp, 1); if (construction) { stack.copy(sp, stack, sp - 1); } break; case ID_INIT_DATE: // reset to defaults if (isConstruction(stack, sp)) { JsDate d_2 = (JsDate)this; if (parCount == 1) { d_2.time.setTime(new DateTime((long)stack.getNumber(sp + 2))); } else if (parCount > 1) { d_2.time.setTime(new DateTime()); int year = stack.getInt(sp + 2); if (year >= 0 && year <= 99) { year += 1900; } d_2.setDate(false, year, stack.getNumber(sp + 3), stack.getNumber(sp + 4)); d_2.setTime(false, stack.getNumber(sp + 5), stack.getNumber(sp + 6), stack.getNumber(sp + 7), stack.getNumber(sp + 8)); } } else { stack.setObject(sp, new JsDate(JsDate.DATE_PROTOTYPE).toString(true, true, true)); } break; // global properties case ID_PRINT: Console.WriteLine(stack.getString(sp + 2)); break; case ID_PARSE_INT: String s = stack.getString(sp + 2).Trim().ToLower(); try { if (stack.isNull(sp + 3)) { stack.setInt(sp, s.StartsWith("0x") ? Convert.ToInt32(s.Substring(2), 16) : Convert.ToInt32(s)); } else { stack.setInt(sp, Convert.ToInt32(s, stack.getInt(sp + 3))); } } catch (FormatException) { stack.setInt(sp, 0); } break; case ID_PARSE_FLOAT: try { stack.setNumber(sp, Convert.ToDouble(stack.getString(sp + 2))); } catch (FormatException) { stack.setNumber(sp, double.NaN); } break; case ID_IS_NAN: stack.setBoolean(sp, double.IsNaN(stack.getNumber(sp + 2))); break; case ID_IS_FINITE: double d_ = stack.getNumber(sp + 2); stack.setBoolean(sp, !Double.IsInfinity(d_) && !Double.IsNaN(d_)); break; case ID_DECODE_URI: obj = stack.getObject(sp + 2); if (obj is sbyte[]) { stack.setObject(sp, JsSystem.decodeURI((sbyte[])obj)); } else { stack.setObject(sp, JsSystem.decodeURI(obj.ToString())); } break; case ID_ENCODE_URI: obj = stack.getObject(sp + 2); if (obj is sbyte[]) { stack.setObject(sp, JsSystem.encodeURI((sbyte[])obj)); } else { stack.setObject(sp, JsSystem.encodeURI(obj.ToString())); } break; //TODO Implement case ID_DECODE_URI_COMPONENT: case ID_ENCODE_URI_COMPONENT: throw new Exception("NYI"); // math properties case ID_ABS: stack.setNumber(sp, Math.Abs(stack.getNumber(sp + 2))); break; case ID_ACOS: case ID_ASIN: case ID_ATAN: case ID_ATAN2: throw new Exception("NYI"); case ID_CEIL: stack.setNumber(sp, Math.Ceiling(stack.getNumber(sp + 2))); break; case ID_COS: stack.setNumber(sp, Math.Cos(stack.getNumber(sp + 2))); break; case ID_EXP: stack.setNumber(sp, JsSystem.exp(stack.getNumber(sp + 2))); break; case ID_FLOOR: stack.setNumber(sp, Math.Floor(stack.getNumber(sp + 2))); break; case ID_LOG: stack.setNumber(sp, JsSystem.ln(stack.getNumber(sp + 2))); break; case ID_MAX: double d2 = Double.NegativeInfinity; for (int i3 = 0; i3 < parCount; i3++) { d2 = Math.Max(d2, stack.getNumber(sp + 2 + i3)); } stack.setNumber(sp, d2); break; case ID_MIN: double d3 = Double.PositiveInfinity; for (int i4 = 0; i4 < parCount; i4++) { d3 = Math.Min(d3, stack.getNumber(sp + 2 + i4)); } stack.setNumber(sp, d3); break; case ID_POW: stack.setNumber(sp, JsSystem.pow(stack.getNumber(sp + 2), stack.getNumber(sp + 3))); break; case ID_RANDOM: stack.setNumber(sp, JsSystem.random.NextDouble()); break; case ID_ROUND: stack.setNumber(sp, Math.Floor(stack.getNumber(sp + 2) + 0.5)); break; case ID_SIN: stack.setNumber(sp, Math.Sin(stack.getNumber(sp + 2))); break; case ID_SQRT: stack.setNumber(sp, Math.Sqrt(stack.getNumber(sp + 2))); break; case ID_TAN: stack.setNumber(sp, Math.Tan(stack.getNumber(sp + 2))); break; // string methods case ID_FROM_CHAR_CODE: char[] chars = new char[parCount]; for (int i5 = 0; i5 < parCount; i5++) { chars[i5] = (char)stack.getInt(sp + 2 + i5); } stack.setObject(sp, new String(chars)); break; // string.prototype methods case ID_CHAR_AT: s = stack.getString(sp); int i6 = stack.getInt(sp + 2); stack.setObject(sp, i6 < 0 || i6 >= s.Length ? "" : s.Substring(i6, 1)); break; case ID_CHAR_CODE_AT: s = stack.getString(sp); int i7 = stack.getInt(sp + 2); stack.setNumber(sp, i7 < 0 || i7 >= s.Length ? Double.NaN : s[i7]); break; case ID_CONCAT: buf = new StringBuilder(stack.getString(sp)); for (int i8 = 0; i8 < parCount; i8++) { buf.Append(stack.getString(sp + i8 + 2)); } stack.setObject(sp, buf.ToString()); break; case ID_INDEX_OF: stack.setNumber(sp, stack.getString(sp).IndexOf(stack.getString(sp + 2), stack.getInt(sp + 3))); break; case ID_LAST_INDEX_OF: s = stack.getString(sp); String find = stack.getString(sp + 2); double d4 = stack.getNumber(sp + 3); int max = (Double.IsNaN(d4)) ? s.Length : (int)d4; int best = -1; while (true) { int found = s.IndexOf(find, best + 1); if (found == -1 || found > max) { break; } best = found; } stack.setNumber(sp, best); break; case ID_LOCALE_COMPARE: stack.setNumber(sp, stack.getString(sp).CompareTo(stack.getString(sp + 2))); break; case ID_REPLACE: s = stack.getString(sp); find = stack.getString(sp + 2); String replace = stack.getString(sp + 3); if (!find.Equals("")) { StringBuilder sb = new StringBuilder(s); int length = find.Length; // Parse nodes into vector while ((index = sb.ToString().IndexOf(find)) >= 0) { sb.Remove(index, index + length); sb.Insert(index, replace); } stack.setObject(sp, sb.ToString()); sb = null; } break; case ID_MATCH: case ID_SEARCH: throw new Exception("Regexp NYI"); case ID_SLICE: s = stack.getString(sp); int len = s.Length; int start = stack.getInt(sp + 2); int end = stack.isNull(sp + 3) ? len : stack.getInt(sp + 3); if (start < 0) { start = Math.Max(len + start, 0); } if (end < 0) { end = Math.Max(len + start, 0); } if (start > len) { start = len; } if (end > len) { end = len; } if (end < start) { end = start; } stack.setObject(sp, s.Substring(start, end - start)); break; case ID_SPLIT: s = stack.getString(sp); String sep = stack.getString(sp + 2); double limit = stack.getNumber(sp + 3); if (Double.IsNaN(limit) || limit < 1) { limit = Double.MaxValue; } JsArray a = new JsArray(); if (sep.Length == 0) { if (s.Length < limit) { limit = s.Length; } for (int i9 = 0; i9 < limit; i9++) { a.setObject(i9, s.Substring(i9, 1)); } } else { int cut0 = 0; while (cut0 < s.Length && a.size() < limit) { int cut = s.IndexOf(sep, cut0); if (cut == -1) { cut = s.Length; } a.setObject(a.size(), s.Substring(cut0, cut - cut0)); cut0 = cut + sep.Length; } } stack.setObject(sp, a); break; case ID_SUBSTRING: s = stack.getString(sp); len = s.Length; start = stack.getInt(sp + 2); end = stack.isNull(sp + 3) ? len : stack.getInt(sp + 3); if (start > end) { int tmp = end; end = start; start = tmp; } start = Math.Min(Math.Max(0, start), len); end = Math.Min(Math.Max(0, end), len); stack.setObject(sp, s.Substring(start, end - start)); break; case ID_TO_LOWER_CASE: //TODO: which locale to use as defautlt? us? case ID_TO_LOCALE_LOWER_CASE: stack.setObject(sp, stack.getString(sp + 2).ToLower()); break; case ID_TO_UPPER_CASE: //TODO: which locale to use as defautlt? us? case ID_TO_LOCALE_UPPER_CASE: stack.setObject(sp, stack.getString(sp + 2).ToUpper()); break; case ID_LENGTH: stack.setInt(sp, ToString().Length); break; case ID_LENGTH_SET: // cannot be changed! break; case ID_TO_EXPONENTIAL: case ID_TO_FIXED: case ID_TO_PRECISION: stack.setObject(sp, JsSystem.formatNumber(index, stack.getNumber(sp + 2), stack.getNumber(sp + 3))); break; case ID_UTC: JsDate date = new JsDate(JsDate.DATE_PROTOTYPE); date.time.setTime(new DateTime()); int year2 = stack.getInt(sp + 2); if (year2 >= 0 && year2 <= 99) { year2 += 1900; } date.setDate(true, year2, stack.getNumber(sp + 3), stack.getNumber(sp + 4)); date.setTime(true, stack.getNumber(sp + 5), stack.getNumber(sp + 6), stack.getNumber(sp + 7), stack.getNumber(sp + 8)); stack.setNumber(sp, date.time.getTime().Ticks); break; case ID_PARSE: double[] vals = { Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN }; s = stack.getString(sp + 2); int curr = -1; int pos = 0; for (int i10 = 0; i10 < s.Length; i10++) { char c = s[i10]; if (c >= '0' && c <= '9') { if (curr == -1) { curr = c - 48; } else { curr = curr * 10 + (c - 48); } } else if (curr != -1) { if (pos < vals.Length) { vals[pos++] = curr; } curr = -1; } } if (curr != -1 && pos < vals.Length) { vals[pos++] = curr; } bool utc = s.EndsWith("GMT") || s.EndsWith("UTC"); date = new JsDate(JsDate.DATE_PROTOTYPE); date.time.setTime(new DateTime()); date.setDate(utc, vals[0], vals[1], vals[2]); date.setTime(utc, vals[3], vals[4], vals[5], vals[6]); stack.setNumber(sp, date.time.getTime().Ticks); break; // Math constants case ID_E: stack.setNumber(sp, Math.E); break; case ID_LN10: stack.setNumber(sp, 2.302585092994046); break; case ID_LN2: stack.setNumber(sp, JsSystem.LN2); break; case ID_LOG2E: stack.setNumber(sp, 1.4426950408889634); break; case ID_LOG10E: stack.setNumber(sp, 0.4342944819032518); break; case ID_PI: stack.setNumber(sp, Math.PI); break; case ID_SQRT1_2: stack.setNumber(sp, Math.Sqrt(0.5)); break; case ID_SQRT2: stack.setNumber(sp, Math.Sqrt(2.0)); break; case ID_E_SET: case ID_LN10_SET: case ID_LN2_SET: case ID_LOG2E_SET: case ID_LOG10E_SET: case ID_PI_SET: case ID_SQRT1_2_SET: case ID_SQRT2_SET: // dont do anything: cannot overwrite those values! break; default: throw new ArgumentException("Unknown native id: " + index + " this: " + this); } }
/** * Returns the string value for the given key. If the actual value is not a * string, it is converted automatically, using the ECMA 262 conversion * rules. */ public String getString(String key) { return(JsSystem.toString(getObject(key))); }
/** * Returns the numeric value for the given key. If the actual value is not * numeric, it is converted automatically, using the ECMA 262 conversion * rules. */ public double getNumber(String key) { return(JsSystem.toNumber(getObject(key))); }
public virtual JsObject getJsObject(int i) { return(JsSystem.toJsObject(getObject(i))); }
public Eval() : base(COMPILER_PROTOTYPE) { scopeChain = JsSystem.createGlobal(); addVar("eval", new JsFunction(ID_EVAL, 2)); }