Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / v8 / src / promise.js
index 710abad..2d8314a 100644 (file)
@@ -18,6 +18,7 @@ var PromiseReject;
 var PromiseChain;
 var PromiseCatch;
 var PromiseThen;
+var PromiseHasRejectHandler;
 
 // mirror-debugger.js currently uses builtins.promiseStatus. It would be nice
 // if we could move these property names into the closure below.
@@ -29,6 +30,8 @@ var promiseValue = GLOBAL_PRIVATE("Promise#value");
 var promiseOnResolve = GLOBAL_PRIVATE("Promise#onResolve");
 var promiseOnReject = GLOBAL_PRIVATE("Promise#onReject");
 var promiseRaw = GLOBAL_PRIVATE("Promise#raw");
+var promiseDebug = GLOBAL_PRIVATE("Promise#debug");
+var lastMicrotaskId = 0;
 
 (function() {
 
@@ -39,13 +42,13 @@ var promiseRaw = GLOBAL_PRIVATE("Promise#raw");
       throw MakeTypeError('resolver_not_a_function', [resolver]);
     var promise = PromiseInit(this);
     try {
-      %DebugPromiseHandlePrologue(function() { return promise });
+      %DebugPushPromise(promise);
       resolver(function(x) { PromiseResolve(promise, x) },
                function(r) { PromiseReject(promise, r) });
     } catch (e) {
       PromiseReject(promise, e);
     } finally {
-      %DebugPromiseHandleEpilogue();
+      %DebugPopPromise();
     }
   }
 
@@ -56,6 +59,9 @@ var promiseRaw = GLOBAL_PRIVATE("Promise#raw");
     SET_PRIVATE(promise, promiseValue, value);
     SET_PRIVATE(promise, promiseOnResolve, onResolve);
     SET_PRIVATE(promise, promiseOnReject, onReject);
+    if (DEBUG_IS_ACTIVE) {
+      %DebugPromiseEvent({ promise: promise, status: status, value: value });
+    }
     return promise;
   }
 
@@ -66,7 +72,7 @@ var promiseRaw = GLOBAL_PRIVATE("Promise#raw");
 
   function PromiseDone(promise, status, value, promiseQueue) {
     if (GET_PRIVATE(promise, promiseStatus) === 0) {
-      PromiseEnqueue(value, GET_PRIVATE(promise, promiseQueue));
+      PromiseEnqueue(value, GET_PRIVATE(promise, promiseQueue), status);
       PromiseSet(promise, status, value);
     }
   }
@@ -94,11 +100,7 @@ var promiseRaw = GLOBAL_PRIVATE("Promise#raw");
 
   function PromiseHandle(value, handler, deferred) {
     try {
-      %DebugPromiseHandlePrologue(
-          function() {
-            var queue = GET_PRIVATE(deferred.promise, promiseOnReject);
-            return (queue && queue.length == 0) ? deferred.promise : UNDEFINED;
-          });
+      %DebugPushPromise(deferred.promise);
       var result = handler(value);
       if (result === deferred.promise)
         throw MakeTypeError('promise_cyclic', [result]);
@@ -107,23 +109,30 @@ var promiseRaw = GLOBAL_PRIVATE("Promise#raw");
       else
         deferred.resolve(result);
     } catch (exception) {
-      try {
-        %DebugPromiseHandlePrologue(function() { return deferred.promise });
-        deferred.reject(exception);
-      } catch (e) { } finally {
-        %DebugPromiseHandleEpilogue();
-      }
+      try { deferred.reject(exception); } catch (e) { }
     } finally {
-      %DebugPromiseHandleEpilogue();
+      %DebugPopPromise();
     }
   }
 
-  function PromiseEnqueue(value, tasks) {
+  function PromiseEnqueue(value, tasks, status) {
+    var id, name, instrumenting = DEBUG_IS_ACTIVE;
     %EnqueueMicrotask(function() {
+      if (instrumenting) {
+        %DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name });
+      }
       for (var i = 0; i < tasks.length; i += 2) {
         PromiseHandle(value, tasks[i], tasks[i + 1])
       }
+      if (instrumenting) {
+        %DebugAsyncTaskEvent({ type: "didHandle", id: id, name: name });
+      }
     });
+    if (instrumenting) {
+      id = ++lastMicrotaskId;
+      name = status > 0 ? "Promise.resolve" : "Promise.reject";
+      %DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name });
+    }
   }
 
   function PromiseIdResolveHandler(x) { return x }
@@ -149,6 +158,13 @@ var promiseRaw = GLOBAL_PRIVATE("Promise#raw");
   }
 
   PromiseReject = function PromiseReject(promise, r) {
+    // Check promise status to confirm that this reject has an effect.
+    // Check promiseDebug property to avoid duplicate event.
+    if (DEBUG_IS_ACTIVE &&
+        GET_PRIVATE(promise, promiseStatus) == 0 &&
+        !HAS_PRIVATE(promise, promiseDebug)) {
+      %DebugPromiseRejectEvent(promise, r);
+    }
     PromiseDone(promise, -1, r, promiseOnReject)
   }
 
@@ -194,7 +210,7 @@ var promiseRaw = GLOBAL_PRIVATE("Promise#raw");
   // Simple chaining.
 
   PromiseChain = function PromiseChain(onResolve, onReject) {  // a.k.a.
-                                                                // flatMap
+                                                               // flatMap
     onResolve = IS_UNDEFINED(onResolve) ? PromiseIdResolveHandler : onResolve;
     onReject = IS_UNDEFINED(onReject) ? PromiseIdRejectHandler : onReject;
     var deferred = %_CallFunction(this.constructor, PromiseDeferred);
@@ -206,12 +222,19 @@ var promiseRaw = GLOBAL_PRIVATE("Promise#raw");
         GET_PRIVATE(this, promiseOnReject).push(onReject, deferred);
         break;
       case +1:  // Resolved
-        PromiseEnqueue(GET_PRIVATE(this, promiseValue), [onResolve, deferred]);
+        PromiseEnqueue(GET_PRIVATE(this, promiseValue),
+                       [onResolve, deferred],
+                       +1);
         break;
       case -1:  // Rejected
-        PromiseEnqueue(GET_PRIVATE(this, promiseValue), [onReject, deferred]);
+        PromiseEnqueue(GET_PRIVATE(this, promiseValue),
+                       [onReject, deferred],
+                       -1);
         break;
     }
+    if (DEBUG_IS_ACTIVE) {
+      %DebugPromiseEvent({ promise: deferred.promise, parentPromise: this });
+    }
     return deferred.promise;
   }
 
@@ -261,11 +284,15 @@ var promiseRaw = GLOBAL_PRIVATE("Promise#raw");
       } 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() {
+              // Nested scope to get closure over current i (and avoid .bind).
+              // TODO(rossberg): Use for-let instead once available.
+              var i_captured = i;
+              return function(x) {
+                resolutions[i_captured] = x;
+                if (--count === 0) deferred.resolve(resolutions);
+              };
+            })(),
             function(r) { deferred.reject(r) }
           );
         }
@@ -295,11 +322,21 @@ var promiseRaw = GLOBAL_PRIVATE("Promise#raw");
     return deferred.promise;
   }
 
+
+  // Utility for debugger
+
+  PromiseHasRejectHandler = function PromiseHasRejectHandler() {
+    // Mark promise as already having triggered a reject event.
+    SET_PRIVATE(this, promiseDebug, true);
+    var queue = GET_PRIVATE(this, promiseOnReject);
+    return !IS_UNDEFINED(queue) && queue.length > 0;
+  };
+
   // -------------------------------------------------------------------
   // Install exported functions.
 
   %CheckIsBootstrapping();
-  %SetProperty(global, 'Promise', $Promise, DONT_ENUM);
+  %AddNamedProperty(global, 'Promise', $Promise, DONT_ENUM);
   InstallFunctions($Promise, DONT_ENUM, [
     "defer", PromiseDeferred,
     "accept", PromiseResolved,