Browse Source

Implementing String.prototype

Sebastien Ros 12 years ago
parent
commit
df03017445

+ 1 - 1
Jint/Native/Function/EvalFunctionInstance.cs

@@ -21,7 +21,7 @@ namespace Jint.Native.Function
                 throw new JavaScriptException(Engine.SyntaxError, "eval() is not allowed in strict mode.");
             }
 
-            var code = TypeConverter.ToString(arguments[0]);
+            var code = TypeConverter.ToString(arguments.At(0));
 
             var parser = new JavaScriptParser();
             try

+ 6 - 1
Jint/Native/Function/ScriptFunctionInstance.cs

@@ -148,7 +148,12 @@ namespace Jint.Native.Function
                     throw new JavaScriptException(result.Value);
                 }
 
-                return result.Value ?? Undefined.Instance;
+                if (result.Type == Completion.Return)
+                {
+                    return result.Value;
+                }
+                
+                return Undefined.Instance;
             }
         }
 

+ 2 - 2
Jint/Native/Object/ObjectPrototype.cs

@@ -22,7 +22,7 @@ namespace Jint.Native.Object
 
         public void Configure()
         {
-            FastAddProperty("toString", new ClrFunctionInstance<object, string>(Engine, ToString), true, false, true);
+            FastAddProperty("toString", new ClrFunctionInstance<object, string>(Engine, ToObjectString), true, false, true);
             FastAddProperty("toLocaleString", new ClrFunctionInstance<object, object>(Engine, ToLocaleString), true, false, true);
             FastAddProperty("valueOf", new ClrFunctionInstance<object, object>(Engine, ValueOf), true, false, true);
             FastAddProperty("hasOwnProperty", new ClrFunctionInstance<object, bool>(Engine, HasOwnProperty), true, false, true);
@@ -89,7 +89,7 @@ namespace Jint.Native.Object
         /// <param name="thisObject"></param>
         /// <param name="arguments"></param>
         /// <returns></returns>
-        public string ToString(object thisObject, object[] arguments)
+        public string ToObjectString(object thisObject, object[] arguments)
         {
             if (thisObject == Undefined.Instance)
             {

+ 2 - 2
Jint/Native/RegExp/RegExpConstructor.cs

@@ -61,12 +61,12 @@ namespace Jint.Native.RegExp
             var flags = arguments.Length > 1 ? arguments[1] : Undefined.Instance;
 
             var r = pattern as RegExpInstance;
-            if (pattern != Undefined.Instance && flags == Undefined.Instance && r != null)
+            if (pattern != null && flags == Undefined.Instance && r != null)
             {
                 p = r.Pattern;
                 f = r.Flags;
             }
-            else if (pattern != Undefined.Instance && flags != Undefined.Instance && r != null)
+            else if (pattern != null && flags != Undefined.Instance && r != null)
             {
                 throw new JavaScriptException(Engine.TypeError);
             }

+ 4 - 4
Jint/Native/RegExp/RegExpPrototype.cs

@@ -52,7 +52,7 @@ namespace Jint.Native.RegExp
             return match != Null.Instance;
         }
 
-        private object Exec(object thisObj, object[] arguments)
+        internal object Exec(object thisObj, object[] arguments)
         {
             var R = TypeConverter.ToObject(Engine, thisObj) as RegExpInstance;
             if (R == null)
@@ -74,7 +74,7 @@ namespace Jint.Native.RegExp
             Match r = null;
             if (i < 0 || i >= length)
             {
-                R.Put("lastIndex", 0, true);
+                R.Put("lastIndex", (double) 0, true);
                 return Null.Instance;
             }
 
@@ -82,7 +82,7 @@ namespace Jint.Native.RegExp
 
             if (!r.Success)
             {
-                R.Put("lastIndex", 0, true);
+                R.Put("lastIndex", (double) 0, true);
                 return Null.Instance;
             }
 
@@ -90,7 +90,7 @@ namespace Jint.Native.RegExp
             
             if (global)
             {
-                R.Put("lastIndex", e, true);
+                R.Put("lastIndex", (double) e, true);
             }
             var n = r.Groups.Count;
             var a = Engine.Array.Construct(Arguments.Empty);

+ 1 - 1
Jint/Native/String/StringConstructor.cs

@@ -31,7 +31,7 @@ namespace Jint.Native.String
 
         public void Configure()
         {
-            FastAddProperty("fromCharCode", new ClrFunctionInstance<object, string>(Engine, FromCharCode), false, false, false);
+            FastAddProperty("fromCharCode", new ClrFunctionInstance<object, string>(Engine, FromCharCode, 1), false, false, false);
         }
 
         private static string FromCharCode(object thisObj, object[] arguments)

+ 2 - 6
Jint/Native/String/StringInstance.cs

@@ -1,5 +1,4 @@
-using System;
-using Jint.Native.Object;
+using Jint.Native.Object;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
 
@@ -7,12 +6,9 @@ namespace Jint.Native.String
 {
     public class StringInstance : ObjectInstance, IPrimitiveType
     {
-        private readonly Engine _engine;
-
         public StringInstance(Engine engine)
             : base(engine)
         {
-            _engine = engine;
         }
 
         public override string Class
@@ -25,7 +21,7 @@ namespace Jint.Native.String
 
         Types IPrimitiveType.Type
         {
-            get { return Types.Boolean; }
+            get { return Types.String; }
         }
 
         object IPrimitiveType.PrimitiveValue

+ 440 - 56
Jint/Native/String/StringPrototype.cs

@@ -1,5 +1,11 @@
 using System;
+using System.Globalization;
+using System.Text;
+using Jint.Native.Array;
+using Jint.Native.Object;
+using Jint.Native.RegExp;
 using Jint.Runtime;
+using Jint.Runtime.Descriptors;
 using Jint.Runtime.Interop;
 
 namespace Jint.Native.String
@@ -21,93 +27,470 @@ namespace Jint.Native.String
             obj.PrimitiveValue = "";
             obj.Extensible = true;
 
-            obj.FastAddProperty("constructor", stringConstructor, false, false, false);
+            obj.FastAddProperty("constructor", stringConstructor, true, false, true);
 
             return obj;
         }
 
         public void Configure()
         {
-            FastAddProperty("toString", new ClrFunctionInstance<object, object>(Engine, ToStringString), false, false, false);
-            FastAddProperty("valueOf", new ClrFunctionInstance<StringInstance, string>(Engine, ValueOf), false, false, false);
-            FastAddProperty("charAt", new ClrFunctionInstance<object, object>(Engine, CharAt), false, false, false);
-            FastAddProperty("charCodeAt", new ClrFunctionInstance<object, object>(Engine, CharCodeAt), false, false, false);
-            FastAddProperty("concat", new ClrFunctionInstance<object, object>(Engine, Concat), false, false, false);
-            FastAddProperty("indexOf", new ClrFunctionInstance<object, object>(Engine, IndexOf), false, false, false);
-            FastAddProperty("lastIndexOf", new ClrFunctionInstance<object, object>(Engine, LastIndexOf), false, false, false);
-            FastAddProperty("localeCompare", new ClrFunctionInstance<object, object>(Engine, LocaleCompare), false, false, false);
-            FastAddProperty("match", new ClrFunctionInstance<object, object>(Engine, Match), false, false, false);
-            FastAddProperty("replace", new ClrFunctionInstance<object, object>(Engine, Replace), false, false, false);
-            FastAddProperty("search", new ClrFunctionInstance<object, object>(Engine, Search), false, false, false);
-            FastAddProperty("slice", new ClrFunctionInstance<object, object>(Engine, Slice), false, false, false);
-            FastAddProperty("split", new ClrFunctionInstance<object, object>(Engine, Split), false, false, false);
-            FastAddProperty("substring", new ClrFunctionInstance<object, object>(Engine, Substring), false, false, false);
-            FastAddProperty("toLowerCase", new ClrFunctionInstance<object, object>(Engine, ToLowerCase), false, false, false);
-            FastAddProperty("toLocaleLowerCase", new ClrFunctionInstance<object, object>(Engine, ToLocaleLowerCase), false, false, false);
-            FastAddProperty("toUpperCase", new ClrFunctionInstance<object, object>(Engine, ToUpperCase), false, false, false);
-            FastAddProperty("toLocaleUpperCase", new ClrFunctionInstance<object, object>(Engine, ToLocaleUpperCase), false, false, false);
-            FastAddProperty("trim", new ClrFunctionInstance<object, object>(Engine, Trim), false, false, false);
-        }
-        private object Trim(object thisObj, object[] arguments)
+            FastAddProperty("toString", new ClrFunctionInstance<object, string>(Engine, ToStringString), true, false, true);
+            FastAddProperty("valueOf", new ClrFunctionInstance<object, string>(Engine, ValueOf), true, false, true);
+            FastAddProperty("charAt", new ClrFunctionInstance<object, object>(Engine, CharAt, 1), true, false, true);
+            FastAddProperty("charCodeAt", new ClrFunctionInstance<object, object>(Engine, CharCodeAt, 1), true, false, true);
+            FastAddProperty("concat", new ClrFunctionInstance<object, string>(Engine, Concat, 1), true, false, true);
+            FastAddProperty("indexOf", new ClrFunctionInstance<object, double>(Engine, IndexOf, 1), true, false, true);
+            FastAddProperty("lastIndexOf", new ClrFunctionInstance<object, double>(Engine, LastIndexOf, 1), true, false, true);
+            FastAddProperty("localeCompare", new ClrFunctionInstance<object, double>(Engine, LocaleCompare), true, false, true);
+            FastAddProperty("match", new ClrFunctionInstance<object, object>(Engine, Match, 1), true, false, true);
+            FastAddProperty("replace", new ClrFunctionInstance<object, object>(Engine, Replace), true, false, true);
+            FastAddProperty("search", new ClrFunctionInstance<object, double>(Engine, Search, 1), true, false, true);
+            FastAddProperty("slice", new ClrFunctionInstance<object, string>(Engine, Slice, 2), true, false, true);
+            FastAddProperty("split", new ClrFunctionInstance<object, ArrayInstance>(Engine, Split, 2), true, false, true);
+            FastAddProperty("substring", new ClrFunctionInstance<object, string>(Engine, Substring, 2), true, false, true);
+            FastAddProperty("toLowerCase", new ClrFunctionInstance<object, string>(Engine, ToLowerCase), true, false, true);
+            FastAddProperty("toLocaleLowerCase", new ClrFunctionInstance<object, string>(Engine, ToLocaleLowerCase), true, false, true);
+            FastAddProperty("toUpperCase", new ClrFunctionInstance<object, string>(Engine, ToUpperCase), true, false, true);
+            FastAddProperty("toLocaleUpperCase", new ClrFunctionInstance<object, string>(Engine, ToLocaleUpperCase), true, false, true);
+            FastAddProperty("trim", new ClrFunctionInstance<object, string>(Engine, Trim), true, false, true);
+        }
+
+        private string ToStringString(object thisObj, object[] arguments)
         {
-            throw new NotImplementedException();
+            var s = TypeConverter.ToObject(Engine, thisObj) as StringInstance;
+            if (s == null)
+            {
+                throw new JavaScriptException(Engine.TypeError);
+            }
+
+            return s.PrimitiveValue;
         }
-        private object ToLocaleUpperCase(object thisObj, object[] arguments)
+
+        private string Trim(object thisObj, object[] arguments)
         {
-            throw new NotImplementedException();
+            TypeConverter.CheckObjectCoercible(Engine, thisObj);
+            var s = TypeConverter.ToString(thisObj);
+            return s.Trim();
         }
-        private object ToUpperCase(object thisObj, object[] arguments)
+
+        private static string ToLocaleUpperCase(object thisObj, object[] arguments)
         {
-            throw new NotImplementedException();
+            var s = TypeConverter.ToString(thisObj);
+            return s.ToUpper();
         }
-        private object ToLocaleLowerCase(object thisObj, object[] arguments)
+
+        private static string ToUpperCase(object thisObj, object[] arguments)
         {
-            throw new NotImplementedException();
+            var s = TypeConverter.ToString(thisObj);
+            return s.ToUpperInvariant();
         }
-        private object ToLowerCase(object thisObj, object[] arguments)
+
+        private static string ToLocaleLowerCase(object thisObj, object[] arguments)
         {
-            throw new NotImplementedException();
+            var s = TypeConverter.ToString(thisObj);
+            return s.ToLower();
         }
-        private object Substring(object thisObj, object[] arguments)
+
+        private static string ToLowerCase(object thisObj, object[] arguments)
         {
-            throw new NotImplementedException();
+            var s = TypeConverter.ToString(thisObj);
+            return s.ToLowerInvariant();
         }
-        private object Split(object thisObj, object[] arguments)
+
+        private string Substring(object thisObj, object[] arguments)
         {
-            throw new NotImplementedException();
+            TypeConverter.CheckObjectCoercible(Engine, thisObj);
+
+            var s = TypeConverter.ToString(thisObj);
+            var start = TypeConverter.ToNumber(arguments.At(0));
+            var end = TypeConverter.ToNumber(arguments.At(1));
+
+            if (double.IsNaN(start) || start < 0)
+            {
+                start = 0;
+            }
+
+            if (double.IsNaN(end) || end < 0)
+            {
+                end = 0;
+            }
+
+            var len = s.Length;
+            var intStart = (int) TypeConverter.ToInteger(start);
+            var intEnd = arguments.At(1) == Undefined.Instance ? len : (int) TypeConverter.ToInteger(end);
+            var finalStart = System.Math.Min(len, System.Math.Max(intStart, 0));
+            var finalEnd = System.Math.Min(len, System.Math.Max(intEnd, 0));
+            var from = System.Math.Min(finalStart, finalEnd);
+            var to = System.Math.Max(finalStart, finalEnd);
+            return s.Substring(from, to - from);
         }
-        private object Slice(object thisObj, object[] arguments)
+
+        private ArrayInstance Split(object thisObj, object[] arguments)
         {
-            throw new NotImplementedException();
+            TypeConverter.CheckObjectCoercible(Engine, thisObj);
+            var s = TypeConverter.ToString(thisObj);
+
+            var separator = arguments.At(0);
+            var l = arguments.At(1);
+
+            var a = (ArrayInstance) Engine.Array.Construct(Arguments.Empty);
+            var lengthA = 0;
+            var limit = l == Undefined.Instance ? UInt32.MaxValue : TypeConverter.ToUint32(l);
+            var len = s.Length;
+            var p = 0;
+            
+            if (limit == 0)
+            {
+                return a;
+            }
+
+            if (separator == Undefined.Instance)
+            {
+                return (ArrayInstance) Engine.Array.Construct(Arguments.From(s));
+            }
+
+            var rx = TypeConverter.ToObject(Engine, separator) as RegExpInstance;
+            if (rx != null)
+            {
+                var match = rx.Value.Match(s, 0);
+
+                int lastIndex = 0;
+                int index = 0;
+                while (match.Success && index < limit)
+                {
+                    if (match.Length == 0 && (match.Index == 0 || match.Index == len || match.Index == lastIndex))
+                    {
+                        match = match.NextMatch();
+                        continue;
+                    }
+
+                    // Add the match results to the array.
+                    a.DefineOwnProperty(index++.ToString(), new DataDescriptor(s.Substring(lastIndex, match.Index - lastIndex)) {Writable = true, Enumerable = true,Configurable = true}, false);
+                    
+                    if (index >= limit)
+                    {
+                        return a;
+                    }
+
+                    lastIndex = match.Index + match.Length;
+                    for (int i = 1; i < match.Groups.Count; i++)
+                    {
+                        var group = match.Groups[i];
+                        object item = Undefined.Instance;
+                        if (group.Captures.Count > 0)
+                        {
+                            item = match.Groups[i].Value;
+                        }
+
+                        a.DefineOwnProperty(index++.ToString(), new DataDescriptor(item) { Writable = true, Enumerable = true, Configurable = true }, false);
+
+                        if (index >= limit)
+                        {
+                            return a;
+                        }
+                    }
+
+                    match = match.NextMatch();
+                }
+
+                return a;
+            }
+            else
+            {
+                var sep = TypeConverter.ToString(separator);
+
+
+                var segments = s.Split(new [] { sep }, StringSplitOptions.None);
+                for (int i = 0; i < segments.Length && i < limit; i++)
+                {
+                    a.DefineOwnProperty(i.ToString(), new DataDescriptor(segments[i]) {Writable = true, Enumerable = true, Configurable=true} , false);
+                }
+            
+                return a;
+            }
+            
+        }
+
+        private string Slice(object thisObj, object[] arguments)
+        {
+            TypeConverter.CheckObjectCoercible(Engine, thisObj);
+
+            var s = TypeConverter.ToString(thisObj);
+            var start = TypeConverter.ToNumber(arguments.At(0));
+            var end = TypeConverter.ToNumber(arguments.At(1));
+            var len = s.Length;
+            var intStart = (int)TypeConverter.ToInteger(start);
+            var intEnd = arguments.At(1) == Undefined.Instance ? len : (int)TypeConverter.ToInteger(end);
+            var from = intStart < 0 ? System.Math.Max(len + intStart, 0) : System.Math.Min(intStart, len);
+            var to = intEnd < 0 ? System.Math.Max(len + intEnd, 0) : System.Math.Min(intEnd, len);
+            var span = System.Math.Max(to - from, 0);
+
+            return s.Substring(from, span);
         }
-        private object Search(object thisObj, object[] arguments)
+
+        private double Search(object thisObj, object[] arguments)
         {
-            throw new NotImplementedException();
+            TypeConverter.CheckObjectCoercible(Engine, thisObj);
+
+            var s = TypeConverter.ToString(thisObj);
+
+            var regex = arguments.At(0);
+            var rx = TypeConverter.ToObject(Engine, regex) as RegExpInstance ?? (RegExpInstance)Engine.RegExp.Construct(new[] { regex });
+            var match = rx.Value.Match(s);
+            if (!match.Success)
+            {
+                return -1;
+            }
+
+            return match.Index;
         }
+
         private object Replace(object thisObj, object[] arguments)
         {
             throw new NotImplementedException();
+            /*
+            TypeConverter.CheckObjectCoercible(Engine, thisObj);
+
+            var s = TypeConverter.ToString(thisObj);
+            var searchValue = arguments.At(0);
+            var replaceValue = arguments.At(1);
+
+            var rx = TypeConverter.ToObject(Engine, searchValue) as RegExpInstance;
+            if (rx != null)
+            {
+
+
+
+
+
+
+                // Check if the replacement string contains any patterns.
+                bool replaceTextContainsPattern = replaceText.IndexOf('$') >= 0;
+
+                // Replace the input string with replaceText, recording the last match found.
+                Match lastMatch = null;
+                string result = rx.Value.Replace(s, match =>
+                {
+                    lastMatch = match;
+
+                    // If there is no pattern, replace the pattern as is.
+                    if (replaceTextContainsPattern == false)
+                        return replaceText;
+
+                    // Patterns
+                    // $$	Inserts a "$".
+                    // $&	Inserts the matched substring.
+                    // $`	Inserts the portion of the string that precedes the matched substring.
+                    // $'	Inserts the portion of the string that follows the matched substring.
+                    // $n or $nn	Where n or nn are decimal digits, inserts the nth parenthesized submatch string, provided the first argument was a RegExp object.
+                    var replacementBuilder = new System.Text.StringBuilder();
+                    for (int i = 0; i < replaceText.Length; i++)
+                    {
+                        char c = replaceText[i];
+                        if (c == '$' && i < replaceText.Length - 1)
+                        {
+                            c = replaceText[++i];
+                            if (c == '$')
+                                replacementBuilder.Append('$');
+                            else if (c == '&')
+                                replacementBuilder.Append(match.Value);
+                            else if (c == '`')
+                                replacementBuilder.Append(input.Substring(0, match.Index));
+                            else if (c == '\'')
+                                replacementBuilder.Append(input.Substring(match.Index + match.Length));
+                            else if (c >= '0' && c <= '9')
+                            {
+                                int matchNumber1 = c - '0';
+
+                                // The match number can be one or two digits long.
+                                int matchNumber2 = 0;
+                                if (i < replaceText.Length - 1 && replaceText[i + 1] >= '0' && replaceText[i + 1] <= '9')
+                                    matchNumber2 = matchNumber1*10 + (replaceText[i + 1] - '0');
+
+                                // Try the two digit capture first.
+                                if (matchNumber2 > 0 && matchNumber2 < match.Groups.Count)
+                                {
+                                    // Two digit capture replacement.
+                                    replacementBuilder.Append(match.Groups[matchNumber2].Value);
+                                    i++;
+                                }
+                                else if (matchNumber1 > 0 && matchNumber1 < match.Groups.Count)
+                                {
+                                    // Single digit capture replacement.
+                                    replacementBuilder.Append(match.Groups[matchNumber1].Value);
+                                }
+                                else
+                                {
+                                    // Capture does not exist.
+                                    replacementBuilder.Append('$');
+                                    i--;
+                                }
+                            }
+                            else
+                            {
+                                // Unknown replacement pattern.
+                                replacementBuilder.Append('$');
+                                replacementBuilder.Append(c);
+                            }
+                        }
+                        else
+                            replacementBuilder.Append(c);
+                    }
+
+                    return replacementBuilder.ToString();
+                }, this.Global == true ? -1 : 1);
+
+                // Set the deprecated RegExp properties if at least one match was found.
+                if (lastMatch != null)
+                    this.Engine.RegExp.SetDeprecatedProperties(input, lastMatch);
+
+                return result;
+            }
+            else
+            {
+                var searchString = TypeConverter.ToString(searchValue);
+                var m = 0;
+
+                // Find the first occurrance of substr.
+                int start = thisObject.IndexOf(substr, StringComparison.Ordinal);
+                if (start == -1)
+                    return thisObject;
+                int end = start + substr.Length;
+
+                // Replace only the first match.
+                var result = new System.Text.StringBuilder(thisObject.Length + (replaceText.Length - substr.Length));
+                result.Append(thisObject, 0, start);
+                result.Append(replaceText);
+                result.Append(thisObject, end, thisObject.Length - end);
+                return result.ToString();
+
+
+
+            }
+             * */
         }
+
         private object Match(object thisObj, object[] arguments)
         {
-            throw new NotImplementedException();
+            TypeConverter.CheckObjectCoercible(Engine, thisObj);
+
+            var s = TypeConverter.ToString(thisObj);
+
+            var regex = arguments.At(0);
+            RegExpInstance rx = null;
+            if (TypeConverter.GetType(regex) == Types.Object)
+            {
+                rx = regex as RegExpInstance;
+            }
+
+            rx = rx ?? (RegExpInstance) Engine.RegExp.Construct(new[] {regex});
+
+            var global = (bool) rx.Get("global");
+            if (!global)
+            {
+                return Engine.RegExp.PrototypeObject.Exec(rx, Arguments.From(s));
+            }
+            else
+            {
+                rx.Put("lastIndex", (double) 0, false);
+                var a = Engine.Array.Construct(Arguments.Empty);
+                double previousLastIndex = 0;
+                var n = 0;
+                var lastMatch = true;
+                while (lastMatch)
+                {
+                    var result = Engine.RegExp.PrototypeObject.Exec(rx, Arguments.From(s)) as ObjectInstance;
+                    if (result == null)
+                    {
+                        lastMatch = false;
+                    }
+                    else
+                    {
+                        var thisIndex = (double) rx.Get("lastIndex");
+                        if (thisIndex == previousLastIndex)
+                        {
+                            rx.Put("lastIndex", (double) thisIndex + 1, false);
+                            previousLastIndex = thisIndex;
+                        }
+
+                        var matchStr = result.Get("0");
+                        a.DefineOwnProperty(TypeConverter.ToString(n), new DataDescriptor(matchStr) { Writable = true, Enumerable = true, Configurable = true}, false);
+                        n++;
+                    }
+                }
+                if (n == 0)
+                {
+                    return Null.Instance;
+                }
+                return a;
+            }
+
         }
-        private object LocaleCompare(object thisObj, object[] arguments)
+
+        private double LocaleCompare(object thisObj, object[] arguments)
         {
-            throw new NotImplementedException();
+            TypeConverter.CheckObjectCoercible(Engine, thisObj);
+
+            var s = TypeConverter.ToString(thisObj);
+            var that = TypeConverter.ToString(arguments.Length > 0 ? arguments[0] : Undefined.Instance);
+
+            return string.CompareOrdinal(s, that);
         }
-        private object LastIndexOf(object thisObj, object[] arguments)
+
+        private double LastIndexOf(object thisObj, object[] arguments)
         {
-            throw new NotImplementedException();
+            TypeConverter.CheckObjectCoercible(Engine, thisObj);
+
+            var s = TypeConverter.ToString(thisObj);
+            var searchStr = TypeConverter.ToString(arguments.At(0));
+            double numPos = arguments.At(0) == Undefined.Instance ? double.NaN : TypeConverter.ToNumber(arguments.At(0));
+            double pos = double.IsNaN(numPos) ? double.PositiveInfinity : TypeConverter.ToInteger(numPos);
+            var len = s.Length;
+            var start = System.Math.Min(len, System.Math.Max(pos, 0));
+            var searchLen = searchStr.Length;
+
+            return s.LastIndexOf(searchStr, len - (int) start, StringComparison.Ordinal);
         }
-        private object IndexOf(object thisObj, object[] arguments)
+
+        private double IndexOf(object thisObj, object[] arguments)
         {
-            throw new NotImplementedException();
+            TypeConverter.CheckObjectCoercible(Engine, thisObj);
+
+            var s = TypeConverter.ToString(thisObj);
+            var searchStr = TypeConverter.ToString(arguments.Length > 0 ? arguments[0] : Undefined.Instance);
+            double pos = 0;
+            if (arguments.Length > 1 && arguments[1] != Undefined.Instance)
+            {
+                pos = TypeConverter.ToInteger(arguments[1]);
+            }
+
+            if (pos >= s.Length)
+            {
+                return -1;
+            }
+
+            if (pos < 0)
+            {
+                pos = 0;
+            }
+
+            return s.IndexOf(searchStr, (int) pos, StringComparison.Ordinal);
         }
-        private object Concat(object thisObj, object[] arguments)
+
+        private string Concat(object thisObj, object[] arguments)
         {
-            throw new NotImplementedException();
+            TypeConverter.CheckObjectCoercible(Engine, thisObj);
+
+            var s = TypeConverter.ToString(thisObj);
+            var sb = new StringBuilder(s);
+            for (int i = 0; i < arguments.Length; i++)
+            {
+                sb.Append(TypeConverter.ToString(arguments[i]));
+            }
+
+            return sb.ToString();
         }
+
         private object CharCodeAt(object thisObj, object[] arguments)
         {
             TypeConverter.CheckObjectCoercible(Engine, thisObj);
@@ -126,9 +509,9 @@ namespace Jint.Native.String
         {
             TypeConverter.CheckObjectCoercible(Engine, thisObj);
             var s = TypeConverter.ToString(thisObj);
-            var position = TypeConverter.ToInteger(arguments.Length > 0 ? arguments[0] : Undefined.Instance);
+            var position = TypeConverter.ToInteger(arguments.At(0));
             var size = s.Length;
-            if (position > size || position < 0)
+            if (position >= size || position < 0)
             {
                 return "";
             }
@@ -136,14 +519,15 @@ namespace Jint.Native.String
 
         }
 
-        private static string ValueOf(StringInstance thisObj, object[] arguments)
+        private string ValueOf(object thisObj, object[] arguments)
         {
-            return thisObj.PrimitiveValue;
-        }
+            var s = thisObj as StringInstance;
+            if (s == null)
+            {
+                throw new JavaScriptException(Engine.TypeError);
+            }
 
-        private object ToStringString(object thisObj, object[] arguments)
-        {
-            throw new NotImplementedException();
+            return s.PrimitiveValue;
         }
     }
 }

+ 14 - 1
Jint/Runtime/Arguments.cs

@@ -1,4 +1,6 @@
-namespace Jint.Runtime
+using Jint.Native;
+
+namespace Jint.Runtime
 {
     public static class Arguments
     {
@@ -8,5 +10,16 @@
         {
             return o;
         }
+
+        /// <summary>
+        /// Returns the arguments at the provided position or Undefined if not present
+        /// </summary>
+        /// <param name="args"></param>
+        /// <param name="index"></param>
+        /// <returns></returns>
+        public static object At(this object[] args, int index)
+        {
+            return args.Length > index ? args[index] : Undefined.Instance;
+        }
     }
 }

+ 7 - 0
Jint/Runtime/StatementInterpreter.cs

@@ -480,6 +480,13 @@ namespace Jint.Runtime
 
         public Completion ExecuteDebuggerStatement(DebuggerStatement debuggerStatement)
         {
+            if (!System.Diagnostics.Debugger.IsAttached)
+            {
+                System.Diagnostics.Debugger.Launch();
+            }
+            
+            System.Diagnostics.Debugger.Break();
+
             return new Completion(Completion.Normal, null, null);
         }
     }

+ 6 - 6
Jint/Runtime/TypeConverter.cs

@@ -353,10 +353,10 @@ namespace Jint.Runtime
                 // n total of digits in fraction s*10^n-k=m
                 // 123.4 s=1234, k=4, n=3
                 // 1234000 s = 1234, k=4, n=7
-
-                var significants = GetSignificantDigitCount((decimal) m);
+                var d = (Decimal) m;
+                var significants = GetSignificantDigitCount(d);
                 var s = significants.Item1;
-                var k = (int)Math.Floor(Math.Log10(s)+1);
+                var k = (int)Math.Floor(Math.Log10((double) s)+1);
                 var n = k - significants.Item2;
                 if (m < 1 && m > -1)
                 {
@@ -502,7 +502,7 @@ namespace Jint.Runtime
             }
         }
 
-        public static Tuple<double, int> GetSignificantDigitCount(decimal value)
+        public static Tuple<decimal, int> GetSignificantDigitCount(decimal value)
         {
             /* So, the decimal type is basically represented as a fraction of two
              * integers: a numerator that can be anything, and a denominator that is 
@@ -535,7 +535,7 @@ namespace Jint.Runtime
 
                 var num = new decimal(lowPart, middlePart, highPart, false, 0);
 
-                return new Tuple<double, int>((double)num, (scalePart >> 16) & 0x7fff);
+                return new Tuple<decimal, int>(num, (scalePart >> 16) & 0x7fff);
             }
             else
             {
@@ -545,7 +545,7 @@ namespace Jint.Runtime
                 // http://msdn.microsoft.com/en-us/library/system.decimal.getbits.aspx
                 int exponent = (scalePart & 0x00FF0000) >> 16;
 
-                return new Tuple<double, int>(lowPart, exponent + 1);
+                return new Tuple<decimal, int>(lowPart, exponent + 1);
             }
         }
     }