DOMPromise should only restrict the resolution type
[WebKit-https.git] / Source / WebCore / bindings / js / JSDOMPromise.h
1 /*
2  * Copyright (C) 2013 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #ifndef JSDOMPromise_h
27 #define JSDOMPromise_h
28
29 #include "JSDOMBinding.h"
30 #include <heap/StrongInlines.h>
31 #include <runtime/JSPromiseDeferred.h>
32 #include <wtf/Optional.h>
33
34 namespace WebCore {
35
36 template<typename DOMClass>
37 struct TypeInspector {
38 private:
39     template<typename T> static constexpr auto testIsVector(int) -> decltype(std::declval<T>().shrinkToFit(), bool()) { return true; }
40     template<typename T> static constexpr bool testIsVector(...) { return false; }
41
42     template<typename T> static constexpr auto testIsRef(int) -> decltype(T::isRef) { return true; }
43     template<typename T> static constexpr bool testIsRef(...) { return false; }
44
45     template<typename T> static constexpr auto testIsRefPtr(int) -> decltype(T::isRefPtr) { return true; }
46     template<typename T> static constexpr bool testIsRefPtr(...) { return false; }
47
48 public:
49     static constexpr bool isRefOrRefPtr = testIsRef<DOMClass>(0) || testIsRefPtr<DOMClass>(0);
50     static constexpr bool isPassByValueType = std::is_pointer<DOMClass>::value
51         || std::is_same<DOMClass, std::nullptr_t>::value
52         || std::is_same<DOMClass, JSC::JSValue>::value
53         || std::is_same<DOMClass, bool>::value;
54     static constexpr bool isPassByConstRefType = testIsVector<DOMClass>(0)
55         || std::is_same<DOMClass, String>::value;
56 };
57
58 template<typename DOMClass, typename Enable = void>
59 struct PromiseResultInspector {
60 public:
61     static constexpr bool passByValue = false;
62     static constexpr bool passByRef = true;
63     static constexpr bool passByURef = false;
64     static constexpr bool passByConstRef = false;
65
66     typedef DOMClass& Type;
67 };
68
69 template<typename DOMClass>
70 struct PromiseResultInspector<DOMClass, typename std::enable_if<TypeInspector<DOMClass>::isPassByValueType>::type> {
71 public:
72     static constexpr bool passByValue = true;
73     static constexpr bool passByRef = false;
74     static constexpr bool passByURef = false;
75     static constexpr bool passByConstRef = false;
76
77     typedef DOMClass Type;
78 };
79
80 template<typename DOMClass>
81 struct PromiseResultInspector<DOMClass, typename std::enable_if<TypeInspector<DOMClass>::isPassByConstRefType>::type> {
82 public:
83     static constexpr bool passByValue = false;
84     static constexpr bool passByRef = false;
85     static constexpr bool passByURef = false;
86     static constexpr bool passByConstRef = true;
87
88     typedef const DOMClass& Type;
89 };
90
91 template<typename DOMClass>
92 struct PromiseResultInspector<DOMClass, typename std::enable_if<TypeInspector<DOMClass>::isRefOrRefPtr>::type> {
93     static constexpr bool passByValue = false;
94     static constexpr bool passByRef = false;
95     static constexpr bool passByURef = true;
96     static constexpr bool passByConstRef = false;
97 };
98
99 class DeferredWrapper {
100 public:
101     DeferredWrapper(JSC::ExecState*, JSDOMGlobalObject*, JSC::JSPromiseDeferred*);
102
103     template<class ResolveResultType> typename std::enable_if<PromiseResultInspector<ResolveResultType>::passByValue, void>::type
104     resolve(ResolveResultType result) { resolveWithValue(result); }
105     template<class ResolveResultType> typename std::enable_if<PromiseResultInspector<ResolveResultType>::passByRef, void>::type
106     resolve(ResolveResultType& result) { resolveWithValue(result); }
107     template<class ResolveResultType> typename std::enable_if<PromiseResultInspector<ResolveResultType>::passByURef, void>::type
108     resolve(ResolveResultType&& result) { resolveWithValue(std::forward<ResolveResultType>(result)); }
109     template<class ResolveResultType> typename std::enable_if<PromiseResultInspector<ResolveResultType>::passByConstRef, void>::type
110     resolve(const ResolveResultType& result) { resolveWithValue(result); }
111
112     template<class RejectResultType> typename std::enable_if<PromiseResultInspector<RejectResultType>::passByValue, void>::type
113     reject(RejectResultType result) { rejectWithValue(result); }
114     template<class RejectResultType> typename std::enable_if<PromiseResultInspector<RejectResultType>::passByRef, void>::type
115     reject(RejectResultType& result) { rejectWithValue(result); }
116     template<class RejectResultType> typename std::enable_if<PromiseResultInspector<RejectResultType>::passByURef, void>::type
117     reject(RejectResultType&& result) { rejectWithValue(std::forward<RejectResultType>(result)); }
118     template<class RejectResultType> typename std::enable_if<PromiseResultInspector<RejectResultType>::passByConstRef, void>::type
119     reject(const RejectResultType& result) { rejectWithValue(result); }
120
121     void reject(ExceptionCode);
122
123     JSDOMGlobalObject& globalObject() const;
124     JSC::JSValue promise() const;
125
126 private:
127     void callFunction(JSC::ExecState&, JSC::JSValue function, JSC::JSValue resolution);
128     void resolve(JSC::ExecState& state, JSC::JSValue resolution) { callFunction(state, m_deferred->resolve(), resolution); }
129     void reject(JSC::ExecState& state, JSC::JSValue resolution) { callFunction(state, m_deferred->reject(), resolution); }
130
131     template<class RejectResultType> void rejectWithValue(RejectResultType&&);
132     template<class ResolveResultType> void resolveWithValue(ResolveResultType&&);
133
134     JSC::Strong<JSDOMGlobalObject> m_globalObject;
135     JSC::Strong<JSC::JSPromiseDeferred> m_deferred;
136 };
137
138 void fulfillPromiseWithJSON(DeferredWrapper&, const String&);
139 void fulfillPromiseWithArrayBuffer(DeferredWrapper&, ArrayBuffer*);
140 void fulfillPromiseWithArrayBuffer(DeferredWrapper&, const void*, size_t);
141 void rejectPromiseWithExceptionIfAny(JSC::ExecState&, JSDOMGlobalObject&, JSC::JSPromiseDeferred&);
142
143 inline JSC::JSValue callPromiseFunction(JSC::ExecState& state, JSC::EncodedJSValue promiseFunction(JSC::ExecState*, JSC::JSPromiseDeferred*))
144 {
145     JSDOMGlobalObject& globalObject = *JSC::jsCast<JSDOMGlobalObject*>(state.lexicalGlobalObject());
146     JSC::JSPromiseDeferred& promiseDeferred = *JSC::JSPromiseDeferred::create(&state, &globalObject);
147     promiseFunction(&state, &promiseDeferred);
148
149     rejectPromiseWithExceptionIfAny(state, globalObject, promiseDeferred);
150     ASSERT(!state.hadException());
151     return promiseDeferred.promise();
152 }
153
154 template <typename Value>
155 class DOMPromise {
156 public:
157     DOMPromise(DeferredWrapper&& wrapper) : m_wrapper(WTFMove(wrapper)) { }
158     DOMPromise(DOMPromise&& promise) : m_wrapper(WTFMove(promise.m_wrapper)) { }
159
160     DOMPromise(const DOMPromise&) = default;
161     DOMPromise& operator=(DOMPromise const&) = default;
162
163     void resolve(typename PromiseResultInspector<Value>::Type value) { m_wrapper.resolve(value); }
164
165     template<typename ErrorType> void reject(ErrorType&& error) { m_wrapper.reject(std::forward<ErrorType>(error)); }
166
167 private:
168     DeferredWrapper m_wrapper;
169 };
170
171 inline void DeferredWrapper::reject(ExceptionCode ec)
172 {
173     ASSERT(m_deferred);
174     ASSERT(m_globalObject);
175     JSC::ExecState* exec = m_globalObject->globalExec();
176     JSC::JSLockHolder locker(exec);
177     reject(*exec, createDOMException(exec, ec));
178 }
179
180 template<class ResolveResultType>
181 inline void DeferredWrapper::resolveWithValue(ResolveResultType&& result)
182 {
183     ASSERT(m_deferred);
184     ASSERT(m_globalObject);
185     JSC::ExecState* exec = m_globalObject->globalExec();
186     JSC::JSLockHolder locker(exec);
187     resolve(*exec, toJS(exec, m_globalObject.get(), std::forward<ResolveResultType>(result)));
188 }
189
190 template<class RejectResultType>
191 inline void DeferredWrapper::rejectWithValue(RejectResultType&& result)
192 {
193     ASSERT(m_deferred);
194     ASSERT(m_globalObject);
195     JSC::ExecState* exec = m_globalObject->globalExec();
196     JSC::JSLockHolder locker(exec);
197     reject(*exec, toJS(exec, m_globalObject.get(), std::forward<RejectResultType>(result)));
198 }
199
200 template<>
201 inline void DeferredWrapper::resolve(bool result)
202 {
203     ASSERT(m_deferred);
204     ASSERT(m_globalObject);
205     JSC::ExecState* exec = m_globalObject->globalExec();
206     JSC::JSLockHolder locker(exec);
207     resolve(*exec, JSC::jsBoolean(result));
208 }
209
210 template<>
211 inline void DeferredWrapper::resolve(JSC::JSValue value)
212 {
213     ASSERT(m_deferred);
214     ASSERT(m_globalObject);
215     JSC::ExecState* exec = m_globalObject->globalExec();
216     JSC::JSLockHolder locker(exec);
217     resolve(*exec, value);
218 }
219
220 template<>
221 inline void DeferredWrapper::reject(JSC::JSValue value)
222 {
223     ASSERT(m_deferred);
224     ASSERT(m_globalObject);
225     JSC::ExecState* exec = m_globalObject->globalExec();
226     JSC::JSLockHolder locker(exec);
227     reject(*exec, value);
228 }
229
230 template<>
231 inline void DeferredWrapper::resolve(std::nullptr_t)
232 {
233     ASSERT(m_deferred);
234     ASSERT(m_globalObject);
235     JSC::ExecState* exec = m_globalObject->globalExec();
236     JSC::JSLockHolder locker(exec);
237     resolve(*exec, JSC::jsUndefined());
238 }
239
240 template<>
241 inline void DeferredWrapper::reject(std::nullptr_t)
242 {
243     ASSERT(m_deferred);
244     ASSERT(m_globalObject);
245     JSC::ExecState* exec = m_globalObject->globalExec();
246     JSC::JSLockHolder locker(exec);
247     reject(*exec, JSC::jsNull());
248 }
249
250 template<>
251 inline void DeferredWrapper::resolve(const String& result)
252 {
253     ASSERT(m_deferred);
254     ASSERT(m_globalObject);
255     JSC::ExecState* exec = m_globalObject->globalExec();
256     JSC::JSLockHolder locker(exec);
257     resolve(*exec, jsString(exec, result));
258 }
259
260 template<>
261 inline void DeferredWrapper::reject(const String& result)
262 {
263     ASSERT(m_deferred);
264     ASSERT(m_globalObject);
265     JSC::ExecState* exec = m_globalObject->globalExec();
266     JSC::JSLockHolder locker(exec);
267     reject(*exec, jsString(exec, result));
268 }
269
270 }
271
272 #endif // JSDOMPromise_h