Source code

Revision control

Other Tools

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 "vm/HelperThreads.h" // js::OffThreadParsingMustWaitForGC, js::StartOffThreadParseScript
21
#include "vm/JSContext.h" // JSContext
22
#include "vm/Runtime.h" // js::CanUseExtraThreads
23
24
using namespace js;
25
26
using mozilla::Utf8Unit;
27
28
using JS::ReadOnlyCompileOptions;
29
30
enum class OffThread { Compile, Decode, DecodeBinAST };
31
32
static bool CanDoOffThread(JSContext* cx, const ReadOnlyCompileOptions& options,
33
size_t length, OffThread what) {
34
static const size_t TINY_LENGTH = 5 * 1000;
35
static const size_t HUGE_SRC_LENGTH = 100 * 1000;
36
static const size_t HUGE_BC_LENGTH = 367 * 1000;
37
static const size_t HUGE_BINAST_LENGTH = 70 * 1000;
38
39
// These are heuristics which the caller may choose to ignore (e.g., for
40
// testing purposes).
41
if (!options.forceAsync) {
42
// Compiling off the main thread inolves creating a new Zone and other
43
// significant overheads. Don't bother if the script is tiny.
44
if (length < TINY_LENGTH) {
45
return false;
46
}
47
48
// If the parsing task would have to wait for GC to complete, it'll probably
49
// be faster to just start it synchronously on the main thread unless the
50
// script is huge.
51
if (OffThreadParsingMustWaitForGC(cx->runtime())) {
52
if (what == OffThread::Compile && length < HUGE_SRC_LENGTH) {
53
return false;
54
}
55
if (what == OffThread::Decode && length < HUGE_BC_LENGTH) {
56
return false;
57
}
58
if (what == OffThread::DecodeBinAST && length < HUGE_BINAST_LENGTH) {
59
return false;
60
}
61
}
62
}
63
64
return cx->runtime()->canUseParallelParsing() && CanUseExtraThreads();
65
}
66
67
JS_PUBLIC_API bool JS::CanCompileOffThread(
68
JSContext* cx, const ReadOnlyCompileOptions& options, size_t length) {
69
return CanDoOffThread(cx, options, length, OffThread::Compile);
70
}
71
72
JS_PUBLIC_API bool JS::CompileOffThread(JSContext* cx,
73
const ReadOnlyCompileOptions& options,
74
JS::SourceText<char16_t>& srcBuf,
75
OffThreadCompileCallback callback,
76
void* callbackData) {
77
MOZ_ASSERT(CanCompileOffThread(cx, options, srcBuf.length()));
78
return StartOffThreadParseScript(cx, options, srcBuf, callback, callbackData);
79
}
80
81
JS_PUBLIC_API bool JS::CompileOffThread(JSContext* cx,
82
const ReadOnlyCompileOptions& options,
83
JS::SourceText<Utf8Unit>& srcBuf,
84
OffThreadCompileCallback callback,
85
void* callbackData) {
86
MOZ_ASSERT(CanCompileOffThread(cx, options, srcBuf.length()));
87
return StartOffThreadParseScript(cx, options, srcBuf, callback, callbackData);
88
}
89
90
JS_PUBLIC_API JSScript* JS::FinishOffThreadScript(JSContext* cx,
91
JS::OffThreadToken* token) {
92
MOZ_ASSERT(cx);
93
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
94
return HelperThreadState().finishScriptParseTask(cx, token);
95
}
96
97
JS_PUBLIC_API void JS::CancelOffThreadScript(JSContext* cx,
98
JS::OffThreadToken* token) {
99
MOZ_ASSERT(cx);
100
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
101
HelperThreadState().cancelParseTask(cx->runtime(), ParseTaskKind::Script,
102
token);
103
}
104
105
JS_PUBLIC_API bool JS::CompileOffThreadModule(
106
JSContext* cx, const ReadOnlyCompileOptions& options,
107
JS::SourceText<char16_t>& srcBuf, OffThreadCompileCallback callback,
108
void* callbackData) {
109
MOZ_ASSERT(CanCompileOffThread(cx, options, srcBuf.length()));
110
return StartOffThreadParseModule(cx, options, srcBuf, callback, callbackData);
111
}
112
113
JS_PUBLIC_API bool JS::CompileOffThreadModule(
114
JSContext* cx, const ReadOnlyCompileOptions& options,
115
JS::SourceText<Utf8Unit>& srcBuf, OffThreadCompileCallback callback,
116
void* callbackData) {
117
MOZ_ASSERT(CanCompileOffThread(cx, options, srcBuf.length()));
118
return StartOffThreadParseModule(cx, options, srcBuf, callback, callbackData);
119
}
120
121
JS_PUBLIC_API JSObject* JS::FinishOffThreadModule(JSContext* cx,
122
JS::OffThreadToken* token) {
123
MOZ_ASSERT(cx);
124
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
125
return HelperThreadState().finishModuleParseTask(cx, token);
126
}
127
128
JS_PUBLIC_API void JS::CancelOffThreadModule(JSContext* cx,
129
JS::OffThreadToken* token) {
130
MOZ_ASSERT(cx);
131
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
132
HelperThreadState().cancelParseTask(cx->runtime(), ParseTaskKind::Module,
133
token);
134
}
135
136
JS_PUBLIC_API bool JS::CanDecodeOffThread(JSContext* cx,
137
const ReadOnlyCompileOptions& options,
138
size_t length) {
139
return CanDoOffThread(cx, options, length, OffThread::Decode);
140
}
141
142
JS_PUBLIC_API bool JS::DecodeOffThreadScript(
143
JSContext* cx, const ReadOnlyCompileOptions& options,
144
mozilla::Vector<uint8_t>& buffer /* TranscodeBuffer& */, size_t cursor,
145
OffThreadCompileCallback callback, void* callbackData) {
146
JS::TranscodeRange range(buffer.begin() + cursor, buffer.length() - cursor);
147
MOZ_ASSERT(CanDecodeOffThread(cx, options, range.length()));
148
return StartOffThreadDecodeScript(cx, options, range, callback, callbackData);
149
}
150
151
JS_PUBLIC_API bool JS::DecodeOffThreadScript(
152
JSContext* cx, const ReadOnlyCompileOptions& options,
153
const mozilla::Range<uint8_t>& range /* TranscodeRange& */,
154
OffThreadCompileCallback callback, void* callbackData) {
155
MOZ_ASSERT(CanDecodeOffThread(cx, options, range.length()));
156
return StartOffThreadDecodeScript(cx, options, range, callback, callbackData);
157
}
158
159
JS_PUBLIC_API JSScript* JS::FinishOffThreadScriptDecoder(
160
JSContext* cx, JS::OffThreadToken* token) {
161
MOZ_ASSERT(cx);
162
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
163
return HelperThreadState().finishScriptDecodeTask(cx, token);
164
}
165
166
JS_PUBLIC_API void JS::CancelOffThreadScriptDecoder(JSContext* cx,
167
JS::OffThreadToken* token) {
168
MOZ_ASSERT(cx);
169
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
170
HelperThreadState().cancelParseTask(cx->runtime(),
171
ParseTaskKind::ScriptDecode, token);
172
}
173
174
JS_PUBLIC_API bool JS::DecodeMultiOffThreadScripts(
175
JSContext* cx, const ReadOnlyCompileOptions& options,
176
TranscodeSources& sources, OffThreadCompileCallback callback,
177
void* callbackData) {
178
#ifdef DEBUG
179
size_t length = 0;
180
for (auto& source : sources) {
181
length += source.range.length();
182
}
183
MOZ_ASSERT(CanCompileOffThread(cx, options, length));
184
#endif
185
return StartOffThreadDecodeMultiScripts(cx, options, sources, callback,
186
callbackData);
187
}
188
189
JS_PUBLIC_API bool JS::FinishMultiOffThreadScriptsDecoder(
190
JSContext* cx, JS::OffThreadToken* token,
191
MutableHandle<ScriptVector> scripts) {
192
MOZ_ASSERT(cx);
193
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
194
return HelperThreadState().finishMultiScriptsDecodeTask(cx, token, scripts);
195
}
196
197
JS_PUBLIC_API void JS::CancelMultiOffThreadScriptsDecoder(
198
JSContext* cx, JS::OffThreadToken* token) {
199
MOZ_ASSERT(cx);
200
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
201
HelperThreadState().cancelParseTask(cx->runtime(),
202
ParseTaskKind::MultiScriptsDecode, token);
203
}
204
205
JS_PUBLIC_API bool JS::CanDecodeBinASTOffThread(
206
JSContext* cx, const ReadOnlyCompileOptions& options, size_t length) {
207
#ifdef JS_BUILD_BINAST
208
return CanDoOffThread(cx, options, length, OffThread::DecodeBinAST);
209
#else // !JS_BUILD_BINAST
210
return false;
211
#endif // JS_BUILD_BINAST
212
}
213
214
JS_PUBLIC_API bool JS::DecodeBinASTOffThread(
215
JSContext* cx, const ReadOnlyCompileOptions& options, const uint8_t* buf,
216
size_t length, JS::BinASTFormat format, OffThreadCompileCallback callback,
217
void* callbackData) {
218
#ifdef JS_BUILD_BINAST
219
return StartOffThreadDecodeBinAST(cx, options, buf, length, format, callback,
220
callbackData);
221
#else // !JS_BUILD_BINAST
222
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
223
JSMSG_SUPPORT_NOT_ENABLED, "BinAST");
224
return false;
225
#endif // JS_BUILD_BINAST
226
}
227
228
JS_PUBLIC_API JSScript* JS::FinishOffThreadBinASTDecode(
229
JSContext* cx, JS::OffThreadToken* token) {
230
#ifdef JS_BUILD_BINAST
231
MOZ_ASSERT(cx);
232
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
233
return HelperThreadState().finishBinASTDecodeTask(cx, token);
234
#else // !JS_BUILD_BINAST
235
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
236
JSMSG_SUPPORT_NOT_ENABLED, "BinAST");
237
return nullptr;
238
#endif // JS_BUILD_BINAST
239
}