Title: [192057] trunk
Revision
192057
Author
calva...@igalia.com
Date
2015-11-05 06:01:13 -0800 (Thu, 05 Nov 2015)

Log Message

[Streams API] Shield implementation from user mangling Promise.reject and resolve methods
https://bugs.webkit.org/show_bug.cgi?id=150895

Reviewed by Youenn Fablet.

Source/_javascript_Core:

Keep Promise.resolve and reject also as internal slots for the Promise constructor given that there is no way to
retrieve the former implementation if the user decides to replace it. This allows to safely create vended
promises even if the user changes the constructor methods.

* runtime/JSPromiseConstructor.h:
* runtime/JSPromiseConstructor.cpp:
(JSC::JSPromiseConstructor::addOwnInternalSlots): Added to include @reject and @resolve.
(JSC::JSPromiseConstructor::create): Call addOwnInternalSlots.

Source/WebCore:

Replace all calls to @Promise.resolve and @Promise.reject with their internal slot counterparts. This way we
ensure that if the user replaces those constructor methods, our implementation still works.

Test: streams/streams-promises.html.

* Modules/streams/ReadableStream.js:
(initializeReadableStream):
(cancel):
* Modules/streams/ReadableStreamInternals.js:
(privateInitializeReadableStreamReader):
(cancelReadableStream):
(readFromReadableStreamReader):
* Modules/streams/ReadableStreamReader.js:
(cancel):
(read):
(closed):
* Modules/streams/StreamInternals.js:
(promiseInvokeOrNoop):
(promiseInvokeOrFallbackOrNoop):
* Modules/streams/WritableStream.js:
(initializeWritableStream):
(abort):
(close):
(write):
(closed):
(ready):

LayoutTests:

* streams/streams-promises.html: Improved some style issues. Added tests about changing Promise.resolve and
reject.
* streams/streams-promises-expected.txt: Added expectations.

Modified Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (192056 => 192057)


--- trunk/LayoutTests/ChangeLog	2015-11-05 11:36:15 UTC (rev 192056)
+++ trunk/LayoutTests/ChangeLog	2015-11-05 14:01:13 UTC (rev 192057)
@@ -1,3 +1,14 @@
+2015-11-05  Xabier Rodriguez Calvar  <calva...@igalia.com>
+
+        [Streams API] Shield implementation from user mangling Promise.reject and resolve methods
+        https://bugs.webkit.org/show_bug.cgi?id=150895
+
+        Reviewed by Youenn Fablet.
+
+        * streams/streams-promises.html: Improved some style issues. Added tests about changing Promise.resolve and
+        reject.
+        * streams/streams-promises-expected.txt: Added expectations.
+
 2015-11-05  Manuel Rego Casasnovas  <r...@igalia.com>
 
         [css-grid] Support positioned grid children

Modified: trunk/LayoutTests/streams/streams-promises-expected.txt (192056 => 192057)


--- trunk/LayoutTests/streams/streams-promises-expected.txt	2015-11-05 11:36:15 UTC (rev 192056)
+++ trunk/LayoutTests/streams/streams-promises-expected.txt	2015-11-05 14:01:13 UTC (rev 192057)
@@ -1,3 +1,5 @@
 
-PASS Streams can be built even if Promise constructor is replaced 
+PASS Streams implementation is not affected if Promise constructor is replaced 
+PASS Streams implementation is not affected if Promise.resolve is replaced 
+PASS Streams implementation is not affected if Promise.reject is replaced 
 

Modified: trunk/LayoutTests/streams/streams-promises.html (192056 => 192057)


--- trunk/LayoutTests/streams/streams-promises.html	2015-11-05 11:36:15 UTC (rev 192056)
+++ trunk/LayoutTests/streams/streams-promises.html	2015-11-05 14:01:13 UTC (rev 192057)
@@ -2,10 +2,43 @@
 <script src=''></script>
 <script src=''></script>
 <script>
+
 test(function() {
-    Promise = function() { throw new Error("nasty things"); };
+    const PromiseBackup = Promise;
 
-    const rs = new ReadableStream(); // Does not throw.
-    const ws = new WritableStream(); // Does not throw.
-}, 'Streams can be built even if Promise constructor is replaced');
+    try {
+        Promise = function() { assert_unreached("streams should not use this Promise object"); };
+
+        new ReadableStream();
+        new WritableStream();
+    } finally {
+        Promise = PromiseBackup;
+    }
+}, 'Streams implementation is not affected if Promise constructor is replaced');
+
+test(function() {
+    const PromiseResolveBackup = Promise.resolve;
+
+    try {
+        Promise.resolve = function() { assert_unreached("streams should not use this Promise.resolve method"); };
+
+        new ReadableStream();
+        new WritableStream();
+    } finally {
+        Promise.resolve = PromiseResolveBackup;
+    }
+}, 'Streams implementation is not affected if Promise.resolve is replaced');
+
+test(function() {
+    const PromiseRejectBackup = Promise.reject;
+
+    try {
+        Promise.reject = function() { assert_unreached("streams should not use this Promise.reject method"); };
+
+        ReadableStream.prototype.cancel.call({}, "reason");
+        WritableStream.prototype.abort.call({}, "reason");
+    } finally {
+        Promise.reject = PromiseRejectBackup;
+    }
+}, 'Streams implementation is not affected if Promise.reject is replaced');
 </script>

Modified: trunk/Source/_javascript_Core/ChangeLog (192056 => 192057)


--- trunk/Source/_javascript_Core/ChangeLog	2015-11-05 11:36:15 UTC (rev 192056)
+++ trunk/Source/_javascript_Core/ChangeLog	2015-11-05 14:01:13 UTC (rev 192057)
@@ -1,3 +1,19 @@
+2015-11-05  Xabier Rodriguez Calvar  <calva...@igalia.com>
+
+        [Streams API] Shield implementation from user mangling Promise.reject and resolve methods
+        https://bugs.webkit.org/show_bug.cgi?id=150895
+
+        Reviewed by Youenn Fablet.
+
+        Keep Promise.resolve and reject also as internal slots for the Promise constructor given that there is no way to
+        retrieve the former implementation if the user decides to replace it. This allows to safely create vended
+        promises even if the user changes the constructor methods.
+
+        * runtime/JSPromiseConstructor.h:
+        * runtime/JSPromiseConstructor.cpp:
+        (JSC::JSPromiseConstructor::addOwnInternalSlots): Added to include @reject and @resolve.
+        (JSC::JSPromiseConstructor::create): Call addOwnInternalSlots.
+
 2015-11-04  Benjamin Poulain  <bpoul...@apple.com>
 
         [JSC] Add B3-to-Air lowering for the shift opcodes

Modified: trunk/Source/_javascript_Core/runtime/JSPromiseConstructor.cpp (192056 => 192057)


--- trunk/Source/_javascript_Core/runtime/JSPromiseConstructor.cpp	2015-11-05 11:36:15 UTC (rev 192056)
+++ trunk/Source/_javascript_Core/runtime/JSPromiseConstructor.cpp	2015-11-05 14:01:13 UTC (rev 192057)
@@ -33,6 +33,7 @@
 #include "JSCBuiltins.h"
 #include "JSCJSValueInlines.h"
 #include "JSCellInlines.h"
+#include "JSFunction.h"
 #include "JSPromise.h"
 #include "JSPromisePrototype.h"
 #include "Lookup.h"
@@ -64,6 +65,7 @@
 {
     JSPromiseConstructor* constructor = new (NotNull, allocateCell<JSPromiseConstructor>(vm.heap)) JSPromiseConstructor(vm, structure);
     constructor->finishCreation(vm, promisePrototype);
+    constructor->addOwnInternalSlots(vm, structure->globalObject());
     return constructor;
 }
 
@@ -84,6 +86,12 @@
     putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(1), ReadOnly | DontEnum | DontDelete);
 }
 
+void JSPromiseConstructor::addOwnInternalSlots(VM& vm, JSGlobalObject* globalObject)
+{
+    JSC_BUILTIN_FUNCTION(vm.propertyNames->builtinNames().resolvePrivateName(), promiseConstructorResolveCodeGenerator, DontEnum | DontDelete | ReadOnly);
+    JSC_BUILTIN_FUNCTION(vm.propertyNames->builtinNames().rejectPrivateName(), promiseConstructorRejectCodeGenerator, DontEnum | DontDelete | ReadOnly);
+}
+
 static EncodedJSValue JSC_HOST_CALL constructPromise(ExecState* exec)
 {
     JSGlobalObject* globalObject = exec->callee()->globalObject();

Modified: trunk/Source/_javascript_Core/runtime/JSPromiseConstructor.h (192056 => 192057)


--- trunk/Source/_javascript_Core/runtime/JSPromiseConstructor.h	2015-11-05 11:36:15 UTC (rev 192056)
+++ trunk/Source/_javascript_Core/runtime/JSPromiseConstructor.h	2015-11-05 14:01:13 UTC (rev 192057)
@@ -52,6 +52,8 @@
 private:
     static ConstructType getConstructData(JSCell*, ConstructData&);
     static CallType getCallData(JSCell*, CallData&);
+
+    void addOwnInternalSlots(VM&, JSGlobalObject*);
 };
 
 } // namespace JSC

Modified: trunk/Source/WebCore/ChangeLog (192056 => 192057)


--- trunk/Source/WebCore/ChangeLog	2015-11-05 11:36:15 UTC (rev 192056)
+++ trunk/Source/WebCore/ChangeLog	2015-11-05 14:01:13 UTC (rev 192057)
@@ -1,3 +1,37 @@
+2015-11-05  Xabier Rodriguez Calvar  <calva...@igalia.com>
+
+        [Streams API] Shield implementation from user mangling Promise.reject and resolve methods
+        https://bugs.webkit.org/show_bug.cgi?id=150895
+
+        Reviewed by Youenn Fablet.
+
+        Replace all calls to @Promise.resolve and @Promise.reject with their internal slot counterparts. This way we
+        ensure that if the user replaces those constructor methods, our implementation still works.
+
+        Test: streams/streams-promises.html.
+
+        * Modules/streams/ReadableStream.js:
+        (initializeReadableStream):
+        (cancel):
+        * Modules/streams/ReadableStreamInternals.js:
+        (privateInitializeReadableStreamReader):
+        (cancelReadableStream):
+        (readFromReadableStreamReader):
+        * Modules/streams/ReadableStreamReader.js:
+        (cancel):
+        (read):
+        (closed):
+        * Modules/streams/StreamInternals.js:
+        (promiseInvokeOrNoop):
+        (promiseInvokeOrFallbackOrNoop):
+        * Modules/streams/WritableStream.js:
+        (initializeWritableStream):
+        (abort):
+        (close):
+        (write):
+        (closed):
+        (ready):
+
 2015-11-05  Andreas Kling  <akl...@apple.com>
 
         Give ResourceUsageOverlay a stacked chart for dirty memory per category.

Modified: trunk/Source/WebCore/Modules/streams/ReadableStream.js (192056 => 192057)


--- trunk/Source/WebCore/Modules/streams/ReadableStream.js	2015-11-05 11:36:15 UTC (rev 192056)
+++ trunk/Source/WebCore/Modules/streams/ReadableStream.js	2015-11-05 14:01:13 UTC (rev 192057)
@@ -56,7 +56,7 @@
 
     var result = @invokeOrNoop(underlyingSource, "start", [this.@controller]);
     var _this = this;
-    @Promise.resolve(result).then(function() {
+    @Promise.@resolve(result).then(function() {
         _this.@started = true;
         @requestReadableStreamPull(_this);
     }, function(error) {
@@ -72,10 +72,10 @@
     "use strict";
 
     if (!@isReadableStream(this))
-        return @Promise.reject(new @TypeError("Function should be called on a ReadableStream"));
+        return @Promise.@reject(new @TypeError("Function should be called on a ReadableStream"));
 
     if (@isReadableStreamLocked(this))
-        return @Promise.reject(new @TypeError("ReadableStream is locked"));
+        return @Promise.@reject(new @TypeError("ReadableStream is locked"));
 
     return @cancelReadableStream(this, reason);
 }

Modified: trunk/Source/WebCore/Modules/streams/ReadableStreamInternals.js (192056 => 192057)


--- trunk/Source/WebCore/Modules/streams/ReadableStreamInternals.js	2015-11-05 11:36:15 UTC (rev 192056)
+++ trunk/Source/WebCore/Modules/streams/ReadableStreamInternals.js	2015-11-05 14:01:13 UTC (rev 192057)
@@ -48,13 +48,13 @@
     if (stream.@state === @streamClosed) {
         this.@ownerReadableStream = null;
         this.@storedError = undefined;
-        this.@closedPromiseCapability = { @promise: @Promise.resolve(undefined) };
+        this.@closedPromiseCapability = { @promise: @Promise.@resolve() };
         return this;
     }
     // FIXME: ASSERT(stream.@state === @streamErrored);
     this.@ownerReadableStream = null;
     this.@storedError = stream.@storedError;
-    this.@closedPromiseCapability = { @promise: @Promise.reject(stream.@storedError) };
+    this.@closedPromiseCapability = { @promise: @Promise.@reject(stream.@storedError) };
 
     return this;
 }
@@ -275,9 +275,9 @@
     "use strict";
 
     if (stream.@state === @streamClosed)
-        return @Promise.resolve();
+        return @Promise.@resolve();
     if (stream.@state === @streamErrored)
-        return @Promise.reject(stream.@storedError);
+        return @Promise.@reject(stream.@storedError);
     stream.@queue = @newQueue();
     @finishClosingReadableStream(stream);
     return @promiseInvokeOrNoop(stream.@underlyingSource, "cancel", [reason]).then(function() { });
@@ -354,9 +354,9 @@
     "use strict";
 
     if (reader.@state === @streamClosed)
-        return @Promise.resolve({value: undefined, done: true});
+        return @Promise.@resolve({value: undefined, done: true});
     if (reader.@state === @streamErrored)
-        return @Promise.reject(reader.@storedError);
+        return @Promise.@reject(reader.@storedError);
     // FIXME: ASSERT(!!reader.@ownerReadableStream);
     // FIXME: ASSERT(reader.@ownerReadableStream.@state === @streamReadable);
     var stream = reader.@ownerReadableStream;
@@ -366,7 +366,7 @@
             @requestReadableStreamPull(stream);
         else if (!stream.@queue.content.length)
             @finishClosingReadableStream(stream);
-        return @Promise.resolve({value: chunk, done: false});
+        return @Promise.@resolve({value: chunk, done: false});
     }
     var readPromiseCapability = @newPromiseCapability(@Promise);
     read...@readrequests.push(readPromiseCapability);

Modified: trunk/Source/WebCore/Modules/streams/ReadableStreamReader.js (192056 => 192057)


--- trunk/Source/WebCore/Modules/streams/ReadableStreamReader.js	2015-11-05 11:36:15 UTC (rev 192056)
+++ trunk/Source/WebCore/Modules/streams/ReadableStreamReader.js	2015-11-05 14:01:13 UTC (rev 192057)
@@ -30,13 +30,13 @@
     "use strict";
 
     if (!@isReadableStreamReader(this))
-        return @Promise.reject(new @TypeError("Function should be called on a ReadableStreamReader"));
+        return @Promise.@reject(new @TypeError("Function should be called on a ReadableStreamReader"));
 
     if (this.@state === @streamClosed)
-        return @Promise.resolve();
+        return @Promise.@resolve();
 
     if (this.@state === @streamErrored)
-        return @Promise.reject(this.@storedError);
+        return @Promise.@reject(this.@storedError);
 
     // FIXME: ASSERT(@isReadableStream(this.@ownerReadableStream));
     // FIXME: ASSERT(this.@ownerReadableStream.@state === @streamReadable);
@@ -48,7 +48,7 @@
     "use strict";
 
     if (!@isReadableStreamReader(this))
-        return @Promise.reject(new @TypeError("Function should be called on a ReadableStreamReader"));
+        return @Promise.@reject(new @TypeError("Function should be called on a ReadableStreamReader"));
 
     return @readFromReadableStreamReader(this);
 }
@@ -74,7 +74,7 @@
     "use strict";
 
     if (!@isReadableStreamReader(this))
-        return @Promise.reject(new @TypeError("Callee of closed is not a ReadableStreamReader"));
+        return @Promise.@reject(new @TypeError("Callee of closed is not a ReadableStreamReader"));
 
     return this.@closedPromiseCapability.@promise;
 }

Modified: trunk/Source/WebCore/Modules/streams/StreamInternals.js (192056 => 192057)


--- trunk/Source/WebCore/Modules/streams/StreamInternals.js	2015-11-05 11:36:15 UTC (rev 192056)
+++ trunk/Source/WebCore/Modules/streams/StreamInternals.js	2015-11-05 14:01:13 UTC (rev 192057)
@@ -44,12 +44,12 @@
     try {
         var method = object[key];
         if (typeof method === "undefined")
-            return @Promise.resolve();
+            return @Promise.@resolve();
         var result = method.@apply(object, args);
-        return @Promise.resolve(result);
+        return @Promise.@resolve(result);
     }
     catch(error) {
-        return @Promise.reject(error);
+        return @Promise.@reject(error);
     }
 
 }
@@ -63,10 +63,10 @@
         if (typeof method === "undefined")
             return @promiseInvokeOrNoop(object, key2, args2);
         const result = method.@apply(object, args1);
-        return @Promise.resolve(result);
+        return @Promise.@resolve(result);
     }
     catch(error) {
-        return @Promise.reject(error);
+        return @Promise.@reject(error);
     }
 }
 

Modified: trunk/Source/WebCore/Modules/streams/WritableStream.js (192056 => 192057)


--- trunk/Source/WebCore/Modules/streams/WritableStream.js	2015-11-05 11:36:15 UTC (rev 192056)
+++ trunk/Source/WebCore/Modules/streams/WritableStream.js	2015-11-05 14:01:13 UTC (rev 192057)
@@ -43,7 +43,7 @@
 
     this.@underlyingSink = underlyingSink;
     this.@closedPromiseCapability = @newPromiseCapability(@Promise);
-    this.@readyPromiseCapability = { @promise: @Promise.resolve(undefined) };
+    this.@readyPromiseCapability = { @promise: @Promise.@resolve() };
     this.@queue = @newQueue();
     this.@state = @streamWritable;
     this.@started = false;
@@ -55,7 +55,7 @@
 
     var error = @errorWritableStream.bind(this);
     var startResult = @invokeOrNoop(underlyingSink, "start", [error]);
-    this.@startedPromise = @Promise.resolve(startResult);
+    this.@startedPromise = @Promise.@resolve(startResult);
     var _this = this;
     th...@startedpromise.then(function() {
         _this.@started = true;
@@ -70,13 +70,13 @@
     "use strict";
 
     if (!@isWritableStream(this))
-        return @Promise.reject(new @TypeError("The WritableStream.abort method can only be used on instances of WritableStream"));
+        return @Promise.@reject(new @TypeError("The WritableStream.abort method can only be used on instances of WritableStream"));
 
     if (this.@state === @streamClosed)
-        return @Promise.resolve(undefined);
+        return @Promise.@resolve();
 
     if (this.@state === @streamErrored)
-        return @Promise.reject(this.@storedError);
+        return @Promise.@reject(this.@storedError);
 
     @errorWritableStream.@apply(this, [reason]);
 
@@ -90,13 +90,13 @@
     "use strict";
 
     if (!@isWritableStream(this))
-        return @Promise.reject(new @TypeError("The WritableStream.close method can only be used on instances of WritableStream"));
+        return @Promise.@reject(new @TypeError("The WritableStream.close method can only be used on instances of WritableStream"));
 
     if (this.@state === @streamClosed || this.@state === @streamClosing)
-        return @Promise.reject(new @TypeError("Cannot close a WritableString that is closed or closing"));
+        return @Promise.@reject(new @TypeError("Cannot close a WritableString that is closed or closing"));
 
     if (this.@state === @streamErrored)
-        return @Promise.reject(this.@storedError);
+        return @Promise.@reject(this.@storedError);
 
     if (this.@state === @streamWaiting)
         this.@readyPromiseCapability.@resolve.@call(undefined, undefined);
@@ -113,13 +113,13 @@
     "use strict";
 
     if (!@isWritableStream(this))
-        return @Promise.reject(new @TypeError("The WritableStream.close method can only be used on instances of WritableStream"));
+        return @Promise.@reject(new @TypeError("The WritableStream.close method can only be used on instances of WritableStream"));
 
     if (this.@state === @streamClosed || this.@state === @streamClosing)
-        return @Promise.reject(new @TypeError("Cannot write on a WritableString that is closed or closing"));
+        return @Promise.@reject(new @TypeError("Cannot write on a WritableString that is closed or closing"));
 
     if (this.@state === @streamErrored)
-        return @Promise.reject(this.@storedError);
+        return @Promise.@reject(this.@storedError);
 
     // FIXME
     // assert(this.@state === @streamWritable || this.@state === @streamWaiting);
@@ -130,7 +130,7 @@
             chunkSize = th...@strategy.size.@call(undefined, chunk);
         } catch(e) {
             @errorWritableStream.@call(this, e);
-            return @Promise.reject(e);
+            return @Promise.@reject(e);
         }
     }
 
@@ -139,7 +139,7 @@
         @enqueueValueWithSize(this.@queue, { promiseCapability: promiseCapability, chunk: chunk }, chunkSize);
     } catch (e) {
         @errorWritableStream.@call(this, e);
-        return @Promise.reject(e);
+        return @Promise.@reject(e);
     }
 
     @syncWritableStreamStateWithQueue(this);
@@ -153,7 +153,7 @@
     "use strict";
 
     if (!@isWritableStream(this))
-        return @Promise.reject(new @TypeError("The WritableStream.closed getter can only be used on instances of WritableStream"));
+        return @Promise.@reject(new @TypeError("The WritableStream.closed getter can only be used on instances of WritableStream"));
 
     return this.@closedPromiseCapability.@promise;
 }
@@ -163,7 +163,7 @@
     "use strict";
 
     if (!@isWritableStream(this))
-        return @Promise.reject(new @TypeError("The WritableStream.ready getter can only be used on instances of WritableStream"));
+        return @Promise.@reject(new @TypeError("The WritableStream.ready getter can only be used on instances of WritableStream"));
 
     return this.@readyPromiseCapability.@promise;
 }
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to