1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "js/OffThreadScriptCompilation.h"
8
9 #include "mozilla/Assertions.h" // MOZ_ASSERT
10 #include "mozilla/Range.h" // mozilla::Range
11 #include "mozilla/Utf8.h" // mozilla::Utf8Unit
12 #include "mozilla/Vector.h" // mozilla::Vector
13
14 #include <stddef.h> // size_t
15
16 #include "jspubtd.h" // js::CurrentThreadCanAccessRuntime
17 #include "jstypes.h" // JS_PUBLIC_API
18
19 #include "js/CompileOptions.h" // JS::ReadOnlyCompileOptions
20 #include "js/experimental/JSStencil.h" // JS::CompileToStencilOffThread, JS::FinishOffThreadCompileToStencil
21 #include "js/SourceText.h" // JS::SourceText
22 #include "vm/HelperThreadState.h" // js::OffThreadParsingMustWaitForGC, js::StartOffThreadParseScript
23 #include "vm/JSContext.h" // JSContext
24 #include "vm/Runtime.h" // js::CanUseExtraThreads
25
26 using namespace js;
27
28 using mozilla::Utf8Unit;
29
30 using JS::ReadOnlyCompileOptions;
31
32 enum class OffThread { Compile, Decode };
33
CanDoOffThread(JSContext * cx,const ReadOnlyCompileOptions & options,size_t length,OffThread what)34 static bool CanDoOffThread(JSContext* cx, const ReadOnlyCompileOptions& options,
35 size_t length, OffThread what) {
36 static const size_t TINY_LENGTH = 5 * 1000;
37 static const size_t HUGE_SRC_LENGTH = 100 * 1000;
38 static const size_t HUGE_BC_LENGTH = 367 * 1000;
39
40 // These are heuristics which the caller may choose to ignore (e.g., for
41 // testing purposes).
42 if (!options.forceAsync) {
43 // Compiling off the main thread inolves creating a new Zone and other
44 // significant overheads. Don't bother if the script is tiny.
45 if (length < TINY_LENGTH) {
46 return false;
47 }
48
49 // If the parsing task would have to wait for GC to complete, it'll probably
50 // be faster to just start it synchronously on the main thread unless the
51 // script is huge.
52 //
53 // NOTE: JS::DecodeMultiOffThreadScript does not use this API so we don't
54 // have to worry about it still using off-thread parse global.
55 bool mustWait = options.useOffThreadParseGlobal &&
56 OffThreadParsingMustWaitForGC(cx->runtime());
57 if (mustWait) {
58 if (what == OffThread::Compile && length < HUGE_SRC_LENGTH) {
59 return false;
60 }
61 if (what == OffThread::Decode && length < HUGE_BC_LENGTH) {
62 return false;
63 }
64 }
65 }
66
67 return cx->runtime()->canUseParallelParsing() && CanUseExtraThreads();
68 }
69
CanCompileOffThread(JSContext * cx,const ReadOnlyCompileOptions & options,size_t length)70 JS_PUBLIC_API bool JS::CanCompileOffThread(
71 JSContext* cx, const ReadOnlyCompileOptions& options, size_t length) {
72 return CanDoOffThread(cx, options, length, OffThread::Compile);
73 }
74
CompileOffThread(JSContext * cx,const ReadOnlyCompileOptions & options,JS::SourceText<char16_t> & srcBuf,OffThreadCompileCallback callback,void * callbackData)75 JS_PUBLIC_API JS::OffThreadToken* JS::CompileOffThread(
76 JSContext* cx, const ReadOnlyCompileOptions& options,
77 JS::SourceText<char16_t>& srcBuf, OffThreadCompileCallback callback,
78 void* callbackData) {
79 MOZ_ASSERT(CanCompileOffThread(cx, options, srcBuf.length()));
80 return StartOffThreadParseScript(cx, options, srcBuf, callback, callbackData);
81 }
82
CompileOffThread(JSContext * cx,const ReadOnlyCompileOptions & options,JS::SourceText<Utf8Unit> & srcBuf,OffThreadCompileCallback callback,void * callbackData)83 JS_PUBLIC_API JS::OffThreadToken* JS::CompileOffThread(
84 JSContext* cx, const ReadOnlyCompileOptions& options,
85 JS::SourceText<Utf8Unit>& srcBuf, OffThreadCompileCallback callback,
86 void* callbackData) {
87 MOZ_ASSERT(CanCompileOffThread(cx, options, srcBuf.length()));
88 return StartOffThreadParseScript(cx, options, srcBuf, callback, callbackData);
89 }
90
FinishOffThreadScript(JSContext * cx,JS::OffThreadToken * token)91 JS_PUBLIC_API JSScript* JS::FinishOffThreadScript(JSContext* cx,
92 JS::OffThreadToken* token) {
93 MOZ_ASSERT(cx);
94 MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
95 return HelperThreadState().finishScriptParseTask(cx, token);
96 }
97
CompileToStencilOffThread(JSContext * cx,const ReadOnlyCompileOptions & options,JS::SourceText<char16_t> & srcBuf,OffThreadCompileCallback callback,void * callbackData)98 JS_PUBLIC_API JS::OffThreadToken* JS::CompileToStencilOffThread(
99 JSContext* cx, const ReadOnlyCompileOptions& options,
100 JS::SourceText<char16_t>& srcBuf, OffThreadCompileCallback callback,
101 void* callbackData) {
102 MOZ_ASSERT(CanCompileOffThread(cx, options, srcBuf.length()));
103 return StartOffThreadCompileToStencil(cx, options, srcBuf, callback,
104 callbackData);
105 }
106
CompileToStencilOffThread(JSContext * cx,const ReadOnlyCompileOptions & options,JS::SourceText<Utf8Unit> & srcBuf,OffThreadCompileCallback callback,void * callbackData)107 JS_PUBLIC_API JS::OffThreadToken* JS::CompileToStencilOffThread(
108 JSContext* cx, const ReadOnlyCompileOptions& options,
109 JS::SourceText<Utf8Unit>& srcBuf, OffThreadCompileCallback callback,
110 void* callbackData) {
111 MOZ_ASSERT(CanCompileOffThread(cx, options, srcBuf.length()));
112 return StartOffThreadCompileToStencil(cx, options, srcBuf, callback,
113 callbackData);
114 }
115
FinishOffThreadCompileToStencil(JSContext * cx,JS::OffThreadToken * token)116 JS_PUBLIC_API RefPtr<JS::Stencil> JS::FinishOffThreadCompileToStencil(
117 JSContext* cx, JS::OffThreadToken* token) {
118 MOZ_ASSERT(cx);
119 MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
120 auto stencil = HelperThreadState().finishCompileToStencilTask(cx, token);
121 return do_AddRef(stencil.release());
122 }
123
FinishOffThreadScriptAndStartIncrementalEncoding(JSContext * cx,JS::OffThreadToken * token)124 JS_PUBLIC_API JSScript* JS::FinishOffThreadScriptAndStartIncrementalEncoding(
125 JSContext* cx, JS::OffThreadToken* token) {
126 MOZ_ASSERT(cx);
127 MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
128 return HelperThreadState().finishScriptParseTask(cx, token,
129 StartEncoding::Yes);
130 }
131
CancelOffThreadScript(JSContext * cx,JS::OffThreadToken * token)132 JS_PUBLIC_API void JS::CancelOffThreadScript(JSContext* cx,
133 JS::OffThreadToken* token) {
134 MOZ_ASSERT(cx);
135 MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
136 HelperThreadState().cancelParseTask(cx->runtime(), ParseTaskKind::Script,
137 token);
138 }
139
CancelOffThreadCompileToStencil(JSContext * cx,JS::OffThreadToken * token)140 JS_PUBLIC_API void JS::CancelOffThreadCompileToStencil(
141 JSContext* cx, JS::OffThreadToken* token) {
142 MOZ_ASSERT(cx);
143 MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
144 HelperThreadState().cancelParseTask(cx->runtime(),
145 ParseTaskKind::ScriptStencil, token);
146 }
147
CompileOffThreadModule(JSContext * cx,const ReadOnlyCompileOptions & options,JS::SourceText<char16_t> & srcBuf,OffThreadCompileCallback callback,void * callbackData)148 JS_PUBLIC_API JS::OffThreadToken* JS::CompileOffThreadModule(
149 JSContext* cx, const ReadOnlyCompileOptions& options,
150 JS::SourceText<char16_t>& srcBuf, OffThreadCompileCallback callback,
151 void* callbackData) {
152 MOZ_ASSERT(CanCompileOffThread(cx, options, srcBuf.length()));
153 return StartOffThreadParseModule(cx, options, srcBuf, callback, callbackData);
154 }
155
CompileOffThreadModule(JSContext * cx,const ReadOnlyCompileOptions & options,JS::SourceText<Utf8Unit> & srcBuf,OffThreadCompileCallback callback,void * callbackData)156 JS_PUBLIC_API JS::OffThreadToken* JS::CompileOffThreadModule(
157 JSContext* cx, const ReadOnlyCompileOptions& options,
158 JS::SourceText<Utf8Unit>& srcBuf, OffThreadCompileCallback callback,
159 void* callbackData) {
160 MOZ_ASSERT(CanCompileOffThread(cx, options, srcBuf.length()));
161 return StartOffThreadParseModule(cx, options, srcBuf, callback, callbackData);
162 }
163
FinishOffThreadModule(JSContext * cx,JS::OffThreadToken * token)164 JS_PUBLIC_API JSObject* JS::FinishOffThreadModule(JSContext* cx,
165 JS::OffThreadToken* token) {
166 MOZ_ASSERT(cx);
167 MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
168 return HelperThreadState().finishModuleParseTask(cx, token);
169 }
170
CancelOffThreadModule(JSContext * cx,JS::OffThreadToken * token)171 JS_PUBLIC_API void JS::CancelOffThreadModule(JSContext* cx,
172 JS::OffThreadToken* token) {
173 MOZ_ASSERT(cx);
174 MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
175 HelperThreadState().cancelParseTask(cx->runtime(), ParseTaskKind::Module,
176 token);
177 }
178
CanDecodeOffThread(JSContext * cx,const ReadOnlyCompileOptions & options,size_t length)179 JS_PUBLIC_API bool JS::CanDecodeOffThread(JSContext* cx,
180 const ReadOnlyCompileOptions& options,
181 size_t length) {
182 return CanDoOffThread(cx, options, length, OffThread::Decode);
183 }
184
DecodeOffThreadScript(JSContext * cx,const ReadOnlyCompileOptions & options,mozilla::Vector<uint8_t> & buffer,size_t cursor,OffThreadCompileCallback callback,void * callbackData)185 JS_PUBLIC_API JS::OffThreadToken* JS::DecodeOffThreadScript(
186 JSContext* cx, const ReadOnlyCompileOptions& options,
187 mozilla::Vector<uint8_t>& buffer /* TranscodeBuffer& */, size_t cursor,
188 OffThreadCompileCallback callback, void* callbackData) {
189 JS::TranscodeRange range(buffer.begin() + cursor, buffer.length() - cursor);
190 MOZ_ASSERT(CanDecodeOffThread(cx, options, range.length()));
191 return StartOffThreadDecodeScript(cx, options, range, callback, callbackData);
192 }
193
DecodeOffThreadScript(JSContext * cx,const ReadOnlyCompileOptions & options,const mozilla::Range<uint8_t> & range,OffThreadCompileCallback callback,void * callbackData)194 JS_PUBLIC_API JS::OffThreadToken* JS::DecodeOffThreadScript(
195 JSContext* cx, const ReadOnlyCompileOptions& options,
196 const mozilla::Range<uint8_t>& range /* TranscodeRange& */,
197 OffThreadCompileCallback callback, void* callbackData) {
198 MOZ_ASSERT(CanDecodeOffThread(cx, options, range.length()));
199 return StartOffThreadDecodeScript(cx, options, range, callback, callbackData);
200 }
201
FinishOffThreadScriptDecoder(JSContext * cx,JS::OffThreadToken * token)202 JS_PUBLIC_API JSScript* JS::FinishOffThreadScriptDecoder(
203 JSContext* cx, JS::OffThreadToken* token) {
204 MOZ_ASSERT(cx);
205 MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
206 return HelperThreadState().finishScriptDecodeTask(cx, token);
207 }
208
CancelOffThreadScriptDecoder(JSContext * cx,JS::OffThreadToken * token)209 JS_PUBLIC_API void JS::CancelOffThreadScriptDecoder(JSContext* cx,
210 JS::OffThreadToken* token) {
211 MOZ_ASSERT(cx);
212 MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
213 HelperThreadState().cancelParseTask(cx->runtime(),
214 ParseTaskKind::ScriptDecode, token);
215 }
216
DecodeMultiOffThreadScripts(JSContext * cx,const ReadOnlyCompileOptions & options,TranscodeSources & sources,OffThreadCompileCallback callback,void * callbackData)217 JS_PUBLIC_API JS::OffThreadToken* JS::DecodeMultiOffThreadScripts(
218 JSContext* cx, const ReadOnlyCompileOptions& options,
219 TranscodeSources& sources, OffThreadCompileCallback callback,
220 void* callbackData) {
221 #ifdef DEBUG
222 size_t length = 0;
223 for (auto& source : sources) {
224 length += source.range.length();
225 }
226 MOZ_ASSERT(CanCompileOffThread(cx, options, length));
227 #endif
228 return StartOffThreadDecodeMultiScripts(cx, options, sources, callback,
229 callbackData);
230 }
231
FinishMultiOffThreadScriptsDecoder(JSContext * cx,JS::OffThreadToken * token,MutableHandle<ScriptVector> scripts)232 JS_PUBLIC_API bool JS::FinishMultiOffThreadScriptsDecoder(
233 JSContext* cx, JS::OffThreadToken* token,
234 MutableHandle<ScriptVector> scripts) {
235 MOZ_ASSERT(cx);
236 MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
237 return HelperThreadState().finishMultiScriptsDecodeTask(cx, token, scripts);
238 }
239
CancelMultiOffThreadScriptsDecoder(JSContext * cx,JS::OffThreadToken * token)240 JS_PUBLIC_API void JS::CancelMultiOffThreadScriptsDecoder(
241 JSContext* cx, JS::OffThreadToken* token) {
242 MOZ_ASSERT(cx);
243 MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
244 HelperThreadState().cancelParseTask(cx->runtime(),
245 ParseTaskKind::MultiScriptsDecode, token);
246 }
247
248 namespace js {
249 bool gUseOffThreadParseGlobal = false;
250 } // namespace js
251
SetUseOffThreadParseGlobal(bool value)252 JS_PUBLIC_API void JS::SetUseOffThreadParseGlobal(bool value) {
253 gUseOffThreadParseGlobal = value;
254 }
255
UseOffThreadParseGlobal()256 bool js::UseOffThreadParseGlobal() { return gUseOffThreadParseGlobal; }
257