Harden a few builtins
authorjkummerow@chromium.org <jkummerow@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 22 May 2014 13:59:37 +0000 (13:59 +0000)
committerjkummerow@chromium.org <jkummerow@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 22 May 2014 13:59:37 +0000 (13:59 +0000)
Introducing BUILTIN_ASSERT, builtins' equivalent of RUNTIME_ASSERT.

R=rossberg@chromium.org

Review URL: https://codereview.chromium.org/292173011

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@21439 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/debug-debugger.js
src/isolate.cc
src/objects.cc
src/promise.js
src/uri.js
tools/generate-runtime-tests.py

index ebd7dbd..660ea79 100644 (file)
@@ -2485,17 +2485,6 @@ DebugCommandProcessor.prototype.systemBreak = function(cmd, args) {
 };
 
 
-function NumberToHex8Str(n) {
-  var r = "";
-  for (var i = 0; i < 8; ++i) {
-    var c = hexCharArray[n & 0x0F];  // hexCharArray is defined in uri.js
-    r = c + r;
-    n = n >>> 4;
-  }
-  return r;
-}
-
-
 /**
  * Convert an Object to its debugger protocol representation. The representation
  * may be serilized to a JSON object using JSON.stringify().
index 7f7670e..4ed0cc0 100644 (file)
@@ -1133,6 +1133,24 @@ void Isolate::DoThrow(Object* exception, MessageLocation* location) {
       } else {
         OS::PrintError("Extension or internal compilation error.\n");
       }
+#ifdef OBJECT_PRINT
+      // Since comments and empty lines have been stripped from the source of
+      // builtins, print the actual source here so that line numbers match.
+      if (location->script()->source()->IsString()) {
+        Handle<String> src(String::cast(location->script()->source()));
+        PrintF("Failing script:\n");
+        int len = src->length();
+        int line_number = 1;
+        PrintF("%5d: ", line_number);
+        for (int i = 0; i < len; i++) {
+          uint16_t character = src->Get(i);
+          PrintF("%c", character);
+          if (character == '\n' && i < len - 2) {
+            PrintF("%5d: ", ++line_number);
+          }
+        }
+      }
+#endif
     }
   }
 
index ddeaacd..c301670 100644 (file)
@@ -14263,7 +14263,7 @@ int JSObject::GetLocalElementKeys(FixedArray* storage,
     case FAST_HOLEY_DOUBLE_ELEMENTS: {
       int length = IsJSArray() ?
           Smi::cast(JSArray::cast(this)->length())->value() :
-          FixedDoubleArray::cast(elements())->length();
+          FixedArrayBase::cast(elements())->length();
       for (int i = 0; i < length; i++) {
         if (!FixedDoubleArray::cast(elements())->is_the_hole(i)) {
           if (storage != NULL) {
index d202f28..aa9da2e 100644 (file)
@@ -9,28 +9,18 @@
 // var $Object = global.Object
 // var $WeakMap = global.WeakMap
 
+// For bootstrapper.
 
-var $Promise = function Promise(resolver) {
-  if (resolver === promiseRaw) return;
-  if (!%_IsConstructCall()) throw MakeTypeError('not_a_promise', [this]);
-  if (!IS_SPEC_FUNCTION(resolver))
-    throw MakeTypeError('resolver_not_a_function', [resolver]);
-  var promise = PromiseInit(this);
-  try {
-    %DebugPromiseHandlePrologue(function() { return promise });
-    resolver(function(x) { PromiseResolve(promise, x) },
-             function(r) { PromiseReject(promise, r) });
-  } catch (e) {
-    PromiseReject(promise, e);
-  } finally {
-    %DebugPromiseHandleEpilogue();
-  }
-}
-
-
-//-------------------------------------------------------------------
+var IsPromise;
+var PromiseCreate;
+var PromiseResolve;
+var PromiseReject;
+var PromiseChain;
+var PromiseCatch;
 
-// Core functionality.
+// mirror-debugger.js currently uses builtins.promiseStatus. It would be nice
+// if we could move these property names into the closure below.
+// TODO(jkummerow/rossberg/yangguo): Find a better solution.
 
 // Status values: 0 = pending, +1 = resolved, -1 = rejected
 var promiseStatus = GLOBAL_PRIVATE("Promise#status");
@@ -39,250 +29,274 @@ var promiseOnResolve = GLOBAL_PRIVATE("Promise#onResolve");
 var promiseOnReject = GLOBAL_PRIVATE("Promise#onReject");
 var promiseRaw = GLOBAL_PRIVATE("Promise#raw");
 
-function IsPromise(x) {
-  return IS_SPEC_OBJECT(x) && %HasLocalProperty(x, promiseStatus);
-}
-
-function PromiseSet(promise, status, value, onResolve, onReject) {
-  SET_PRIVATE(promise, promiseStatus, status);
-  SET_PRIVATE(promise, promiseValue, value);
-  SET_PRIVATE(promise, promiseOnResolve, onResolve);
-  SET_PRIVATE(promise, promiseOnReject, onReject);
-  return promise;
-}
-
-function PromiseInit(promise) {
-  return PromiseSet(promise, 0, UNDEFINED, new InternalArray, new InternalArray)
-}
-
-function PromiseDone(promise, status, value, promiseQueue) {
-  if (GET_PRIVATE(promise, promiseStatus) === 0) {
-    PromiseEnqueue(value, GET_PRIVATE(promise, promiseQueue));
-    PromiseSet(promise, status, value);
+(function() {
+
+  var $Promise = function Promise(resolver) {
+    if (resolver === promiseRaw) return;
+    if (!%_IsConstructCall()) throw MakeTypeError('not_a_promise', [this]);
+    if (!IS_SPEC_FUNCTION(resolver))
+      throw MakeTypeError('resolver_not_a_function', [resolver]);
+    var promise = PromiseInit(this);
+    try {
+      %DebugPromiseHandlePrologue(function() { return promise });
+      resolver(function(x) { PromiseResolve(promise, x) },
+               function(r) { PromiseReject(promise, r) });
+    } catch (e) {
+      PromiseReject(promise, e);
+    } finally {
+      %DebugPromiseHandleEpilogue();
+    }
   }
-}
 
-function PromiseResolve(promise, x) {
-  PromiseDone(promise, +1, x, promiseOnResolve)
-}
+  // Core functionality.
 
-function PromiseReject(promise, r) {
-  PromiseDone(promise, -1, r, promiseOnReject)
-}
+  function PromiseSet(promise, status, value, onResolve, onReject) {
+    SET_PRIVATE(promise, promiseStatus, status);
+    SET_PRIVATE(promise, promiseValue, value);
+    SET_PRIVATE(promise, promiseOnResolve, onResolve);
+    SET_PRIVATE(promise, promiseOnReject, onReject);
+    return promise;
+  }
 
+  function PromiseInit(promise) {
+    return PromiseSet(promise, 0, UNDEFINED, new InternalArray,
+                      new InternalArray)
+  }
 
-// For API.
+  function PromiseDone(promise, status, value, promiseQueue) {
+    if (GET_PRIVATE(promise, promiseStatus) === 0) {
+      PromiseEnqueue(value, GET_PRIVATE(promise, promiseQueue));
+      PromiseSet(promise, status, value);
+    }
+  }
 
-function PromiseNopResolver() {}
+  function PromiseCoerce(constructor, x) {
+    if (!IsPromise(x) && IS_SPEC_OBJECT(x)) {
+      var then;
+      try {
+        then = x.then;
+      } catch(r) {
+        return %_CallFunction(constructor, r, PromiseRejected);
+      }
+      if (IS_SPEC_FUNCTION(then)) {
+        var deferred = %_CallFunction(constructor, PromiseDeferred);
+        try {
+          %_CallFunction(x, deferred.resolve, deferred.reject, then);
+        } catch(r) {
+          deferred.reject(r);
+        }
+        return deferred.promise;
+      }
+    }
+    return x;
+  }
 
-function PromiseCreate() {
-  return new $Promise(PromiseNopResolver)
-}
+  function PromiseHandle(value, handler, deferred) {
+    try {
+      %DebugPromiseHandlePrologue(
+          function() {
+            var queue = GET_PRIVATE(deferred.promise, promiseOnReject);
+            return (queue && queue.length == 0) ? deferred.promise : UNDEFINED;
+          });
+      var result = handler(value);
+      if (result === deferred.promise)
+        throw MakeTypeError('promise_cyclic', [result]);
+      else if (IsPromise(result))
+        %_CallFunction(result, deferred.resolve, deferred.reject, PromiseChain);
+      else
+        deferred.resolve(result);
+    } catch (exception) {
+      try {
+        %DebugPromiseHandlePrologue(function() { return deferred.promise });
+        deferred.reject(exception);
+      } catch (e) { } finally {
+        %DebugPromiseHandleEpilogue();
+      }
+    } finally {
+      %DebugPromiseHandleEpilogue();
+    }
+  }
+
+  function PromiseEnqueue(value, tasks) {
+    %EnqueueMicrotask(function() {
+      for (var i = 0; i < tasks.length; i += 2) {
+        PromiseHandle(value, tasks[i], tasks[i + 1])
+      }
+    });
+  }
 
+  function PromiseIdResolveHandler(x) { return x }
+  function PromiseIdRejectHandler(r) { throw r }
 
-// Convenience.
+  function PromiseNopResolver() {}
 
-function PromiseDeferred() {
-  if (this === $Promise) {
-    // Optimized case, avoid extra closure.
-    var promise = PromiseInit(new $Promise(promiseRaw));
-    return {
-      promise: promise,
-      resolve: function(x) { PromiseResolve(promise, x) },
-      reject: function(r) { PromiseReject(promise, r) }
-    };
-  } else {
-    var result = {};
-    result.promise = new this(function(resolve, reject) {
-      result.resolve = resolve;
-      result.reject = reject;
-    })
-    return result;
-  }
-}
-
-function PromiseResolved(x) {
-  if (this === $Promise) {
-    // Optimized case, avoid extra closure.
-    return PromiseSet(new $Promise(promiseRaw), +1, x);
-  } else {
-    return new this(function(resolve, reject) { resolve(x) });
+  // -------------------------------------------------------------------
+  // Define exported functions.
+
+  // For bootstrapper.
+
+  IsPromise = function IsPromise(x) {
+    return IS_SPEC_OBJECT(x) && %HasLocalProperty(x, promiseStatus);
   }
-}
-
-function PromiseRejected(r) {
-  if (this === $Promise) {
-    // Optimized case, avoid extra closure.
-    return PromiseSet(new $Promise(promiseRaw), -1, r);
-  } else {
-    return new this(function(resolve, reject) { reject(r) });
+
+  PromiseCreate = function PromiseCreate() {
+    return new $Promise(PromiseNopResolver)
   }
-}
-
-
-// Simple chaining.
-
-function PromiseIdResolveHandler(x) { return x }
-function PromiseIdRejectHandler(r) { throw r }
-
-function PromiseChain(onResolve, onReject) {  // a.k.a.  flatMap
-  onResolve = IS_UNDEFINED(onResolve) ? PromiseIdResolveHandler : onResolve;
-  onReject = IS_UNDEFINED(onReject) ? PromiseIdRejectHandler : onReject;
-  var deferred = %_CallFunction(this.constructor, PromiseDeferred);
-  switch (GET_PRIVATE(this, promiseStatus)) {
-    case UNDEFINED:
-      throw MakeTypeError('not_a_promise', [this]);
-    case 0:  // Pending
-      GET_PRIVATE(this, promiseOnResolve).push(onResolve, deferred);
-      GET_PRIVATE(this, promiseOnReject).push(onReject, deferred);
-      break;
-    case +1:  // Resolved
-      PromiseEnqueue(GET_PRIVATE(this, promiseValue), [onResolve, deferred]);
-      break;
-    case -1:  // Rejected
-      PromiseEnqueue(GET_PRIVATE(this, promiseValue), [onReject, deferred]);
-      break;
+
+  PromiseResolve = function PromiseResolve(promise, x) {
+    PromiseDone(promise, +1, x, promiseOnResolve)
   }
-  return deferred.promise;
-}
 
-function PromiseCatch(onReject) {
-  return this.then(UNDEFINED, onReject);
-}
+  PromiseReject = function PromiseReject(promise, r) {
+    PromiseDone(promise, -1, r, promiseOnReject)
+  }
 
-function PromiseEnqueue(value, tasks) {
-  %EnqueueMicrotask(function() {
-    for (var i = 0; i < tasks.length; i += 2) {
-      PromiseHandle(value, tasks[i], tasks[i + 1])
+  // Convenience.
+
+  function PromiseDeferred() {
+    if (this === $Promise) {
+      // Optimized case, avoid extra closure.
+      var promise = PromiseInit(new $Promise(promiseRaw));
+      return {
+        promise: promise,
+        resolve: function(x) { PromiseResolve(promise, x) },
+        reject: function(r) { PromiseReject(promise, r) }
+      };
+    } else {
+      var result = {};
+      result.promise = new this(function(resolve, reject) {
+        result.resolve = resolve;
+        result.reject = reject;
+      })
+      return result;
     }
-  });
-}
-
-function PromiseHandle(value, handler, deferred) {
-  try {
-    %DebugPromiseHandlePrologue(
-        function() {
-          var queue = GET_PRIVATE(deferred.promise, promiseOnReject);
-          return (queue && queue.length == 0) ? deferred.promise : UNDEFINED;
-        });
-    var result = handler(value);
-    if (result === deferred.promise)
-      throw MakeTypeError('promise_cyclic', [result]);
-    else if (IsPromise(result))
-      %_CallFunction(result, deferred.resolve, deferred.reject, PromiseChain);
-    else
-      deferred.resolve(result);
-  } catch (exception) {
-    try {
-      %DebugPromiseHandlePrologue(function() { return deferred.promise });
-      deferred.reject(exception);
-    } catch (e) { } finally {
-      %DebugPromiseHandleEpilogue();
+  }
+
+  function PromiseResolved(x) {
+    if (this === $Promise) {
+      // Optimized case, avoid extra closure.
+      return PromiseSet(new $Promise(promiseRaw), +1, x);
+    } else {
+      return new this(function(resolve, reject) { resolve(x) });
     }
-  } finally {
-    %DebugPromiseHandleEpilogue();
   }
-}
-
-
-// Multi-unwrapped chaining with thenable coercion.
-
-function PromiseThen(onResolve, onReject) {
-  onResolve = IS_SPEC_FUNCTION(onResolve) ? onResolve : PromiseIdResolveHandler;
-  onReject = IS_SPEC_FUNCTION(onReject) ? onReject : PromiseIdRejectHandler;
-  var that = this;
-  var constructor = this.constructor;
-  return %_CallFunction(
-    this,
-    function(x) {
-      x = PromiseCoerce(constructor, x);
-      return x === that ? onReject(MakeTypeError('promise_cyclic', [x])) :
-             IsPromise(x) ? x.then(onResolve, onReject) : onResolve(x);
-    },
-    onReject,
-    PromiseChain
-  );
-}
-
-function PromiseCoerce(constructor, x) {
-  if (!IsPromise(x) && IS_SPEC_OBJECT(x)) {
-    var then;
-    try {
-      then = x.then;
-    } catch(r) {
-      return %_CallFunction(constructor, r, PromiseRejected);
+
+  function PromiseRejected(r) {
+    if (this === $Promise) {
+      // Optimized case, avoid extra closure.
+      return PromiseSet(new $Promise(promiseRaw), -1, r);
+    } else {
+      return new this(function(resolve, reject) { reject(r) });
     }
-    if (IS_SPEC_FUNCTION(then)) {
-      var deferred = %_CallFunction(constructor, PromiseDeferred);
-      try {
-        %_CallFunction(x, deferred.resolve, deferred.reject, then);
-      } catch(r) {
-        deferred.reject(r);
-      }
-      return deferred.promise;
+  }
+
+  // Simple chaining.
+
+  PromiseChain = function PromiseChain(onResolve, onReject) {  // a.k.a.
+                                                                // flatMap
+    onResolve = IS_UNDEFINED(onResolve) ? PromiseIdResolveHandler : onResolve;
+    onReject = IS_UNDEFINED(onReject) ? PromiseIdRejectHandler : onReject;
+    var deferred = %_CallFunction(this.constructor, PromiseDeferred);
+    switch (GET_PRIVATE(this, promiseStatus)) {
+      case UNDEFINED:
+        throw MakeTypeError('not_a_promise', [this]);
+      case 0:  // Pending
+        GET_PRIVATE(this, promiseOnResolve).push(onResolve, deferred);
+        GET_PRIVATE(this, promiseOnReject).push(onReject, deferred);
+        break;
+      case +1:  // Resolved
+        PromiseEnqueue(GET_PRIVATE(this, promiseValue), [onResolve, deferred]);
+        break;
+      case -1:  // Rejected
+        PromiseEnqueue(GET_PRIVATE(this, promiseValue), [onReject, deferred]);
+        break;
     }
+    return deferred.promise;
   }
-  return x;
-}
 
+  PromiseCatch = function PromiseCatch(onReject) {
+    return this.then(UNDEFINED, onReject);
+  }
 
-// Combinators.
+  // Multi-unwrapped chaining with thenable coercion.
+
+  function PromiseThen(onResolve, onReject) {
+    onResolve = IS_SPEC_FUNCTION(onResolve) ? onResolve
+                                            : PromiseIdResolveHandler;
+    onReject = IS_SPEC_FUNCTION(onReject) ? onReject
+                                          : PromiseIdRejectHandler;
+    var that = this;
+    var constructor = this.constructor;
+    return %_CallFunction(
+      this,
+      function(x) {
+        x = PromiseCoerce(constructor, x);
+        return x === that ? onReject(MakeTypeError('promise_cyclic', [x])) :
+               IsPromise(x) ? x.then(onResolve, onReject) : onResolve(x);
+      },
+      onReject,
+      PromiseChain
+    );
+  }
 
-function PromiseCast(x) {
-  // TODO(rossberg): cannot do better until we support @@create.
-  return IsPromise(x) ? x : new this(function(resolve) { resolve(x) });
-}
+  // Combinators.
 
-function PromiseAll(values) {
-  var deferred = %_CallFunction(this, PromiseDeferred);
-  var resolutions = [];
-  if (!%_IsArray(values)) {
-    deferred.reject(MakeTypeError('invalid_argument'));
+  function PromiseCast(x) {
+    // TODO(rossberg): cannot do better until we support @@create.
+    return IsPromise(x) ? x : new this(function(resolve) { resolve(x) });
+  }
+
+  function PromiseAll(values) {
+    var deferred = %_CallFunction(this, PromiseDeferred);
+    var resolutions = [];
+    if (!%_IsArray(values)) {
+      deferred.reject(MakeTypeError('invalid_argument'));
+      return deferred.promise;
+    }
+    try {
+      var count = values.length;
+      if (count === 0) {
+        deferred.resolve(resolutions);
+      } else {
+        for (var i = 0; i < values.length; ++i) {
+          this.resolve(values[i]).then(
+            function(i, x) {
+              resolutions[i] = x;
+              if (--count === 0) deferred.resolve(resolutions);
+            }.bind(UNDEFINED, i),  // TODO(rossberg): use let loop once
+                                    // available
+            function(r) { deferred.reject(r) }
+          );
+        }
+      }
+    } catch (e) {
+      deferred.reject(e)
+    }
     return deferred.promise;
   }
-  try {
-    var count = values.length;
-    if (count === 0) {
-      deferred.resolve(resolutions);
-    } else {
+
+  function PromiseOne(values) {
+    var deferred = %_CallFunction(this, PromiseDeferred);
+    if (!%_IsArray(values)) {
+      deferred.reject(MakeTypeError('invalid_argument'));
+      return deferred.promise;
+    }
+    try {
       for (var i = 0; i < values.length; ++i) {
         this.resolve(values[i]).then(
-          function(i, x) {
-            resolutions[i] = x;
-            if (--count === 0) deferred.resolve(resolutions);
-          }.bind(UNDEFINED, i),  // TODO(rossberg): use let loop once available
+          function(x) { deferred.resolve(x) },
           function(r) { deferred.reject(r) }
         );
       }
+    } catch (e) {
+      deferred.reject(e)
     }
-  } catch (e) {
-    deferred.reject(e)
-  }
-  return deferred.promise;
-}
-
-function PromiseOne(values) {
-  var deferred = %_CallFunction(this, PromiseDeferred);
-  if (!%_IsArray(values)) {
-    deferred.reject(MakeTypeError('invalid_argument'));
     return deferred.promise;
   }
-  try {
-    for (var i = 0; i < values.length; ++i) {
-      this.resolve(values[i]).then(
-        function(x) { deferred.resolve(x) },
-        function(r) { deferred.reject(r) }
-      );
-    }
-  } catch (e) {
-    deferred.reject(e)
-  }
-  return deferred.promise;
-}
 
-//-------------------------------------------------------------------
+  // -------------------------------------------------------------------
+  // Install exported functions.
 
-function SetUpPromise() {
   %CheckIsBootstrapping();
   %SetProperty(global, 'Promise', $Promise, DONT_ENUM);
   InstallFunctions($Promise, DONT_ENUM, [
@@ -298,6 +312,5 @@ function SetUpPromise() {
     "then", PromiseThen,
     "catch", PromiseCatch
   ]);
-}
 
-SetUpPromise();
+})();
index fb9742f..4b7d1f7 100644 (file)
 // This file contains support for URI manipulations written in
 // JavaScript.
 
-// Lazily initialized.
-var hexCharArray = 0;
-var hexCharCodeArray = 0;
 
+(function() {
 
-function URIAddEncodedOctetToBuffer(octet, result, index) {
-  result[index++] = 37; // Char code of '%'.
-  result[index++] = hexCharCodeArray[octet >> 4];
-  result[index++] = hexCharCodeArray[octet & 0x0F];
-  return index;
-}
+  // -------------------------------------------------------------------
+  // Define internal helper functions.
 
+  function HexValueOf(code) {
+    // 0-9
+    if (code >= 48 && code <= 57) return code - 48;
+    // A-F
+    if (code >= 65 && code <= 70) return code - 55;
+    // a-f
+    if (code >= 97 && code <= 102) return code - 87;
 
-function URIEncodeOctets(octets, result, index) {
-  if (hexCharCodeArray === 0) {
-    hexCharCodeArray = [48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
-                        65, 66, 67, 68, 69, 70];
+    return -1;
   }
-  index = URIAddEncodedOctetToBuffer(octets[0], result, index);
-  if (octets[1]) index = URIAddEncodedOctetToBuffer(octets[1], result, index);
-  if (octets[2]) index = URIAddEncodedOctetToBuffer(octets[2], result, index);
-  if (octets[3]) index = URIAddEncodedOctetToBuffer(octets[3], result, index);
-  return index;
-}
-
-
-function URIEncodeSingle(cc, result, index) {
-  var x = (cc >> 12) & 0xF;
-  var y = (cc >> 6) & 63;
-  var z = cc & 63;
-  var octets = new $Array(3);
-  if (cc <= 0x007F) {
-    octets[0] = cc;
-  } else if (cc <= 0x07FF) {
-    octets[0] = y + 192;
-    octets[1] = z + 128;
-  } else {
-    octets[0] = x + 224;
-    octets[1] = y + 128;
-    octets[2] = z + 128;
+
+  // Does the char code correspond to an alpha-numeric char.
+  function isAlphaNumeric(cc) {
+    // a - z
+    if (97 <= cc && cc <= 122) return true;
+    // A - Z
+    if (65 <= cc && cc <= 90) return true;
+    // 0 - 9
+    if (48 <= cc && cc <= 57) return true;
+
+    return false;
   }
-  return URIEncodeOctets(octets, result, index);
-}
-
-
-function URIEncodePair(cc1 , cc2, result, index) {
-  var u = ((cc1 >> 6) & 0xF) + 1;
-  var w = (cc1 >> 2) & 0xF;
-  var x = cc1 & 3;
-  var y = (cc2 >> 6) & 0xF;
-  var z = cc2 & 63;
-  var octets = new $Array(4);
-  octets[0] = (u >> 2) + 240;
-  octets[1] = (((u & 3) << 4) | w) + 128;
-  octets[2] = ((x << 4) | y) + 128;
-  octets[3] = z + 128;
-  return URIEncodeOctets(octets, result, index);
-}
-
-
-function URIHexCharsToCharCode(highChar, lowChar) {
-  var highCode = HexValueOf(highChar);
-  var lowCode = HexValueOf(lowChar);
-  if (highCode == -1 || lowCode == -1) {
-    throw new $URIError("URI malformed");
+
+  //Lazily initialized.
+  var hexCharCodeArray = 0;
+
+  function URIAddEncodedOctetToBuffer(octet, result, index) {
+    result[index++] = 37; // Char code of '%'.
+    result[index++] = hexCharCodeArray[octet >> 4];
+    result[index++] = hexCharCodeArray[octet & 0x0F];
+    return index;
   }
-  return (highCode << 4) | lowCode;
-}
-
-
-function URIDecodeOctets(octets, result, index) {
-  if (!IS_STRING(result)) throw new $URIError("Internal error");
-  var value;
-  var o0 = octets[0];
-  if (o0 < 0x80) {
-    value = o0;
-  } else if (o0 < 0xc2) {
-    throw new $URIError("URI malformed");
-  } else {
-    var o1 = octets[1];
-    if (o0 < 0xe0) {
-      var a = o0 & 0x1f;
-      if ((o1 < 0x80) || (o1 > 0xbf)) {
-        throw new $URIError("URI malformed");
-      }
-      var b = o1 & 0x3f;
-      value = (a << 6) + b;
-      if (value < 0x80 || value > 0x7ff) {
-        throw new $URIError("URI malformed");
-      }
+
+  function URIEncodeOctets(octets, result, index) {
+    if (hexCharCodeArray === 0) {
+      hexCharCodeArray = [48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
+                          65, 66, 67, 68, 69, 70];
+    }
+    index = URIAddEncodedOctetToBuffer(octets[0], result, index);
+    if (octets[1]) index = URIAddEncodedOctetToBuffer(octets[1], result, index);
+    if (octets[2]) index = URIAddEncodedOctetToBuffer(octets[2], result, index);
+    if (octets[3]) index = URIAddEncodedOctetToBuffer(octets[3], result, index);
+    return index;
+  }
+
+  function URIEncodeSingle(cc, result, index) {
+    var x = (cc >> 12) & 0xF;
+    var y = (cc >> 6) & 63;
+    var z = cc & 63;
+    var octets = new $Array(3);
+    if (cc <= 0x007F) {
+      octets[0] = cc;
+    } else if (cc <= 0x07FF) {
+      octets[0] = y + 192;
+      octets[1] = z + 128;
     } else {
-      var o2 = octets[2];
-      if (o0 < 0xf0) {
-        var a = o0 & 0x0f;
+      octets[0] = x + 224;
+      octets[1] = y + 128;
+      octets[2] = z + 128;
+    }
+    return URIEncodeOctets(octets, result, index);
+  }
+
+  function URIEncodePair(cc1 , cc2, result, index) {
+    var u = ((cc1 >> 6) & 0xF) + 1;
+    var w = (cc1 >> 2) & 0xF;
+    var x = cc1 & 3;
+    var y = (cc2 >> 6) & 0xF;
+    var z = cc2 & 63;
+    var octets = new $Array(4);
+    octets[0] = (u >> 2) + 240;
+    octets[1] = (((u & 3) << 4) | w) + 128;
+    octets[2] = ((x << 4) | y) + 128;
+    octets[3] = z + 128;
+    return URIEncodeOctets(octets, result, index);
+  }
+
+  function URIHexCharsToCharCode(highChar, lowChar) {
+    var highCode = HexValueOf(highChar);
+    var lowCode = HexValueOf(lowChar);
+    if (highCode == -1 || lowCode == -1) {
+      throw new $URIError("URI malformed");
+    }
+    return (highCode << 4) | lowCode;
+  }
+
+  // Callers must ensure that |result| is a sufficiently long sequential
+  // two-byte string!
+  function URIDecodeOctets(octets, result, index) {
+    var value;
+    var o0 = octets[0];
+    if (o0 < 0x80) {
+      value = o0;
+    } else if (o0 < 0xc2) {
+      throw new $URIError("URI malformed");
+    } else {
+      var o1 = octets[1];
+      if (o0 < 0xe0) {
+        var a = o0 & 0x1f;
         if ((o1 < 0x80) || (o1 > 0xbf)) {
           throw new $URIError("URI malformed");
         }
         var b = o1 & 0x3f;
-        if ((o2 < 0x80) || (o2 > 0xbf)) {
-          throw new $URIError("URI malformed");
-        }
-        var c = o2 & 0x3f;
-        value = (a << 12) + (b << 6) + c;
-        if ((value < 0x800) || (value > 0xffff)) {
+        value = (a << 6) + b;
+        if (value < 0x80 || value > 0x7ff) {
           throw new $URIError("URI malformed");
         }
       } else {
-        var o3 = octets[3];
-        if (o0 < 0xf8) {
-          var a = (o0 & 0x07);
+        var o2 = octets[2];
+        if (o0 < 0xf0) {
+          var a = o0 & 0x0f;
           if ((o1 < 0x80) || (o1 > 0xbf)) {
             throw new $URIError("URI malformed");
           }
-          var b = (o1 & 0x3f);
+          var b = o1 & 0x3f;
           if ((o2 < 0x80) || (o2 > 0xbf)) {
             throw new $URIError("URI malformed");
           }
-          var c = (o2 & 0x3f);
-          if ((o3 < 0x80) || (o3 > 0xbf)) {
+          var c = o2 & 0x3f;
+          value = (a << 12) + (b << 6) + c;
+          if ((value < 0x800) || (value > 0xffff)) {
             throw new $URIError("URI malformed");
           }
-          var d = (o3 & 0x3f);
-          value = (a << 18) + (b << 12) + (c << 6) + d;
-          if ((value < 0x10000) || (value > 0x10ffff)) {
+        } else {
+          var o3 = octets[3];
+          if (o0 < 0xf8) {
+            var a = (o0 & 0x07);
+            if ((o1 < 0x80) || (o1 > 0xbf)) {
+              throw new $URIError("URI malformed");
+            }
+            var b = (o1 & 0x3f);
+            if ((o2 < 0x80) || (o2 > 0xbf)) {
+              throw new $URIError("URI malformed");
+            }
+            var c = (o2 & 0x3f);
+            if ((o3 < 0x80) || (o3 > 0xbf)) {
+              throw new $URIError("URI malformed");
+            }
+            var d = (o3 & 0x3f);
+            value = (a << 18) + (b << 12) + (c << 6) + d;
+            if ((value < 0x10000) || (value > 0x10ffff)) {
+              throw new $URIError("URI malformed");
+            }
+          } else {
             throw new $URIError("URI malformed");
           }
-        } else {
-          throw new $URIError("URI malformed");
         }
       }
     }
-  }
-  if (0xD800 <= value && value <= 0xDFFF) {
-    throw new $URIError("URI malformed");
-  }
-  if (value < 0x10000) {
-    if (index < 0 || index >= result.length) {
-      throw new $URIError("Internal error");
+    if (0xD800 <= value && value <= 0xDFFF) {
+      throw new $URIError("URI malformed");
     }
-    %_TwoByteSeqStringSetChar(result, index++, value);
-    return index;
-  } else {
-    if (index < 0 || index >= result.length - 1) {
-      throw new $URIError("Internal error");
+    if (value < 0x10000) {
+      %_TwoByteSeqStringSetChar(result, index++, value);
+    } else {
+      %_TwoByteSeqStringSetChar(result, index++, (value >> 10) + 0xd7c0);
+      %_TwoByteSeqStringSetChar(result, index++, (value & 0x3ff) + 0xdc00);
     }
-    %_TwoByteSeqStringSetChar(result, index++, (value >> 10) + 0xd7c0);
-    %_TwoByteSeqStringSetChar(result, index++, (value & 0x3ff) + 0xdc00);
     return index;
   }
-}
-
-
-// ECMA-262, section 15.1.3
-function Encode(uri, unescape) {
-  var uriLength = uri.length;
-  var array = new InternalArray(uriLength);
-  var index = 0;
-  for (var k = 0; k < uriLength; k++) {
-    var cc1 = uri.charCodeAt(k);
-    if (unescape(cc1)) {
-      array[index++] = cc1;
-    } else {
-      if (cc1 >= 0xDC00 && cc1 <= 0xDFFF) throw new $URIError("URI malformed");
-      if (cc1 < 0xD800 || cc1 > 0xDBFF) {
-        index = URIEncodeSingle(cc1, array, index);
+
+  // ECMA-262, section 15.1.3
+  function Encode(uri, unescape) {
+    var uriLength = uri.length;
+    var array = new InternalArray(uriLength);
+    var index = 0;
+    for (var k = 0; k < uriLength; k++) {
+      var cc1 = uri.charCodeAt(k);
+      if (unescape(cc1)) {
+        array[index++] = cc1;
       } else {
-        k++;
-        if (k == uriLength) throw new $URIError("URI malformed");
-        var cc2 = uri.charCodeAt(k);
-        if (cc2 < 0xDC00 || cc2 > 0xDFFF) throw new $URIError("URI malformed");
-        index = URIEncodePair(cc1, cc2, array, index);
+        if (cc1 >= 0xDC00 && cc1 <= 0xDFFF) throw new $URIError("URI malformed");
+        if (cc1 < 0xD800 || cc1 > 0xDBFF) {
+          index = URIEncodeSingle(cc1, array, index);
+        } else {
+          k++;
+          if (k == uriLength) throw new $URIError("URI malformed");
+          var cc2 = uri.charCodeAt(k);
+          if (cc2 < 0xDC00 || cc2 > 0xDFFF) throw new $URIError("URI malformed");
+          index = URIEncodePair(cc1, cc2, array, index);
+        }
       }
     }
-  }
 
-  var result = %NewString(array.length, NEW_ONE_BYTE_STRING);
-  for (var i = 0; i < array.length; i++) {
-    %_OneByteSeqStringSetChar(result, i, array[i]);
+    var result = %NewString(array.length, NEW_ONE_BYTE_STRING);
+    for (var i = 0; i < array.length; i++) {
+      %_OneByteSeqStringSetChar(result, i, array[i]);
+    }
+    return result;
   }
-  return result;
-}
-
-
-// ECMA-262, section 15.1.3
-function Decode(uri, reserved) {
-  var uriLength = uri.length;
-  var one_byte = %NewString(uriLength, NEW_ONE_BYTE_STRING);
-  var index = 0;
-  var k = 0;
-
-  // Optimistically assume ascii string.
-  for ( ; k < uriLength; k++) {
-    var code = uri.charCodeAt(k);
-    if (code == 37) {  // '%'
-      if (k + 2 >= uriLength) throw new $URIError("URI malformed");
-      var cc = URIHexCharsToCharCode(uri.charCodeAt(k+1), uri.charCodeAt(k+2));
-      if (cc >> 7) break;  // Assumption wrong, two byte string.
-      if (reserved(cc)) {
-        %_OneByteSeqStringSetChar(one_byte, index++, 37);  // '%'.
-        %_OneByteSeqStringSetChar(one_byte, index++, uri.charCodeAt(k+1));
-        %_OneByteSeqStringSetChar(one_byte, index++, uri.charCodeAt(k+2));
+
+  // ECMA-262, section 15.1.3
+  function Decode(uri, reserved) {
+    var uriLength = uri.length;
+    var one_byte = %NewString(uriLength, NEW_ONE_BYTE_STRING);
+    var index = 0;
+    var k = 0;
+
+    // Optimistically assume ascii string.
+    for ( ; k < uriLength; k++) {
+      var code = uri.charCodeAt(k);
+      if (code == 37) {  // '%'
+        if (k + 2 >= uriLength) throw new $URIError("URI malformed");
+        var cc = URIHexCharsToCharCode(uri.charCodeAt(k+1), uri.charCodeAt(k+2));
+        if (cc >> 7) break;  // Assumption wrong, two byte string.
+        if (reserved(cc)) {
+          %_OneByteSeqStringSetChar(one_byte, index++, 37);  // '%'.
+          %_OneByteSeqStringSetChar(one_byte, index++, uri.charCodeAt(k+1));
+          %_OneByteSeqStringSetChar(one_byte, index++, uri.charCodeAt(k+2));
+        } else {
+          %_OneByteSeqStringSetChar(one_byte, index++, cc);
+        }
+        k += 2;
       } else {
-        %_OneByteSeqStringSetChar(one_byte, index++, cc);
+        if (code > 0x7f) break;  // Assumption wrong, two byte string.
+        %_OneByteSeqStringSetChar(one_byte, index++, code);
       }
-      k += 2;
-    } else {
-      if (code > 0x7f) break;  // Assumption wrong, two byte string.
-      %_OneByteSeqStringSetChar(one_byte, index++, code);
     }
-  }
 
-  one_byte = %TruncateString(one_byte, index);
-  if (k == uriLength) return one_byte;
-
-  // Write into two byte string.
-  var two_byte = %NewString(uriLength - k, NEW_TWO_BYTE_STRING);
-  index = 0;
-
-  for ( ; k < uriLength; k++) {
-    var code = uri.charCodeAt(k);
-    if (code == 37) {  // '%'
-      if (k + 2 >= uriLength) throw new $URIError("URI malformed");
-      var cc = URIHexCharsToCharCode(uri.charCodeAt(++k), uri.charCodeAt(++k));
-      if (cc >> 7) {
-        var n = 0;
-        while (((cc << ++n) & 0x80) != 0) { }
-        if (n == 1 || n > 4) throw new $URIError("URI malformed");
-        var octets = new $Array(n);
-        octets[0] = cc;
-        if (k + 3 * (n - 1) >= uriLength) throw new $URIError("URI malformed");
-        for (var i = 1; i < n; i++) {
-          if (uri.charAt(++k) != '%') throw new $URIError("URI malformed");
-          octets[i] = URIHexCharsToCharCode(uri.charCodeAt(++k),
-                                            uri.charCodeAt(++k));
+    one_byte = %TruncateString(one_byte, index);
+    if (k == uriLength) return one_byte;
+
+    // Write into two byte string.
+    var two_byte = %NewString(uriLength - k, NEW_TWO_BYTE_STRING);
+    index = 0;
+
+    for ( ; k < uriLength; k++) {
+      var code = uri.charCodeAt(k);
+      if (code == 37) {  // '%'
+        if (k + 2 >= uriLength) throw new $URIError("URI malformed");
+        var cc = URIHexCharsToCharCode(uri.charCodeAt(++k), uri.charCodeAt(++k));
+        if (cc >> 7) {
+          var n = 0;
+          while (((cc << ++n) & 0x80) != 0) { }
+          if (n == 1 || n > 4) throw new $URIError("URI malformed");
+          var octets = new $Array(n);
+          octets[0] = cc;
+          if (k + 3 * (n - 1) >= uriLength) throw new $URIError("URI malformed");
+          for (var i = 1; i < n; i++) {
+            if (uri.charAt(++k) != '%') throw new $URIError("URI malformed");
+            octets[i] = URIHexCharsToCharCode(uri.charCodeAt(++k),
+                                              uri.charCodeAt(++k));
+          }
+          index = URIDecodeOctets(octets, two_byte, index);
+        } else  if (reserved(cc)) {
+          %_TwoByteSeqStringSetChar(two_byte, index++, 37);  // '%'.
+          %_TwoByteSeqStringSetChar(two_byte, index++, uri.charCodeAt(k - 1));
+          %_TwoByteSeqStringSetChar(two_byte, index++, uri.charCodeAt(k));
+        } else {
+          %_TwoByteSeqStringSetChar(two_byte, index++, cc);
         }
-        index = URIDecodeOctets(octets, two_byte, index);
-      } else  if (reserved(cc)) {
-        %_TwoByteSeqStringSetChar(two_byte, index++, 37);  // '%'.
-        %_TwoByteSeqStringSetChar(two_byte, index++, uri.charCodeAt(k - 1));
-        %_TwoByteSeqStringSetChar(two_byte, index++, uri.charCodeAt(k));
       } else {
-        %_TwoByteSeqStringSetChar(two_byte, index++, cc);
+        %_TwoByteSeqStringSetChar(two_byte, index++, code);
       }
-    } else {
-      %_TwoByteSeqStringSetChar(two_byte, index++, code);
     }
-  }
 
-  two_byte = %TruncateString(two_byte, index);
-  return one_byte + two_byte;
-}
-
-
-// ECMA-262 - 15.1.3.1.
-function URIDecode(uri) {
-  var reservedPredicate = function(cc) {
-    // #$
-    if (35 <= cc && cc <= 36) return true;
-    // &
-    if (cc == 38) return true;
-    // +,
-    if (43 <= cc && cc <= 44) return true;
-    // /
-    if (cc == 47) return true;
-    // :;
-    if (58 <= cc && cc <= 59) return true;
-    // =
-    if (cc == 61) return true;
-    // ?@
-    if (63 <= cc && cc <= 64) return true;
-
-    return false;
-  };
-  var string = ToString(uri);
-  return Decode(string, reservedPredicate);
-}
-
-
-// ECMA-262 - 15.1.3.2.
-function URIDecodeComponent(component) {
-  var reservedPredicate = function(cc) { return false; };
-  var string = ToString(component);
-  return Decode(string, reservedPredicate);
-}
-
-
-// Does the char code correspond to an alpha-numeric char.
-function isAlphaNumeric(cc) {
-  // a - z
-  if (97 <= cc && cc <= 122) return true;
-  // A - Z
-  if (65 <= cc && cc <= 90) return true;
-  // 0 - 9
-  if (48 <= cc && cc <= 57) return true;
-
-  return false;
-}
-
-
-// ECMA-262 - 15.1.3.3.
-function URIEncode(uri) {
-  var unescapePredicate = function(cc) {
-    if (isAlphaNumeric(cc)) return true;
-    // !
-    if (cc == 33) return true;
-    // #$
-    if (35 <= cc && cc <= 36) return true;
-    // &'()*+,-./
-    if (38 <= cc && cc <= 47) return true;
-    // :;
-    if (58 <= cc && cc <= 59) return true;
-    // =
-    if (cc == 61) return true;
-    // ?@
-    if (63 <= cc && cc <= 64) return true;
-    // _
-    if (cc == 95) return true;
-    // ~
-    if (cc == 126) return true;
-
-    return false;
-  };
-
-  var string = ToString(uri);
-  return Encode(string, unescapePredicate);
-}
-
-
-// ECMA-262 - 15.1.3.4
-function URIEncodeComponent(component) {
-  var unescapePredicate = function(cc) {
-    if (isAlphaNumeric(cc)) return true;
-    // !
-    if (cc == 33) return true;
-    // '()*
-    if (39 <= cc && cc <= 42) return true;
-    // -.
-    if (45 <= cc && cc <= 46) return true;
-    // _
-    if (cc == 95) return true;
-    // ~
-    if (cc == 126) return true;
-
-    return false;
-  };
-
-  var string = ToString(component);
-  return Encode(string, unescapePredicate);
-}
+    two_byte = %TruncateString(two_byte, index);
+    return one_byte + two_byte;
+  }
 
+  // -------------------------------------------------------------------
+  // Define exported functions.
 
-function HexValueOf(code) {
-  // 0-9
-  if (code >= 48 && code <= 57) return code - 48;
-  // A-F
-  if (code >= 65 && code <= 70) return code - 55;
-  // a-f
-  if (code >= 97 && code <= 102) return code - 87;
+  // ECMA-262 - B.2.1.
+  function URIEscapeJS(str) {
+    var s = ToString(str);
+    return %URIEscape(s);
+  }
 
-  return -1;
-}
+  // ECMA-262 - B.2.2.
+  function URIUnescapeJS(str) {
+    var s = ToString(str);
+    return %URIUnescape(s);
+  }
 
+  // ECMA-262 - 15.1.3.1.
+  function URIDecode(uri) {
+    var reservedPredicate = function(cc) {
+      // #$
+      if (35 <= cc && cc <= 36) return true;
+      // &
+      if (cc == 38) return true;
+      // +,
+      if (43 <= cc && cc <= 44) return true;
+      // /
+      if (cc == 47) return true;
+      // :;
+      if (58 <= cc && cc <= 59) return true;
+      // =
+      if (cc == 61) return true;
+      // ?@
+      if (63 <= cc && cc <= 64) return true;
 
-// Convert a character code to 4-digit hex string representation
-// 64 -> 0040, 62234 -> F31A.
-function CharCodeToHex4Str(cc) {
-  var r = "";
-  if (hexCharArray === 0) {
-    hexCharArray = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
-                    "A", "B", "C", "D", "E", "F"];
-  }
-  for (var i = 0; i < 4; ++i) {
-    var c = hexCharArray[cc & 0x0F];
-    r = c + r;
-    cc = cc >>> 4;
-  }
-  return r;
-}
-
-
-// Returns true if all digits in string s are valid hex numbers
-function IsValidHex(s) {
-  for (var i = 0; i < s.length; ++i) {
-    var cc = s.charCodeAt(i);
-    if ((48 <= cc && cc <= 57) ||
-        (65 <= cc && cc <= 70) ||
-        (97 <= cc && cc <= 102)) {
-      // '0'..'9', 'A'..'F' and 'a' .. 'f'.
-    } else {
       return false;
-    }
+    };
+    var string = ToString(uri);
+    return Decode(string, reservedPredicate);
   }
-  return true;
-}
 
+  // ECMA-262 - 15.1.3.2.
+  function URIDecodeComponent(component) {
+    var reservedPredicate = function(cc) { return false; };
+    var string = ToString(component);
+    return Decode(string, reservedPredicate);
+  }
 
-// ECMA-262 - B.2.1.
-function URIEscapeJS(str) {
-  var s = ToString(str);
-  return %URIEscape(s);
-}
+  // ECMA-262 - 15.1.3.3.
+  function URIEncode(uri) {
+    var unescapePredicate = function(cc) {
+      if (isAlphaNumeric(cc)) return true;
+      // !
+      if (cc == 33) return true;
+      // #$
+      if (35 <= cc && cc <= 36) return true;
+      // &'()*+,-./
+      if (38 <= cc && cc <= 47) return true;
+      // :;
+      if (58 <= cc && cc <= 59) return true;
+      // =
+      if (cc == 61) return true;
+      // ?@
+      if (63 <= cc && cc <= 64) return true;
+      // _
+      if (cc == 95) return true;
+      // ~
+      if (cc == 126) return true;
 
+      return false;
+    };
+    var string = ToString(uri);
+    return Encode(string, unescapePredicate);
+  }
 
-// ECMA-262 - B.2.2.
-function URIUnescapeJS(str) {
-  var s = ToString(str);
-  return %URIUnescape(s);
-}
+  // ECMA-262 - 15.1.3.4
+  function URIEncodeComponent(component) {
+    var unescapePredicate = function(cc) {
+      if (isAlphaNumeric(cc)) return true;
+      // !
+      if (cc == 33) return true;
+      // '()*
+      if (39 <= cc && cc <= 42) return true;
+      // -.
+      if (45 <= cc && cc <= 46) return true;
+      // _
+      if (cc == 95) return true;
+      // ~
+      if (cc == 126) return true;
 
+      return false;
+    };
+    var string = ToString(component);
+    return Encode(string, unescapePredicate);
+  }
 
-// -------------------------------------------------------------------
+  // -------------------------------------------------------------------
+  // Install exported functions.
 
-function SetUpUri() {
   %CheckIsBootstrapping();
 
   // Set up non-enumerable URI functions on the global object and set
   // their names.
   InstallFunctions(global, DONT_ENUM, $Array(
-    "escape", URIEscapeJS,
-    "unescape", URIUnescapeJS,
-    "decodeURI", URIDecode,
-    "decodeURIComponent", URIDecodeComponent,
-    "encodeURI", URIEncode,
-    "encodeURIComponent", URIEncodeComponent
+      "escape", URIEscapeJS,
+      "unescape", URIUnescapeJS,
+      "decodeURI", URIDecode,
+      "decodeURIComponent", URIDecodeComponent,
+      "encodeURI", URIEncode,
+      "encodeURIComponent", URIEncodeComponent
   ));
-}
 
-SetUpUri();
+})();
index 96d4b35..d91986b 100755 (executable)
@@ -51,7 +51,7 @@ EXPECTED_FUNCTION_COUNT = 359
 EXPECTED_FUZZABLE_COUNT = 326
 EXPECTED_CCTEST_COUNT = 6
 EXPECTED_UNKNOWN_COUNT = 5
-EXPECTED_BUILTINS_COUNT = 824
+EXPECTED_BUILTINS_COUNT = 781
 
 
 # Don't call these at all.