Browse Source

Implemented unescape function on global object. (#385)

girishjjain 8 years ago
parent
commit
2883f2dea5
2 changed files with 284 additions and 30 deletions
  1. 212 0
      Jint.Tests/Runtime/EngineTests.cs
  2. 72 30
      Jint/Native/Global/GlobalObject.cs

+ 212 - 0
Jint.Tests/Runtime/EngineTests.cs

@@ -1921,5 +1921,217 @@ namespace Jint.Tests.Runtime
 
 
             Assert.Equal(expected, result);
             Assert.Equal(expected, result);
         }
         }
+
+        [Theory]
+        //https://github.com/tc39/test262/blob/master/test/annexB/built-ins/unescape/empty-string.js
+        [InlineData("", "unescape('')")]
+        //https://github.com/tc39/test262/blob/master/test/annexB/built-ins/unescape/four-ignore-bad-u.js
+        [InlineData("%U0000", "unescape('%U0000')")]
+        [InlineData("%t0000", "unescape('%t0000')")]
+        [InlineData("%v0000", "unescape('%v0000')")]
+        [InlineData("%" + "\x00" + "00", "unescape('%%0000')")]
+        //https://github.com/tc39/test262/blob/master/test/annexB/built-ins/unescape/four-ignore-end-str.js
+        [InlineData("%u", "unescape('%u')")]
+        [InlineData("%u0", "unescape('%u0')")]
+        [InlineData("%u1", "unescape('%u1')")]
+        [InlineData("%u2", "unescape('%u2')")]
+        [InlineData("%u3", "unescape('%u3')")]
+        [InlineData("%u4", "unescape('%u4')")]
+        [InlineData("%u5", "unescape('%u5')")]
+        [InlineData("%u6", "unescape('%u6')")]
+        [InlineData("%u7", "unescape('%u7')")]
+        [InlineData("%u8", "unescape('%u8')")]
+        [InlineData("%u9", "unescape('%u9')")]
+        [InlineData("%ua", "unescape('%ua')")]
+        [InlineData("%uA", "unescape('%uA')")]
+        [InlineData("%ub", "unescape('%ub')")]
+        [InlineData("%uB", "unescape('%uB')")]
+        [InlineData("%uc", "unescape('%uc')")]
+        [InlineData("%uC", "unescape('%uC')")]
+        [InlineData("%ud", "unescape('%ud')")]
+        [InlineData("%uD", "unescape('%uD')")]
+        [InlineData("%ue", "unescape('%ue')")]
+        [InlineData("%uE", "unescape('%uE')")]
+        [InlineData("%uf", "unescape('%uf')")]
+        [InlineData("%uF", "unescape('%uF')")]
+        [InlineData("%u00", "unescape('%u00')")]
+        [InlineData("%u01", "unescape('%u01')")]
+        [InlineData("%u02", "unescape('%u02')")]
+        [InlineData("%u03", "unescape('%u03')")]
+        [InlineData("%u04", "unescape('%u04')")]
+        [InlineData("%u05", "unescape('%u05')")]
+        [InlineData("%u06", "unescape('%u06')")]
+        [InlineData("%u07", "unescape('%u07')")]
+        [InlineData("%u08", "unescape('%u08')")]
+        [InlineData("%u09", "unescape('%u09')")]
+        [InlineData("%u0a", "unescape('%u0a')")]
+        [InlineData("%u0A", "unescape('%u0A')")]
+        [InlineData("%u0b", "unescape('%u0b')")]
+        [InlineData("%u0B", "unescape('%u0B')")]
+        [InlineData("%u0c", "unescape('%u0c')")]
+        [InlineData("%u0C", "unescape('%u0C')")]
+        [InlineData("%u0d", "unescape('%u0d')")]
+        [InlineData("%u0D", "unescape('%u0D')")]
+        [InlineData("%u0e", "unescape('%u0e')")]
+        [InlineData("%u0E", "unescape('%u0E')")]
+        [InlineData("%u0f", "unescape('%u0f')")]
+        [InlineData("%u0F", "unescape('%u0F')")]
+        [InlineData("%u000", "unescape('%u000')")]
+        [InlineData("%u001", "unescape('%u001')")]
+        [InlineData("%u002", "unescape('%u002')")]
+        [InlineData("%u003", "unescape('%u003')")]
+        [InlineData("%u004", "unescape('%u004')")]
+        [InlineData("%u005", "unescape('%u005')")]
+        [InlineData("%u006", "unescape('%u006')")]
+        [InlineData("%u007", "unescape('%u007')")]
+        [InlineData("%u008", "unescape('%u008')")]
+        [InlineData("%u009", "unescape('%u009')")]
+        [InlineData("%u00a", "unescape('%u00a')")]
+        [InlineData("%u00A", "unescape('%u00A')")]
+        [InlineData("%u00b", "unescape('%u00b')")]
+        [InlineData("%u00B", "unescape('%u00B')")]
+        [InlineData("%u00c", "unescape('%u00c')")]
+        [InlineData("%u00C", "unescape('%u00C')")]
+        [InlineData("%u00d", "unescape('%u00d')")]
+        [InlineData("%u00D", "unescape('%u00D')")]
+        [InlineData("%u00e", "unescape('%u00e')")]
+        [InlineData("%u00E", "unescape('%u00E')")]
+        [InlineData("%u00f", "unescape('%u00f')")]
+        [InlineData("%u00F", "unescape('%u00F')")]
+        //https://github.com/tc39/test262/blob/master/test/annexB/built-ins/unescape/four-ignore-non-hex.js
+        [InlineData("%u000%0", "unescape('%u000%0')")]
+        [InlineData("%u000g0", "unescape('%u000g0')")]
+        [InlineData("%u000G0", "unescape('%u000G0')")]
+        [InlineData("%u00g00", "unescape('%u00g00')")]
+        [InlineData("%u00G00", "unescape('%u00G00')")]
+        [InlineData("%u0g000", "unescape('%u0g000')")]
+        [InlineData("%u0G000", "unescape('%u0G000')")]
+        [InlineData("%ug0000", "unescape('%ug0000')")]
+        [InlineData("%uG0000", "unescape('%uG0000')")]
+        [InlineData("%u000u0", "unescape('%u000u0')")]
+        [InlineData("%u000U0", "unescape('%u000U0')")]
+        [InlineData("%u00u00", "unescape('%u00u00')")]
+        [InlineData("%u00U00", "unescape('%u00U00')")]
+        [InlineData("%u0u000", "unescape('%u0u000')")]
+        [InlineData("%u0U000", "unescape('%u0U000')")]
+        [InlineData("%uu0000", "unescape('%uu0000')")]
+        [InlineData("%uU0000", "unescape('%uU0000')")]
+        //https://github.com/tc39/test262/blob/master/test/annexB/built-ins/unescape/four.js
+        [InlineData("%0" + "\x00" + "0", "unescape('%0%u00000')")]
+        [InlineData("%0" + "\x01" + "0", "unescape('%0%u00010')")]
+        [InlineData("%0)0", "unescape('%0%u00290')")]
+        [InlineData("%0*0", "unescape('%0%u002a0')")]
+        [InlineData("%0*0", "unescape('%0%u002A0')")]
+        [InlineData("%0+0", "unescape('%0%u002b0')")]
+        [InlineData("%0+0", "unescape('%0%u002B0')")]
+        [InlineData("%0,0", "unescape('%0%u002c0')")]
+        [InlineData("%0,0", "unescape('%0%u002C0')")]
+        [InlineData("%0-0", "unescape('%0%u002d0')")]
+        [InlineData("%0-0", "unescape('%0%u002D0')")]
+        [InlineData("%090", "unescape('%0%u00390')")]
+        [InlineData("%0:0", "unescape('%0%u003a0')")]
+        [InlineData("%0:0", "unescape('%0%u003A0')")]
+        [InlineData("%0?0", "unescape('%0%u003f0')")]
+        [InlineData("%0?0", "unescape('%0%u003F0')")]
+        [InlineData("%0@0", "unescape('%0%u00400')")]
+        [InlineData("%0Z0", "unescape('%0%u005a0')")]
+        [InlineData("%0Z0", "unescape('%0%u005A0')")]
+        [InlineData("%0[0", "unescape('%0%u005b0')")]
+        [InlineData("%0[0", "unescape('%0%u005B0')")]
+        [InlineData("%0^0", "unescape('%0%u005e0')")]
+        [InlineData("%0^0", "unescape('%0%u005E0')")]
+        [InlineData("%0_0", "unescape('%0%u005f0')")]
+        [InlineData("%0_0", "unescape('%0%u005F0')")]
+        [InlineData("%0`0", "unescape('%0%u00600')")]
+        [InlineData("%0a0", "unescape('%0%u00610')")]
+        [InlineData("%0z0", "unescape('%0%u007a0')")]
+        [InlineData("%0z0", "unescape('%0%u007A0')")]
+        [InlineData("%0{0", "unescape('%0%u007b0')")]
+        [InlineData("%0{0", "unescape('%0%u007B0')")]
+        [InlineData("%0" + "\ufffe" + "0", "unescape('%0%ufffe0')")]
+        [InlineData("%0" + "\ufffe" + "0", "unescape('%0%uFffe0')")]
+        [InlineData("%0" + "\ufffe" + "0", "unescape('%0%ufFfe0')")]
+        [InlineData("%0" + "\ufffe" + "0", "unescape('%0%uffFe0')")]
+        [InlineData("%0" + "\ufffe" + "0", "unescape('%0%ufffE0')")]
+        [InlineData("%0" + "\ufffe" + "0", "unescape('%0%uFFFE0')")]
+        [InlineData("%0" + "\uffff" + "0", "unescape('%0%uffff0')")]
+        [InlineData("%0" + "\uffff" + "0", "unescape('%0%uFfff0')")]
+        [InlineData("%0" + "\uffff" + "0", "unescape('%0%ufFff0')")]
+        [InlineData("%0" + "\uffff" + "0", "unescape('%0%uffFf0')")]
+        [InlineData("%0" + "\uffff" + "0", "unescape('%0%ufffF0')")]
+        [InlineData("%0" + "\uffff" + "0", "unescape('%0%uFFFF0')")]
+        //https://github.com/tc39/test262/blob/master/test/annexB/built-ins/unescape/two-ignore-end-str.js
+        [InlineData("%", "unescape('%')")]
+        [InlineData("%0", "unescape('%0')")]
+        [InlineData("%1", "unescape('%1')")]
+        [InlineData("%2", "unescape('%2')")]
+        [InlineData("%3", "unescape('%3')")]
+        [InlineData("%4", "unescape('%4')")]
+        [InlineData("%5", "unescape('%5')")]
+        [InlineData("%6", "unescape('%6')")]
+        [InlineData("%7", "unescape('%7')")]
+        [InlineData("%8", "unescape('%8')")]
+        [InlineData("%9", "unescape('%9')")]
+        [InlineData("%a", "unescape('%a')")]
+        [InlineData("%A", "unescape('%A')")]
+        [InlineData("%b", "unescape('%b')")]
+        [InlineData("%B", "unescape('%B')")]
+        [InlineData("%c", "unescape('%c')")]
+        [InlineData("%C", "unescape('%C')")]
+        [InlineData("%d", "unescape('%d')")]
+        [InlineData("%D", "unescape('%D')")]
+        [InlineData("%e", "unescape('%e')")]
+        [InlineData("%E", "unescape('%E')")]
+        [InlineData("%f", "unescape('%f')")]
+        [InlineData("%F", "unescape('%F')")]
+        //https://github.com/tc39/test262/blob/master/test/annexB/built-ins/unescape/two-ignore-non-hex.js
+        [InlineData("%0%0", "unescape('%0%0')")]
+        [InlineData("%0g0", "unescape('%0g0')")]
+        [InlineData("%0G0", "unescape('%0G0')")]
+        [InlineData("%g00", "unescape('%g00')")]
+        [InlineData("%G00", "unescape('%G00')")]
+        [InlineData("%0u0", "unescape('%0u0')")]
+        [InlineData("%0U0", "unescape('%0U0')")]
+        [InlineData("%u00", "unescape('%u00')")]
+        [InlineData("%U00", "unescape('%U00')")]
+        //https://github.com/tc39/test262/blob/master/test/annexB/built-ins/unescape/two.js
+        [InlineData("%0" + "\x00" + "00", "unescape('%0%0000')")]
+        [InlineData("%0" + "\x01" + "00", "unescape('%0%0100')")]
+        [InlineData("%0)00", "unescape('%0%2900')")]
+        [InlineData("%0*00", "unescape('%0%2a00')")]
+        [InlineData("%0*00", "unescape('%0%2A00')")]
+        [InlineData("%0+00", "unescape('%0%2b00')")]
+        [InlineData("%0+00", "unescape('%0%2B00')")]
+        [InlineData("%0,00", "unescape('%0%2c00')")]
+        [InlineData("%0,00", "unescape('%0%2C00')")]
+        [InlineData("%0-00", "unescape('%0%2d00')")]
+        [InlineData("%0-00", "unescape('%0%2D00')")]
+        [InlineData("%0900", "unescape('%0%3900')")]
+        [InlineData("%0:00", "unescape('%0%3a00')")]
+        [InlineData("%0:00", "unescape('%0%3A00')")]
+        [InlineData("%0?00", "unescape('%0%3f00')")]
+        [InlineData("%0?00", "unescape('%0%3F00')")]
+        [InlineData("%0@00", "unescape('%0%4000')")]
+        [InlineData("%0Z00", "unescape('%0%5a00')")]
+        [InlineData("%0Z00", "unescape('%0%5A00')")]
+        [InlineData("%0[00", "unescape('%0%5b00')")]
+        [InlineData("%0[00", "unescape('%0%5B00')")]
+        [InlineData("%0^00", "unescape('%0%5e00')")]
+        [InlineData("%0^00", "unescape('%0%5E00')")]
+        [InlineData("%0_00", "unescape('%0%5f00')")]
+        [InlineData("%0_00", "unescape('%0%5F00')")]
+        [InlineData("%0`00", "unescape('%0%6000')")]
+        [InlineData("%0a00", "unescape('%0%6100')")]
+        [InlineData("%0z00", "unescape('%0%7a00')")]
+        [InlineData("%0z00", "unescape('%0%7A00')")]
+        [InlineData("%0{00", "unescape('%0%7b00')")]
+        [InlineData("%0{00", "unescape('%0%7B00')")]
+        public void ShouldEvaluateUnescape(object expected, string source)
+        {
+            var engine = new Engine();
+            var result = engine.Execute(source).GetCompletionValue().ToObject();
+
+            Assert.Equal(expected, result);
+        }
     }
     }
 }
 }

+ 72 - 30
Jint/Native/Global/GlobalObject.cs

@@ -1,4 +1,5 @@
 using System;
 using System;
+using System.Globalization;
 using System.Linq;
 using System.Linq;
 using System.Text;
 using System.Text;
 using Jint.Native.Object;
 using Jint.Native.Object;
@@ -16,7 +17,7 @@ namespace Jint.Native.Global
 
 
         public static GlobalObject CreateGlobalObject(Engine engine)
         public static GlobalObject CreateGlobalObject(Engine engine)
         {
         {
-            var global = new GlobalObject(engine) {Prototype = null, Extensible = true};
+            var global = new GlobalObject(engine) { Prototype = null, Extensible = true };
 
 
             return global;
             return global;
         }
         }
@@ -60,6 +61,7 @@ namespace Jint.Native.Global
             FastAddProperty("encodeURI", new ClrFunctionInstance(Engine, EncodeUri, 1), true, false, true);
             FastAddProperty("encodeURI", new ClrFunctionInstance(Engine, EncodeUri, 1), true, false, true);
             FastAddProperty("encodeURIComponent", new ClrFunctionInstance(Engine, EncodeUriComponent, 1), true, false, true);
             FastAddProperty("encodeURIComponent", new ClrFunctionInstance(Engine, EncodeUriComponent, 1), true, false, true);
             FastAddProperty("escape", new ClrFunctionInstance(Engine, Escape, 1), true, false, true);
             FastAddProperty("escape", new ClrFunctionInstance(Engine, Escape, 1), true, false, true);
+            FastAddProperty("unescape", new ClrFunctionInstance(Engine, Unescape, 1), true, false, true);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -77,7 +79,7 @@ namespace Jint.Native.Global
                 {
                 {
                     sign = -1;
                     sign = -1;
                 }
                 }
-                
+
                 if (s[0] == '-' || s[0] == '+')
                 if (s[0] == '-' || s[0] == '+')
                 {
                 {
                     s = s.Substring(1);
                     s = s.Substring(1);
@@ -103,7 +105,7 @@ namespace Jint.Native.Global
             {
             {
                 return double.NaN;
                 return double.NaN;
             }
             }
-            else if(radix != 16)
+            else if (radix != 16)
             {
             {
                 stripPrefix = false;
                 stripPrefix = false;
             }
             }
@@ -133,7 +135,7 @@ namespace Jint.Native.Global
 
 
             double result = 0;
             double result = 0;
             double pow = 1;
             double pow = 1;
-            for (int i = number.Length - 1; i >= 0 ; i--)
+            for (int i = number.Length - 1; i >= 0; i--)
             {
             {
                 double index = double.NaN;
                 double index = double.NaN;
                 char digit = number[i];
                 char digit = number[i];
@@ -156,7 +158,7 @@ namespace Jint.Native.Global
                     return Parse(number.Substring(0, i), radix);
                     return Parse(number.Substring(0, i), radix);
                 }
                 }
 
 
-                result += index*pow;
+                result += index * pow;
                 pow = pow * radix;
                 pow = pow * radix;
             }
             }
 
 
@@ -172,7 +174,7 @@ namespace Jint.Native.Global
             var trimmedString = StringPrototype.TrimStartEx(inputString);
             var trimmedString = StringPrototype.TrimStartEx(inputString);
 
 
             var sign = 1;
             var sign = 1;
-            if (trimmedString.Length > 0 )
+            if (trimmedString.Length > 0)
             {
             {
                 if (trimmedString[0] == '-')
                 if (trimmedString[0] == '-')
                 {
                 {
@@ -187,7 +189,7 @@ namespace Jint.Native.Global
 
 
             if (trimmedString.StartsWith("Infinity"))
             if (trimmedString.StartsWith("Infinity"))
             {
             {
-                return sign*double.PositiveInfinity;
+                return sign * double.PositiveInfinity;
             }
             }
 
 
             if (trimmedString.StartsWith("NaN"))
             if (trimmedString.StartsWith("NaN"))
@@ -195,7 +197,7 @@ namespace Jint.Native.Global
                 return double.NaN;
                 return double.NaN;
             }
             }
 
 
-            var separator = (char) 0;
+            var separator = (char)0;
 
 
             bool isNan = true;
             bool isNan = true;
             decimal number = 0;
             decimal number = 0;
@@ -209,8 +211,8 @@ namespace Jint.Native.Global
                     separator = '.';
                     separator = '.';
                     break;
                     break;
                 }
                 }
-                
-                if(c == 'e' || c == 'E')
+
+                if (c == 'e' || c == 'E')
                 {
                 {
                     i++;
                     i++;
                     separator = 'e';
                     separator = 'e';
@@ -310,8 +312,8 @@ namespace Jint.Native.Global
                     number /= 10;
                     number /= 10;
                 }
                 }
             }
             }
-            
-            return (double) (sign * number);
+
+            return (double)(sign * number);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -342,7 +344,7 @@ namespace Jint.Native.Global
             return true;
             return true;
         }
         }
 
 
-        private static readonly char[] UriReserved = {';', '/', '?', ':', '@', '&', '=', '+', '$', ','};
+        private static readonly char[] UriReserved = { ';', '/', '?', ':', '@', '&', '=', '+', '$', ',' };
 
 
         private static readonly char[] UriUnescaped =
         private static readonly char[] UriUnescaped =
         {
         {
@@ -370,7 +372,7 @@ namespace Jint.Native.Global
         public JsValue EncodeUri(JsValue thisObject, JsValue[] arguments)
         public JsValue EncodeUri(JsValue thisObject, JsValue[] arguments)
         {
         {
             var uriString = TypeConverter.ToString(arguments.At(0));
             var uriString = TypeConverter.ToString(arguments.At(0));
-            var unescapedUriSet = UriReserved.Concat(UriUnescaped).Concat(new [] {'#'}).ToArray();
+            var unescapedUriSet = UriReserved.Concat(UriUnescaped).Concat(new[] { '#' }).ToArray();
 
 
             return Encode(uriString, unescapedUriSet);
             return Encode(uriString, unescapedUriSet);
         }
         }
@@ -393,7 +395,7 @@ namespace Jint.Native.Global
         {
         {
             var strLen = uriString.Length;
             var strLen = uriString.Length;
             var r = new StringBuilder(uriString.Length);
             var r = new StringBuilder(uriString.Length);
-            for (var k = 0; k< strLen; k++)
+            for (var k = 0; k < strLen; k++)
             {
             {
                 var c = uriString[k];
                 var c = uriString[k];
                 if (System.Array.IndexOf(unescapedUriSet, c) != -1)
                 if (System.Array.IndexOf(unescapedUriSet, c) != -1)
@@ -420,7 +422,7 @@ namespace Jint.Native.Global
                             throw new JavaScriptException(Engine.UriError);
                             throw new JavaScriptException(Engine.UriError);
                         }
                         }
 
 
-                        var kChar = (int) uriString[k];
+                        var kChar = (int)uriString[k];
                         if (kChar < 0xDC00 || kChar > 0xDFFF)
                         if (kChar < 0xDC00 || kChar > 0xDFFF)
                         {
                         {
                             throw new JavaScriptException(Engine.UriError);
                             throw new JavaScriptException(Engine.UriError);
@@ -441,7 +443,7 @@ namespace Jint.Native.Global
                         // 00000yyy yyzzzzzz ->	110yyyyy ; 10zzzzzz
                         // 00000yyy yyzzzzzz ->	110yyyyy ; 10zzzzzz
                         octets = new[]
                         octets = new[]
                         {
                         {
-                            (byte)(0xC0 | (v >> 6)), 
+                            (byte)(0xC0 | (v >> 6)),
                             (byte)(0x80 | (v & 0x3F))
                             (byte)(0x80 | (v & 0x3F))
                         };
                         };
                     }
                     }
@@ -450,8 +452,8 @@ namespace Jint.Native.Global
                         // xxxxyyyy yyzzzzzz -> 1110xxxx; 10yyyyyy; 10zzzzzz	
                         // xxxxyyyy yyzzzzzz -> 1110xxxx; 10yyyyyy; 10zzzzzz	
                         octets = new[]
                         octets = new[]
                         {
                         {
-                            (byte)(0xE0 | (v >> 12)), 
-                            (byte)(0x80 | ((v >> 6) & 0x3F)), 
+                            (byte)(0xE0 | (v >> 12)),
+                            (byte)(0x80 | ((v >> 6) & 0x3F)),
                             (byte)(0x80 | (v & 0x3F))
                             (byte)(0x80 | (v & 0x3F))
                         };
                         };
                     }
                     }
@@ -529,7 +531,7 @@ namespace Jint.Native.Global
 
 
                     if (!IsValidHexaChar(uriString[k + 1]) || !IsValidHexaChar(uriString[k + 2]))
                     if (!IsValidHexaChar(uriString[k + 1]) || !IsValidHexaChar(uriString[k + 2]))
                     {
                     {
-                        throw new JavaScriptException(Engine.UriError);    
+                        throw new JavaScriptException(Engine.UriError);
                     }
                     }
 
 
                     var B = Convert.ToByte(uriString[k + 1].ToString() + uriString[k + 2], 16);
                     var B = Convert.ToByte(uriString[k + 1].ToString() + uriString[k + 2], 16);
@@ -537,7 +539,7 @@ namespace Jint.Native.Global
                     k += 2;
                     k += 2;
                     if ((B & 0x80) == 0)
                     if ((B & 0x80) == 0)
                     {
                     {
-                        C = (char) B;
+                        C = (char)B;
                         if (System.Array.IndexOf(reservedSet, C) == -1)
                         if (System.Array.IndexOf(reservedSet, C) == -1)
                         {
                         {
                             R.Append(C);
                             R.Append(C);
@@ -556,16 +558,16 @@ namespace Jint.Native.Global
                         {
                         {
                             throw new JavaScriptException(Engine.UriError);
                             throw new JavaScriptException(Engine.UriError);
                         }
                         }
-                        
+
                         var Octets = new byte[n];
                         var Octets = new byte[n];
                         Octets[0] = B;
                         Octets[0] = B;
-                        
-                        if (k + (3*(n - 1)) >= strLen)
+
+                        if (k + (3 * (n - 1)) >= strLen)
                         {
                         {
                             throw new JavaScriptException(Engine.UriError);
                             throw new JavaScriptException(Engine.UriError);
                         }
                         }
-                        
-                        for(var j=1; j <n; j++)
+
+                        for (var j = 1; j < n; j++)
                         {
                         {
                             k++;
                             k++;
                             if (uriString[k] != '%')
                             if (uriString[k] != '%')
@@ -595,12 +597,12 @@ namespace Jint.Native.Global
                     }
                     }
                 }
                 }
             }
             }
-            
+
             return R.ToString();
             return R.ToString();
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// http://www.ecma-international.org/ecma-262/5.1/#sec-B.2.2
+        /// http://www.ecma-international.org/ecma-262/5.1/#sec-B.2.1
         /// </summary>
         /// </summary>
         public JsValue Escape(JsValue thisObject, JsValue[] arguments)
         public JsValue Escape(JsValue thisObject, JsValue[] arguments)
         {
         {
@@ -627,6 +629,46 @@ namespace Jint.Native.Global
             }
             }
 
 
             return r.ToString();
             return r.ToString();
-        }		
+        }
+
+        /// <summary>
+        /// http://www.ecma-international.org/ecma-262/5.1/#sec-B.2.2
+        /// </summary>
+        public JsValue Unescape(JsValue thisObject, JsValue[] arguments)
+        {
+            var uriString = TypeConverter.ToString(arguments.At(0));
+
+            var strLen = uriString.Length;
+            var r = new StringBuilder(strLen);
+            for (var k = 0; k < strLen; k++)
+            {
+                var c = uriString[k];
+                if (c == '%')
+                {
+                    if (k < strLen - 6
+                        && uriString[k + 1] == 'u'
+                        && uriString.Skip(k + 2).Take(4).All(IsValidHexaChar))
+                    {
+                        c = (char)int.Parse(
+                            string.Join(string.Empty, uriString.Skip(k + 2).Take(4)),
+                            NumberStyles.AllowHexSpecifier);
+
+                        k += 5;
+                    }
+                    else if (k < strLen - 3
+                        && uriString.Skip(k + 1).Take(2).All(IsValidHexaChar))
+                    {
+                        c = (char)int.Parse(
+                            string.Join(string.Empty, uriString.Skip(k + 1).Take(2)),
+                            NumberStyles.AllowHexSpecifier);
+
+                        k += 2;
+                    }
+                }
+                r.Append(c);
+            }
+
+            return r.ToString();
+        }
     }
     }
-}
+}