1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* Structures and functions for transcoding compiled scripts and functions to
* and from memory.
*/
#ifndef js_Transcoding_h
#define js_Transcoding_h
#include "mozilla/Range.h" // mozilla::Range
#include "mozilla/Vector.h" // mozilla::Vector
#include <stddef.h> // size_t
#include <stdint.h> // uint8_t, uint32_t
#include "js/TypeDecls.h"
namespace JS {
class ReadOnlyCompileOptions;
using TranscodeBuffer = mozilla::Vector<uint8_t>;
using TranscodeRange = mozilla::Range<uint8_t>;
struct TranscodeSource final {
TranscodeSource(const TranscodeRange& range_, const char* file, uint32_t line)
: range(range_), filename(file), lineno(line) {}
const TranscodeRange range;
const char* filename;
const uint32_t lineno;
};
using TranscodeSources = mozilla::Vector<TranscodeSource>;
enum TranscodeResult : uint8_t {
// Successful encoding / decoding.
TranscodeResult_Ok = 0,
// A warning message, is set to the message out-param.
TranscodeResult_Failure = 0x10,
TranscodeResult_Failure_BadBuildId = TranscodeResult_Failure | 0x1,
TranscodeResult_Failure_RunOnceNotSupported = TranscodeResult_Failure | 0x2,
TranscodeResult_Failure_AsmJSNotSupported = TranscodeResult_Failure | 0x3,
TranscodeResult_Failure_BadDecode = TranscodeResult_Failure | 0x4,
TranscodeResult_Failure_WrongCompileOption = TranscodeResult_Failure | 0x5,
TranscodeResult_Failure_NotInterpretedFun = TranscodeResult_Failure | 0x6,
// There is a pending exception on the context.
TranscodeResult_Throw = 0x20
};
static constexpr size_t BytecodeOffsetAlignment = 4;
static_assert(BytecodeOffsetAlignment <= alignof(std::max_align_t),
"Alignment condition requires a custom allocator.");
// Align the bytecode offset for transcoding for the requirement.
inline size_t AlignTranscodingBytecodeOffset(size_t offset) {
size_t extra = offset % BytecodeOffsetAlignment;
if (extra == 0) {
return offset;
}
size_t padding = BytecodeOffsetAlignment - extra;
return offset + padding;
}
inline bool IsTranscodingBytecodeOffsetAligned(size_t offset) {
return offset % BytecodeOffsetAlignment == 0;
}
inline bool IsTranscodingBytecodeAligned(void* offset) {
return IsTranscodingBytecodeOffsetAligned(size_t(offset));
}
// Encode JSScript into the buffer.
//
// If the `buffer` isn't empty, the start of the `buffer` should meet
// IsTranscodingBytecodeAligned, and the length should meet
// IsTranscodingBytecodeOffsetAligned.
//
// NOTE: As long as IsTranscodingBytecodeOffsetAligned is met, that means
// there's JS::BytecodeOffsetAlignment+extra bytes in the buffer,
// IsTranscodingBytecodeAligned should be guaranteed to meet by
// malloc, used by MallocAllocPolicy in mozilla::Vector.
extern JS_PUBLIC_API TranscodeResult EncodeScript(JSContext* cx,
TranscodeBuffer& buffer,
Handle<JSScript*> script);
// Decode JSScript from the buffer.
//
// The start of `buffer` and `cursorIndex` should meet
// IsTranscodingBytecodeAligned and IsTranscodingBytecodeOffsetAligned.
// (This should be handled while encoding).
extern JS_PUBLIC_API TranscodeResult
DecodeScript(JSContext* cx, const ReadOnlyCompileOptions& options,
TranscodeBuffer& buffer, MutableHandle<JSScript*> scriptp,
size_t cursorIndex = 0);
// Decode JSScript from the range.
//
// The start of `range` should meet IsTranscodingBytecodeAligned and
// IsTranscodingBytecodeOffsetAligned.
// (This should be handled while encoding).
extern JS_PUBLIC_API TranscodeResult
DecodeScript(JSContext* cx, const ReadOnlyCompileOptions& options,
const TranscodeRange& range, MutableHandle<JSScript*> scriptp);
// If js::UseOffThreadParseGlobal is true, decode JSScript from the buffer.
//
// If js::UseOffThreadParseGlobal is false, decode CompilationStencil from the
// buffer and instantiate JSScript from it.
//
// options.useOffThreadParseGlobal should match JS::SetUseOffThreadParseGlobal.
//
// The start of `buffer` and `cursorIndex` should meet
// IsTranscodingBytecodeAligned and IsTranscodingBytecodeOffsetAligned.
// (This should be handled while encoding).
extern JS_PUBLIC_API TranscodeResult DecodeScriptMaybeStencil(
JSContext* cx, const ReadOnlyCompileOptions& options,
TranscodeBuffer& buffer, MutableHandle<JSScript*> scriptp,
size_t cursorIndex = 0);
// If js::UseOffThreadParseGlobal is true, decode JSScript from the buffer.
//
// If js::UseOffThreadParseGlobal is false, decode CompilationStencil from the
// buffer and instantiate JSScript from it.
//
// And then register an encoder on its script source, such that all functions
// can be encoded as they are parsed. This strategy is used to avoid blocking
// the main thread in a non-interruptible way.
//
// See also JS::FinishIncrementalEncoding.
//
// options.useOffThreadParseGlobal should match JS::SetUseOffThreadParseGlobal.
//
// The start of `buffer` and `cursorIndex` should meet
// IsTranscodingBytecodeAligned and IsTranscodingBytecodeOffsetAligned.
// (This should be handled while encoding).
extern JS_PUBLIC_API TranscodeResult DecodeScriptAndStartIncrementalEncoding(
JSContext* cx, const ReadOnlyCompileOptions& options,
TranscodeBuffer& buffer, MutableHandle<JSScript*> scriptp,
size_t cursorIndex = 0);
// Finish incremental encoding started by one of:
// * JS::CompileAndStartIncrementalEncoding
// * JS::FinishOffThreadScriptAndStartIncrementalEncoding
// * JS::DecodeScriptAndStartIncrementalEncoding
//
// The |script| argument of |FinishIncrementalEncoding| should be the top-level
// script returned from one of the above.
//
// The |buffer| argument of |FinishIncrementalEncoding| is used for appending
// the encoded bytecode into the buffer. If any of these functions failed, the
// content of |buffer| would be undefined.
//
// If js::UseOffThreadParseGlobal is true, |buffer| contains encoded JSScript.
//
// If js::UseOffThreadParseGlobal is false, |buffer| contains encoded
// CompilationStencil.
//
// If the `buffer` isn't empty, the start of the `buffer` should meet
// IsTranscodingBytecodeAligned, and the length should meet
// IsTranscodingBytecodeOffsetAligned.
//
// NOTE: As long as IsTranscodingBytecodeOffsetAligned is met, that means
// there's JS::BytecodeOffsetAlignment+extra bytes in the buffer,
// IsTranscodingBytecodeAligned should be guaranteed to meet by
// malloc, used by MallocAllocPolicy in mozilla::Vector.
extern JS_PUBLIC_API bool FinishIncrementalEncoding(JSContext* cx,
Handle<JSScript*> script,
TranscodeBuffer& buffer);
// Check if the compile options and script's flag matches.
//
// JS::DecodeScript* and JS::DecodeOffThreadScript internally check this.
//
// JS::DecodeMultiOffThreadScripts checks some options shared across multiple
// scripts. Caller is responsible for checking each script with this API when
// using the decoded script instead of compiling a new script wiht the given
// options.
extern JS_PUBLIC_API bool CheckCompileOptionsMatch(
const ReadOnlyCompileOptions& options, JSScript* script);
} // namespace JS
#endif /* js_Transcoding_h */
|