OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2017 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "modules/wasm/WasmResponseExtensions.h" | |
6 | |
7 #include "bindings/core/v8/ExceptionState.h" | |
8 #include "bindings/core/v8/ScriptPromise.h" | |
9 #include "bindings/core/v8/ScriptPromiseResolver.h" | |
10 #include "bindings/core/v8/ScriptState.h" | |
11 #include "bindings/modules/v8/V8Response.h" | |
12 #include "modules/fetch/BodyStreamBuffer.h" | |
13 #include "modules/fetch/FetchDataLoader.h" | |
14 #include "platform/heap/Handle.h" | |
15 #include "wtf/RefPtr.h" | |
16 | |
17 namespace blink { | |
18 | |
19 namespace { | |
20 | |
21 class FetchDataLoaderAsWasmModule final : public FetchDataLoader, | |
22 public BytesConsumer::Client { | |
23 USING_GARBAGE_COLLECTED_MIXIN(FetchDataLoaderAsWasmModule); | |
24 | |
25 public: | |
26 FetchDataLoaderAsWasmModule(ScriptPromiseResolver* resolver, | |
27 ScriptState* scriptState) | |
28 : m_resolver(resolver), | |
29 m_builder(scriptState->isolate()), | |
30 m_scriptState(scriptState) {} | |
31 | |
32 void start(BytesConsumer* consumer, | |
33 FetchDataLoader::Client* client) override { | |
34 DCHECK(!m_consumer); | |
35 DCHECK(!m_client); | |
36 m_client = client; | |
37 m_consumer = consumer; | |
38 m_consumer->setClient(this); | |
39 onStateChange(); | |
40 } | |
41 | |
42 void onStateChange() override { | |
43 while (true) { | |
44 // buffer is allocated by beginRead and de-allocated by endRead. | |
haraken
2017/04/07 10:22:40
allocated / deallocated sounds misleading to me. b
Mircea Trofin
2017/04/07 15:33:08
True - rephrased.
| |
45 const char* buffer = nullptr; | |
46 size_t available = 0; | |
47 BytesConsumer::Result result = m_consumer->beginRead(&buffer, &available); | |
48 | |
49 if (result == BytesConsumer::Result::ShouldWait) | |
50 return; | |
51 if (result == BytesConsumer::Result::Ok) { | |
52 if (available > 0) { | |
53 DCHECK_NE(buffer, nullptr); | |
54 m_builder.OnBytesReceived(reinterpret_cast<const uint8_t*>(buffer), | |
55 available); | |
56 } | |
57 result = m_consumer->endRead(available); | |
58 } | |
59 switch (result) { | |
60 case BytesConsumer::Result::ShouldWait: | |
61 NOTREACHED(); | |
62 return; | |
63 case BytesConsumer::Result::Ok: { | |
64 break; | |
65 } | |
66 case BytesConsumer::Result::Done: { | |
67 v8::Isolate* isolate = m_scriptState->isolate(); | |
68 ScriptState::Scope scope(m_scriptState.get()); | |
69 v8::TryCatch trycatch(isolate); | |
70 v8::Local<v8::WasmCompiledModule> module; | |
71 if (m_builder.Finish().ToLocal(&module)) { | |
72 DCHECK(!trycatch.HasCaught()); | |
73 ScriptValue scriptValue(m_scriptState.get(), module); | |
74 m_resolver->resolve(scriptValue); | |
75 } else { | |
76 DCHECK(trycatch.HasCaught()); | |
77 m_resolver->reject(trycatch.Exception()); | |
78 trycatch.Reset(); | |
haraken
2017/04/07 10:22:40
I don't fully understand why you need to reset it.
Mircea Trofin
2017/04/07 15:33:09
Granted, the destructor of TryCatch clears the exc
| |
79 } | |
80 m_client->didFetchDataLoadedCustomFormat(); | |
81 return; | |
82 } | |
83 case BytesConsumer::Result::Error: { | |
84 // TODO(mtrofin): do we need an abort on the wasm side? | |
85 // Something like "m_outStream->abort()" maybe? | |
86 return rejectPromise(); | |
87 } | |
88 } | |
89 } | |
90 } | |
91 | |
92 void cancel() override { | |
93 m_consumer->cancel(); | |
94 return rejectPromise(); | |
95 } | |
96 | |
97 DEFINE_INLINE_TRACE() { | |
98 visitor->trace(m_consumer); | |
99 visitor->trace(m_resolver); | |
100 visitor->trace(m_client); | |
101 FetchDataLoader::trace(visitor); | |
102 BytesConsumer::Client::trace(visitor); | |
103 } | |
104 | |
105 private: | |
106 // TODO(mtrofin): replace with spec-ed error types, once spec clarifies | |
107 // what they are. | |
108 void rejectPromise() { | |
109 m_resolver->reject(V8ThrowException::createTypeError( | |
110 m_scriptState->isolate(), "Could not download wasm module")); | |
111 } | |
112 Member<BytesConsumer> m_consumer; | |
113 Member<ScriptPromiseResolver> m_resolver; | |
114 Member<FetchDataLoader::Client> m_client; | |
115 v8::WasmModuleObjectBuilder m_builder; | |
116 const RefPtr<ScriptState> m_scriptState; | |
117 }; | |
118 | |
119 // TODO(mtrofin): WasmDataLoaderClient is necessary so we may provide an | |
120 // argument to BodyStreamBuffer::startLoading, however, it fulfills | |
121 // a very small role. Consider refactoring to avoid it. | |
122 class WasmDataLoaderClient final | |
123 : public GarbageCollectedFinalized<WasmDataLoaderClient>, | |
124 public FetchDataLoader::Client { | |
125 WTF_MAKE_NONCOPYABLE(WasmDataLoaderClient); | |
126 USING_GARBAGE_COLLECTED_MIXIN(WasmDataLoaderClient); | |
127 | |
128 public: | |
129 explicit WasmDataLoaderClient() {} | |
130 void didFetchDataLoadedCustomFormat() override {} | |
131 void didFetchDataLoadFailed() override { NOTREACHED(); } | |
132 }; | |
133 | |
134 // This callback may be entered as a promise is resolved, or directly | |
135 // from the overload callback. | |
136 // See | |
137 // https://github.com/WebAssembly/design/blob/master/Web.md#webassemblycompile | |
138 void compileFromResponseCallback( | |
139 const v8::FunctionCallbackInfo<v8::Value>& args) { | |
140 ExceptionState exceptionState(args.GetIsolate(), | |
141 ExceptionState::ExecutionContext, "WebAssembly", | |
142 "compile"); | |
143 ExceptionToRejectPromiseScope rejectPromiseScope(args, exceptionState); | |
144 | |
145 ScriptState* scriptState = ScriptState::forReceiverObject(args); | |
146 if (!scriptState->getExecutionContext()) { | |
147 v8SetReturnValue(args, ScriptPromise().v8Value()); | |
148 return; | |
149 } | |
150 | |
151 if (args.Length() < 1 || !args[0]->IsObject() || | |
152 !V8Response::hasInstance(args[0], args.GetIsolate())) { | |
153 v8SetReturnValue( | |
154 args, ScriptPromise::reject( | |
155 scriptState, V8ThrowException::createTypeError( | |
156 scriptState->isolate(), | |
157 "Promise argument must be called with a " | |
158 "Promise<Response> object")) | |
159 .v8Value()); | |
160 return; | |
161 } | |
162 | |
163 Response* response = V8Response::toImpl(v8::Local<v8::Object>::Cast(args[0])); | |
164 ScriptPromise promise; | |
165 if (response->isBodyLocked() || response->bodyUsed()) { | |
166 promise = ScriptPromise::reject( | |
167 scriptState, | |
168 V8ThrowException::createTypeError( | |
169 scriptState->isolate(), | |
170 "Cannot compile WebAssembly.Module from an already read Response")); | |
171 } else { | |
172 ScriptPromiseResolver* resolver = | |
173 ScriptPromiseResolver::create(scriptState); | |
174 if (response->bodyBuffer()) { | |
175 promise = resolver->promise(); | |
176 response->bodyBuffer()->startLoading( | |
177 new FetchDataLoaderAsWasmModule(resolver, scriptState), | |
178 new WasmDataLoaderClient()); | |
179 } else { | |
180 promise = ScriptPromise::reject( | |
181 scriptState, | |
182 V8ThrowException::createTypeError( | |
183 scriptState->isolate(), "Response object has a null body.")); | |
184 } | |
185 } | |
186 v8SetReturnValue(args, promise.v8Value()); | |
187 } | |
188 | |
189 // See https://crbug.com/708238 for tracking avoiding the hand-generated code. | |
190 bool wasmCompileOverload(const v8::FunctionCallbackInfo<v8::Value>& args) { | |
191 if (args.Length() < 1 || !args[0]->IsObject()) | |
192 return false; | |
193 | |
194 if (!args[0]->IsPromise() && | |
195 !V8Response::hasInstance(args[0], args.GetIsolate())) | |
196 return false; | |
197 | |
198 v8::Isolate* isolate = args.GetIsolate(); | |
199 ScriptState* scriptState = ScriptState::forReceiverObject(args); | |
200 | |
201 v8::Local<v8::Function> compileCallback = | |
202 v8::Function::New(isolate, compileFromResponseCallback); | |
203 | |
yhirano
2017/04/07 07:39:47
V8SetReturnValue(args, ScriptPromise::cast(scriptS
Mircea Trofin
2017/04/07 15:33:08
Nice! Thanks!
| |
204 ScriptPromiseResolver* scriptPromiseResolver = | |
205 ScriptPromiseResolver::create(scriptState); | |
206 // treat either case of parameter as | |
207 // Promise.resolve(parameter) | |
208 // as per https://www.w3.org/2001/tag/doc/promises-guide#resolve-arguments | |
209 | |
210 // Ending with: | |
211 // return Promise.resolve(parameter).then(compileCallback); | |
212 ScriptPromise parameterAsPromise = scriptPromiseResolver->promise(); | |
213 v8SetReturnValue(args, parameterAsPromise.then(compileCallback).v8Value()); | |
214 | |
215 // resolve the first parameter promise. | |
216 scriptPromiseResolver->resolve(ScriptValue::from(scriptState, args[0])); | |
217 return true; | |
218 } | |
219 | |
220 } // namespace | |
221 | |
222 void WasmResponseExtensions::initialize(v8::Isolate* isolate) { | |
223 isolate->SetWasmCompileCallback(wasmCompileOverload); | |
224 } | |
225 | |
226 } // namespace blink | |
OLD | NEW |