зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1243808 - Allow modules to be compiled off main thread r=shu
This commit is contained in:
Родитель
9d724cbe13
Коммит
cb077bbb33
|
@ -6,6 +6,8 @@
|
|||
|
||||
#include "builtin/ModuleObject.h"
|
||||
|
||||
#include "mozilla/DebugOnly.h"
|
||||
|
||||
#include "builtin/SelfHostingDefines.h"
|
||||
#include "frontend/ParseNode.h"
|
||||
#include "frontend/SharedContext.h"
|
||||
|
@ -109,7 +111,7 @@ GlobalObject::initImportEntryProto(JSContext* cx, Handle<GlobalObject*> global)
|
|||
}
|
||||
|
||||
/* static */ ImportEntryObject*
|
||||
ImportEntryObject::create(JSContext* cx,
|
||||
ImportEntryObject::create(ExclusiveContext* cx,
|
||||
HandleAtom moduleRequest,
|
||||
HandleAtom importName,
|
||||
HandleAtom localName)
|
||||
|
@ -181,7 +183,7 @@ StringOrNullValue(JSString* maybeString)
|
|||
}
|
||||
|
||||
/* static */ ExportEntryObject*
|
||||
ExportEntryObject::create(JSContext* cx,
|
||||
ExportEntryObject::create(ExclusiveContext* cx,
|
||||
HandleAtom maybeExportName,
|
||||
HandleAtom maybeModuleRequest,
|
||||
HandleAtom maybeImportName,
|
||||
|
@ -701,6 +703,62 @@ ModuleObject::initImportExportData(HandleArrayObject requestedModules,
|
|||
initReservedSlot(StarExportEntriesSlot, ObjectValue(*starExportEntries));
|
||||
}
|
||||
|
||||
static bool
|
||||
FreezeObjectProperty(JSContext* cx, HandleNativeObject obj, uint32_t slot)
|
||||
{
|
||||
RootedObject property(cx, &obj->getSlot(slot).toObject());
|
||||
return FreezeObject(cx, property);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
ModuleObject::FreezeArrayProperties(JSContext* cx, HandleModuleObject self)
|
||||
{
|
||||
return FreezeObjectProperty(cx, self, RequestedModulesSlot) &&
|
||||
FreezeObjectProperty(cx, self, ImportEntriesSlot) &&
|
||||
FreezeObjectProperty(cx, self, LocalExportEntriesSlot) &&
|
||||
FreezeObjectProperty(cx, self, IndirectExportEntriesSlot) &&
|
||||
FreezeObjectProperty(cx, self, StarExportEntriesSlot);
|
||||
}
|
||||
|
||||
static inline void
|
||||
AssertObjectPropertyFrozen(JSContext* cx, HandleNativeObject obj, uint32_t slot)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
bool frozen = false;
|
||||
RootedObject property(cx, &obj->getSlot(slot).toObject());
|
||||
MOZ_ALWAYS_TRUE(TestIntegrityLevel(cx, property, IntegrityLevel::Frozen, &frozen));
|
||||
MOZ_ASSERT(frozen);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* static */ inline void
|
||||
ModuleObject::AssertArrayPropertiesFrozen(JSContext* cx, HandleModuleObject self)
|
||||
{
|
||||
AssertObjectPropertyFrozen(cx, self, RequestedModulesSlot);
|
||||
AssertObjectPropertyFrozen(cx, self, ImportEntriesSlot);
|
||||
AssertObjectPropertyFrozen(cx, self, LocalExportEntriesSlot);
|
||||
AssertObjectPropertyFrozen(cx, self, IndirectExportEntriesSlot);
|
||||
AssertObjectPropertyFrozen(cx, self, StarExportEntriesSlot);
|
||||
}
|
||||
|
||||
inline static void
|
||||
AssertModuleScopesMatch(ModuleObject* module)
|
||||
{
|
||||
mozilla::DebugOnly<StaticModuleScope*> staticScope = module->staticScope();
|
||||
MOZ_ASSERT(IsStaticGlobalLexicalScope(staticScope->enclosingScope()));
|
||||
MOZ_ASSERT(&module->initialEnvironment().staticScope() == staticScope);
|
||||
}
|
||||
|
||||
void
|
||||
ModuleObject::fixScopesAfterCompartmentMerge(JSContext* cx)
|
||||
{
|
||||
AssertModuleScopesMatch(this);
|
||||
Rooted<ClonedBlockObject*> lexicalScope(cx, &script()->global().lexicalScope());
|
||||
staticScope()->setEnclosingScope(lexicalScope->staticScope());
|
||||
initialEnvironment().setEnclosingScope(lexicalScope);
|
||||
AssertModuleScopesMatch(this);
|
||||
}
|
||||
|
||||
bool
|
||||
ModuleObject::hasScript() const
|
||||
{
|
||||
|
@ -777,6 +835,8 @@ ModuleObject::noteFunctionDeclaration(ExclusiveContext* cx, HandleAtom name, Han
|
|||
/* static */ bool
|
||||
ModuleObject::instantiateFunctionDeclarations(JSContext* cx, HandleModuleObject self)
|
||||
{
|
||||
AssertArrayPropertiesFrozen(cx, self);
|
||||
|
||||
FunctionDeclarationVector* funDecls = self->functionDeclarations();
|
||||
if (!funDecls) {
|
||||
JS_ReportError(cx, "Module function declarations have already been instantiated");
|
||||
|
@ -813,6 +873,8 @@ ModuleObject::setEvaluated()
|
|||
/* static */ bool
|
||||
ModuleObject::evaluate(JSContext* cx, HandleModuleObject self, MutableHandleValue rval)
|
||||
{
|
||||
AssertArrayPropertiesFrozen(cx, self);
|
||||
|
||||
RootedScript script(cx, self->script());
|
||||
RootedModuleEnvironmentObject scope(cx, self->environment());
|
||||
if (!scope) {
|
||||
|
@ -903,7 +965,7 @@ js::InitModuleClasses(JSContext* cx, HandleObject obj)
|
|||
///////////////////////////////////////////////////////////////////////////
|
||||
// ModuleBuilder
|
||||
|
||||
ModuleBuilder::ModuleBuilder(JSContext* cx, HandleModuleObject module)
|
||||
ModuleBuilder::ModuleBuilder(ExclusiveContext* cx, HandleModuleObject module)
|
||||
: cx_(cx),
|
||||
module_(cx, module),
|
||||
requestedModules_(cx, AtomVector(cx)),
|
||||
|
@ -1186,8 +1248,6 @@ ArrayObject* ModuleBuilder::createArray(const GCVector<T>& vector)
|
|||
array->setDenseInitializedLength(length);
|
||||
for (uint32_t i = 0; i < length; i++)
|
||||
array->initDenseElement(i, MakeElementValue(vector[i]));
|
||||
if (!JS_FreezeObject(cx_, array))
|
||||
return nullptr;
|
||||
|
||||
return array;
|
||||
}
|
||||
|
|
|
@ -45,9 +45,9 @@ class ImportEntryObject : public NativeObject
|
|||
};
|
||||
|
||||
static const Class class_;
|
||||
static JSObject* initClass(JSContext* cx, HandleObject obj);
|
||||
static JSObject* initClass(ExclusiveContext* cx, HandleObject obj);
|
||||
static bool isInstance(HandleValue value);
|
||||
static ImportEntryObject* create(JSContext* cx,
|
||||
static ImportEntryObject* create(ExclusiveContext* cx,
|
||||
HandleAtom moduleRequest,
|
||||
HandleAtom importName,
|
||||
HandleAtom localName);
|
||||
|
@ -72,9 +72,9 @@ class ExportEntryObject : public NativeObject
|
|||
};
|
||||
|
||||
static const Class class_;
|
||||
static JSObject* initClass(JSContext* cx, HandleObject obj);
|
||||
static JSObject* initClass(ExclusiveContext* cx, HandleObject obj);
|
||||
static bool isInstance(HandleValue value);
|
||||
static ExportEntryObject* create(JSContext* cx,
|
||||
static ExportEntryObject* create(ExclusiveContext* cx,
|
||||
HandleAtom maybeExportName,
|
||||
HandleAtom maybeModuleRequest,
|
||||
HandleAtom maybeImportName,
|
||||
|
@ -234,6 +234,9 @@ class ModuleObject : public NativeObject
|
|||
HandleArrayObject localExportEntries,
|
||||
HandleArrayObject indiretExportEntries,
|
||||
HandleArrayObject starExportEntries);
|
||||
static bool FreezeArrayProperties(JSContext* cx, HandleModuleObject self);
|
||||
static void AssertArrayPropertiesFrozen(JSContext* cx, HandleModuleObject self);
|
||||
void fixScopesAfterCompartmentMerge(JSContext* cx);
|
||||
|
||||
JSScript* script() const;
|
||||
StaticModuleScope* staticScope() const;
|
||||
|
@ -275,7 +278,7 @@ class ModuleObject : public NativeObject
|
|||
class MOZ_STACK_CLASS ModuleBuilder
|
||||
{
|
||||
public:
|
||||
explicit ModuleBuilder(JSContext* cx, HandleModuleObject module);
|
||||
explicit ModuleBuilder(ExclusiveContext* cx, HandleModuleObject module);
|
||||
|
||||
bool processImport(frontend::ParseNode* pn);
|
||||
bool processExport(frontend::ParseNode* pn);
|
||||
|
@ -298,7 +301,7 @@ class MOZ_STACK_CLASS ModuleBuilder
|
|||
using RootedImportEntryVector = JS::Rooted<ImportEntryVector>;
|
||||
using RootedExportEntryVector = JS::Rooted<ExportEntryVector>;
|
||||
|
||||
JSContext* cx_;
|
||||
ExclusiveContext* cx_;
|
||||
RootedModuleObject module_;
|
||||
RootedAtomVector requestedModules_;
|
||||
RootedAtomVector importedBoundNames_;
|
||||
|
|
|
@ -575,7 +575,7 @@ BytecodeCompiler::compileModule()
|
|||
|
||||
module->init(script);
|
||||
|
||||
ModuleBuilder builder(cx->asJSContext(), module);
|
||||
ModuleBuilder builder(cx, module);
|
||||
ParseNode* pn = parser->standaloneModule(module, builder);
|
||||
if (!pn)
|
||||
return nullptr;
|
||||
|
@ -759,20 +759,40 @@ frontend::CompileScript(ExclusiveContext* cx, LifoAlloc* alloc, HandleObject sco
|
|||
}
|
||||
|
||||
ModuleObject*
|
||||
frontend::CompileModule(JSContext* cx, HandleObject obj,
|
||||
const ReadOnlyCompileOptions& optionsInput,
|
||||
SourceBufferHolder& srcBuf)
|
||||
frontend::CompileModule(ExclusiveContext* cx, const ReadOnlyCompileOptions& optionsInput,
|
||||
SourceBufferHolder& srcBuf, LifoAlloc* alloc)
|
||||
{
|
||||
MOZ_ASSERT(srcBuf.get());
|
||||
MOZ_ASSERT(cx->isJSContext() == (alloc == nullptr));
|
||||
|
||||
if (!alloc)
|
||||
alloc = &cx->asJSContext()->tempLifoAlloc();
|
||||
|
||||
CompileOptions options(cx, optionsInput);
|
||||
options.maybeMakeStrictMode(true); // ES6 10.2.1 Module code is always strict mode code.
|
||||
options.setIsRunOnce(true);
|
||||
|
||||
// ! WARNING WARNING WARNING !
|
||||
//
|
||||
// See comment in Parser::bindLexical about optimizing global lexical
|
||||
// bindings. If we start optimizing them, passing in cx's global lexical
|
||||
// scope would be incorrect as cx can be an off-main-thread parse task's
|
||||
// context here!
|
||||
//
|
||||
// ! WARNING WARNING WARNING !
|
||||
Rooted<StaticScope*> staticScope(cx, &cx->global()->lexicalScope().staticBlock());
|
||||
BytecodeCompiler compiler(cx, &cx->tempLifoAlloc(), options, srcBuf, staticScope,
|
||||
BytecodeCompiler compiler(cx, alloc, options, srcBuf, staticScope,
|
||||
TraceLogger_ParserCompileModule);
|
||||
return compiler.compileModule();
|
||||
RootedModuleObject module(cx, compiler.compileModule());
|
||||
if (!module)
|
||||
return nullptr;
|
||||
|
||||
// This happens in GlobalHelperThreadState::finishModuleParseTask() when a
|
||||
// module is compiled off main thread.
|
||||
if (cx->isJSContext() && !ModuleObject::FreezeArrayProperties(cx->asJSContext(), module))
|
||||
return nullptr;
|
||||
|
||||
return module;
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -32,9 +32,9 @@ CompileScript(ExclusiveContext* cx, LifoAlloc* alloc,
|
|||
SourceCompressionTask* extraSct = nullptr,
|
||||
ScriptSourceObject** sourceObjectOut = nullptr);
|
||||
|
||||
ModuleObject *
|
||||
CompileModule(JSContext *cx, HandleObject obj, const ReadOnlyCompileOptions &options,
|
||||
SourceBufferHolder &srcBuf);
|
||||
ModuleObject*
|
||||
CompileModule(ExclusiveContext *cx, const ReadOnlyCompileOptions &options,
|
||||
SourceBufferHolder &srcBuf, LifoAlloc* alloc = nullptr);
|
||||
|
||||
bool
|
||||
CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const char16_t* chars, size_t length);
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
// Test interaction with global object and global lexical scope.
|
||||
|
||||
function parseAndEvaluate(source) {
|
||||
let m = parseModule(source);
|
||||
m.declarationInstantiation();
|
||||
return m.evaluation();
|
||||
}
|
||||
|
||||
var x = 1;
|
||||
assertEq(parseAndEvaluate("let r = x; x = 2; r"), 1);
|
||||
assertEq(x, 2);
|
||||
|
||||
let y = 3;
|
||||
assertEq(parseAndEvaluate("let r = y; y = 4; r"), 3);
|
||||
assertEq(y, 4);
|
||||
|
||||
if (helperThreadCount() == 0)
|
||||
quit();
|
||||
|
||||
function offThreadParseAndEvaluate(source) {
|
||||
offThreadCompileModule(source);
|
||||
let m = finishOffThreadModule();
|
||||
print("compiled");
|
||||
m.declarationInstantiation();
|
||||
return m.evaluation();
|
||||
}
|
||||
|
||||
assertEq(offThreadParseAndEvaluate("let r = x; x = 5; r"), 2);
|
||||
assertEq(x, 5);
|
||||
|
||||
assertEq(offThreadParseAndEvaluate("let r = y; y = 6; r"), 4);
|
||||
assertEq(y, 6);
|
|
@ -0,0 +1,30 @@
|
|||
// Test off thread module compilation.
|
||||
|
||||
if (helperThreadCount() == 0)
|
||||
quit();
|
||||
|
||||
load(libdir + "dummyModuleResolveHook.js");
|
||||
|
||||
function offThreadParse(source) {
|
||||
offThreadCompileModule(source);
|
||||
return finishOffThreadModule();
|
||||
}
|
||||
|
||||
const sa =
|
||||
`export default 20;
|
||||
export let a = 22;
|
||||
export function f(x, y) { return x + y }
|
||||
`;
|
||||
|
||||
const sb =
|
||||
`import x from "a";
|
||||
import { a as y } from "a";
|
||||
import * as ns from "a";
|
||||
ns.f(x, y);
|
||||
`;
|
||||
|
||||
let a = moduleRepo['a'] = offThreadParse(sa);
|
||||
let b = moduleRepo['b'] = offThreadParse(sb);
|
||||
b.declarationInstantiation();
|
||||
assertEq(b.evaluation(), 42);
|
||||
|
|
@ -4133,11 +4133,11 @@ JS::FinishOffThreadScript(JSContext* maybecx, JSRuntime* rt, void* token)
|
|||
RootedScript script(maybecx);
|
||||
{
|
||||
AutoLastFrameCheck lfc(maybecx);
|
||||
script = HelperThreadState().finishParseTask(maybecx, rt, token);
|
||||
script = HelperThreadState().finishScriptParseTask(maybecx, rt, token);
|
||||
}
|
||||
return script;
|
||||
} else {
|
||||
return HelperThreadState().finishParseTask(maybecx, rt, token);
|
||||
return HelperThreadState().finishScriptParseTask(maybecx, rt, token);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3423,7 +3423,6 @@ ParseModule(JSContext* cx, unsigned argc, Value* vp)
|
|||
return false;
|
||||
}
|
||||
|
||||
RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
|
||||
JSFlatString* scriptContents = args[0].toString()->ensureFlat(cx);
|
||||
if (!scriptContents)
|
||||
return false;
|
||||
|
@ -3455,7 +3454,7 @@ ParseModule(JSContext* cx, unsigned argc, Value* vp)
|
|||
SourceBufferHolder srcBuf(chars, scriptContents->length(),
|
||||
SourceBufferHolder::NoOwnership);
|
||||
|
||||
RootedObject module(cx, frontend::CompileModule(cx, global, options, srcBuf));
|
||||
RootedObject module(cx, frontend::CompileModule(cx, options, srcBuf));
|
||||
if (!module)
|
||||
return false;
|
||||
|
||||
|
@ -3789,6 +3788,109 @@ runOffThreadScript(JSContext* cx, unsigned argc, Value* vp)
|
|||
return JS_ExecuteScript(cx, script, args.rval());
|
||||
}
|
||||
|
||||
static bool
|
||||
CompileOffThreadModule(JSContext* cx, const ReadOnlyCompileOptions& options,
|
||||
const char16_t* chars, size_t length,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData)
|
||||
{
|
||||
MOZ_ASSERT(JS::CanCompileOffThread(cx, options, length));
|
||||
return StartOffThreadParseModule(cx, options, chars, length, callback, callbackData);
|
||||
}
|
||||
|
||||
static JSObject*
|
||||
FinishOffThreadModule(JSContext* maybecx, JSRuntime* rt, void* token)
|
||||
{
|
||||
MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
|
||||
return HelperThreadState().finishModuleParseTask(maybecx, rt, token);
|
||||
}
|
||||
|
||||
static bool
|
||||
OffThreadCompileModule(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
if (args.length() != 1 || !args[0].isString()) {
|
||||
JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS,
|
||||
"offThreadCompileModule");
|
||||
return false;
|
||||
}
|
||||
|
||||
JSAutoByteString fileNameBytes;
|
||||
CompileOptions options(cx);
|
||||
options.setIntroductionType("js shell offThreadCompileModule")
|
||||
.setFileAndLine("<string>", 1);
|
||||
options.setIsRunOnce(true)
|
||||
.setSourceIsLazy(false);
|
||||
options.forceAsync = true;
|
||||
|
||||
JSString* scriptContents = args[0].toString();
|
||||
AutoStableStringChars stableChars(cx);
|
||||
if (!stableChars.initTwoByte(cx, scriptContents))
|
||||
return false;
|
||||
|
||||
size_t length = scriptContents->length();
|
||||
const char16_t* chars = stableChars.twoByteRange().start().get();
|
||||
|
||||
// Make sure we own the string's chars, so that they are not freed before
|
||||
// the compilation is finished.
|
||||
ScopedJSFreePtr<char16_t> ownedChars;
|
||||
if (stableChars.maybeGiveOwnershipToCaller()) {
|
||||
ownedChars = const_cast<char16_t*>(chars);
|
||||
} else {
|
||||
char16_t* copy = cx->pod_malloc<char16_t>(length);
|
||||
if (!copy)
|
||||
return false;
|
||||
|
||||
mozilla::PodCopy(copy, chars, length);
|
||||
ownedChars = copy;
|
||||
chars = copy;
|
||||
}
|
||||
|
||||
if (!JS::CanCompileOffThread(cx, options, length)) {
|
||||
JS_ReportError(cx, "cannot compile code on worker thread");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!offThreadState.startIfIdle(cx, ownedChars)) {
|
||||
JS_ReportError(cx, "called offThreadCompileModule without receiving prior off-thread "
|
||||
"compilation");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CompileOffThreadModule(cx, options, chars, length,
|
||||
OffThreadCompileScriptCallback, nullptr))
|
||||
{
|
||||
offThreadState.abandon(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
FinishOffThreadModule(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
JSRuntime* rt = cx->runtime();
|
||||
if (OffThreadParsingMustWaitForGC(rt))
|
||||
gc::AutoFinishGC finishgc(rt);
|
||||
|
||||
void* token = offThreadState.waitUntilDone(cx);
|
||||
if (!token) {
|
||||
JS_ReportError(cx, "called finishOffThreadModule when no compilation is pending");
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedObject module(cx, FinishOffThreadModule(cx, rt, token));
|
||||
if (!module)
|
||||
return false;
|
||||
|
||||
args.rval().setObject(*module);
|
||||
return true;
|
||||
}
|
||||
|
||||
struct MOZ_RAII FreeOnReturn
|
||||
{
|
||||
JSContext* cx;
|
||||
|
@ -5150,6 +5252,16 @@ static const JSFunctionSpecWithHelp shell_functions[] = {
|
|||
" throw the appropriate exception; otherwise, run the script and return\n"
|
||||
" its value."),
|
||||
|
||||
JS_FN_HELP("offThreadCompileModule", OffThreadCompileModule, 1, 0,
|
||||
"offThreadCompileModule(code)",
|
||||
" Compile |code| on a helper thread. To wait for the compilation to finish\n"
|
||||
" and get the module object, call |finishOffThreadModule|."),
|
||||
|
||||
JS_FN_HELP("finishOffThreadModule", FinishOffThreadModule, 0, 0,
|
||||
"finishOffThreadModule()",
|
||||
" Wait for off-thread compilation to complete. If an error occurred,\n"
|
||||
" throw the appropriate exception; otherwise, return the module object"),
|
||||
|
||||
JS_FN_HELP("timeout", Timeout, 1, 0,
|
||||
"timeout([seconds], [func])",
|
||||
" Get/Set the limit in seconds for the execution time for the current context.\n"
|
||||
|
|
|
@ -745,3 +745,18 @@ GlobalObject::addIntrinsicValue(JSContext* cx, Handle<GlobalObject*> global,
|
|||
holder->setSlot(shape->slot(), value);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
GlobalObject::ensureModulePrototypesCreated(JSContext *cx, Handle<GlobalObject*> global)
|
||||
{
|
||||
if (global->getSlot(MODULE_PROTO).isUndefined()) {
|
||||
MOZ_ASSERT(global->getSlot(IMPORT_ENTRY_PROTO).isUndefined() &&
|
||||
global->getSlot(EXPORT_ENTRY_PROTO).isUndefined());
|
||||
if (!js::InitModuleClasses(cx, global))
|
||||
return false;
|
||||
}
|
||||
MOZ_ASSERT(global->getSlot(MODULE_PROTO).isObject() &&
|
||||
global->getSlot(IMPORT_ENTRY_PROTO).isObject() &&
|
||||
global->getSlot(EXPORT_ENTRY_PROTO).isObject());
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -469,16 +469,39 @@ class GlobalObject : public NativeObject
|
|||
return getOrCreateObject(cx, DATE_TIME_FORMAT_PROTO, initDateTimeFormatProto);
|
||||
}
|
||||
|
||||
static bool ensureModulePrototypesCreated(JSContext *cx, Handle<GlobalObject*> global);
|
||||
|
||||
JSObject* maybeGetModulePrototype() {
|
||||
Value value = getSlot(MODULE_PROTO);
|
||||
return value.isUndefined() ? nullptr : &value.toObject();
|
||||
}
|
||||
|
||||
JSObject* maybeGetImportEntryPrototype() {
|
||||
Value value = getSlot(IMPORT_ENTRY_PROTO);
|
||||
return value.isUndefined() ? nullptr : &value.toObject();
|
||||
}
|
||||
|
||||
JSObject* maybeGetExportEntryPrototype() {
|
||||
Value value = getSlot(EXPORT_ENTRY_PROTO);
|
||||
return value.isUndefined() ? nullptr : &value.toObject();
|
||||
}
|
||||
|
||||
JSObject* getModulePrototype() {
|
||||
return &getSlot(MODULE_PROTO).toObject();
|
||||
JSObject* proto = maybeGetModulePrototype();
|
||||
MOZ_ASSERT(proto);
|
||||
return proto;
|
||||
}
|
||||
|
||||
JSObject* getImportEntryPrototype() {
|
||||
return &getSlot(IMPORT_ENTRY_PROTO).toObject();
|
||||
JSObject* proto = maybeGetImportEntryPrototype();
|
||||
MOZ_ASSERT(proto);
|
||||
return proto;
|
||||
}
|
||||
|
||||
JSObject* getExportEntryPrototype() {
|
||||
return &getSlot(EXPORT_ENTRY_PROTO).toObject();
|
||||
JSObject* proto = maybeGetExportEntryPrototype();
|
||||
MOZ_ASSERT(proto);
|
||||
return proto;
|
||||
}
|
||||
|
||||
static JSFunction*
|
||||
|
|
|
@ -195,10 +195,10 @@ static const JSClass parseTaskGlobalClass = {
|
|||
JS_GlobalObjectTraceHook
|
||||
};
|
||||
|
||||
ParseTask::ParseTask(ExclusiveContext* cx, JSObject* exclusiveContextGlobal, JSContext* initCx,
|
||||
const char16_t* chars, size_t length,
|
||||
ParseTask::ParseTask(ParseTaskKind kind, ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
|
||||
JSContext* initCx, const char16_t* chars, size_t length,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData)
|
||||
: cx(cx), options(initCx), chars(chars), length(length),
|
||||
: kind(kind), cx(cx), options(initCx), chars(chars), length(length),
|
||||
alloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
|
||||
exclusiveContextGlobal(initCx->runtime(), exclusiveContextGlobal),
|
||||
callback(callback), callbackData(callbackData),
|
||||
|
@ -244,6 +244,52 @@ ParseTask::~ParseTask()
|
|||
js_delete(errors[i]);
|
||||
}
|
||||
|
||||
ScriptParseTask::ScriptParseTask(ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
|
||||
JSContext* initCx, const char16_t* chars, size_t length,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData)
|
||||
: ParseTask(ParseTaskKind::Script, cx, exclusiveContextGlobal, initCx, chars, length, callback,
|
||||
callbackData)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
ScriptParseTask::parse()
|
||||
{
|
||||
SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership);
|
||||
|
||||
// ! WARNING WARNING WARNING !
|
||||
//
|
||||
// See comment in Parser::bindLexical about optimizing global lexical
|
||||
// bindings. If we start optimizing them, passing in task->cx's
|
||||
// global lexical scope would be incorrect!
|
||||
//
|
||||
// ! WARNING WARNING WARNING !
|
||||
Rooted<ClonedBlockObject*> globalLexical(cx, &cx->global()->lexicalScope());
|
||||
Rooted<StaticScope*> staticScope(cx, &globalLexical->staticBlock());
|
||||
script = frontend::CompileScript(cx, &alloc, globalLexical, staticScope, nullptr,
|
||||
options, srcBuf,
|
||||
/* source_ = */ nullptr,
|
||||
/* extraSct = */ nullptr,
|
||||
/* sourceObjectOut = */ sourceObject.address());
|
||||
}
|
||||
|
||||
ModuleParseTask::ModuleParseTask(ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
|
||||
JSContext* initCx, const char16_t* chars, size_t length,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData)
|
||||
: ParseTask(ParseTaskKind::Module, cx, exclusiveContextGlobal, initCx, chars, length, callback,
|
||||
callbackData)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
ModuleParseTask::parse()
|
||||
{
|
||||
SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership);
|
||||
ModuleObject* module = frontend::CompileModule(cx, options, srcBuf, &alloc);
|
||||
if (module)
|
||||
script = module->script();
|
||||
}
|
||||
|
||||
void
|
||||
js::CancelOffThreadParses(JSRuntime* rt)
|
||||
{
|
||||
|
@ -285,7 +331,8 @@ js::CancelOffThreadParses(JSRuntime* rt)
|
|||
if (task->runtimeMatches(rt)) {
|
||||
found = true;
|
||||
AutoUnlockHelperThreadState unlock;
|
||||
HelperThreadState().finishParseTask(/* maybecx = */ nullptr, rt, task);
|
||||
HelperThreadState().finishParseTask(/* maybecx = */ nullptr, rt, task->kind,
|
||||
task);
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
|
@ -319,7 +366,7 @@ EnsureConstructor(JSContext* cx, Handle<GlobalObject*> global, JSProtoKey key)
|
|||
// Initialize all classes potentially created during parsing for use in parser
|
||||
// data structures, template objects, &c.
|
||||
static bool
|
||||
EnsureParserCreatedClasses(JSContext* cx)
|
||||
EnsureParserCreatedClasses(JSContext* cx, ParseTaskKind kind)
|
||||
{
|
||||
Handle<GlobalObject*> global = cx->global();
|
||||
|
||||
|
@ -338,18 +385,15 @@ EnsureParserCreatedClasses(JSContext* cx)
|
|||
if (!GlobalObject::initStarGenerators(cx, global))
|
||||
return false; // needed by function*() {} and generator comprehensions
|
||||
|
||||
if (kind == ParseTaskKind::Module && !GlobalObject::ensureModulePrototypesCreated(cx, global))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& options,
|
||||
const char16_t* chars, size_t length,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData)
|
||||
static JSObject*
|
||||
CreateGlobalForOffThreadParse(JSContext* cx, ParseTaskKind kind, const gc::AutoSuppressGC& nogc)
|
||||
{
|
||||
// Suppress GC so that calls below do not trigger a new incremental GC
|
||||
// which could require barriers on the atoms compartment.
|
||||
gc::AutoSuppressGC suppress(cx);
|
||||
|
||||
JSCompartment* currentCompartment = cx->compartment();
|
||||
|
||||
JS::CompartmentOptions compartmentOptions(currentCompartment->creationOptions(),
|
||||
|
@ -367,21 +411,60 @@ js::StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& optio
|
|||
JSObject* global = JS_NewGlobalObject(cx, &parseTaskGlobalClass, nullptr,
|
||||
JS::FireOnNewGlobalHook, compartmentOptions);
|
||||
if (!global)
|
||||
return false;
|
||||
return nullptr;
|
||||
|
||||
JS_SetCompartmentPrincipals(global->compartment(), currentCompartment->principals());
|
||||
|
||||
// Initialize all classes required for parsing while still on the main
|
||||
// thread, for both the target and the new global so that prototype
|
||||
// pointers can be changed infallibly after parsing finishes.
|
||||
if (!EnsureParserCreatedClasses(cx))
|
||||
return false;
|
||||
if (!EnsureParserCreatedClasses(cx, kind))
|
||||
return nullptr;
|
||||
{
|
||||
AutoCompartment ac(cx, global);
|
||||
if (!EnsureParserCreatedClasses(cx))
|
||||
return false;
|
||||
if (!EnsureParserCreatedClasses(cx, kind))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return global;
|
||||
}
|
||||
|
||||
static bool
|
||||
QueueOffThreadParseTask(JSContext* cx, ParseTask* task)
|
||||
{
|
||||
if (OffThreadParsingMustWaitForGC(cx->runtime())) {
|
||||
AutoLockHelperThreadState lock;
|
||||
if (!HelperThreadState().parseWaitingOnGC().append(task)) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
AutoLockHelperThreadState lock;
|
||||
if (!HelperThreadState().parseWorklist().append(task)) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
task->activate(cx->runtime());
|
||||
HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& options,
|
||||
const char16_t* chars, size_t length,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData)
|
||||
{
|
||||
// Suppress GC so that calls below do not trigger a new incremental GC
|
||||
// which could require barriers on the atoms compartment.
|
||||
gc::AutoSuppressGC nogc(cx);
|
||||
|
||||
JSObject* global = CreateGlobalForOffThreadParse(cx, ParseTaskKind::Script, nogc);
|
||||
if (!global)
|
||||
return false;
|
||||
|
||||
ScopedJSDeletePtr<ExclusiveContext> helpercx(
|
||||
cx->new_<ExclusiveContext>(cx->runtime(), (PerThreadData*) nullptr,
|
||||
ExclusiveContext::Context_Exclusive));
|
||||
|
@ -389,32 +472,50 @@ js::StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& optio
|
|||
return false;
|
||||
|
||||
ScopedJSDeletePtr<ParseTask> task(
|
||||
cx->new_<ParseTask>(helpercx.get(), global, cx, chars, length,
|
||||
callback, callbackData));
|
||||
cx->new_<ScriptParseTask>(helpercx.get(), global, cx, chars, length,
|
||||
callback, callbackData));
|
||||
if (!task)
|
||||
return false;
|
||||
|
||||
helpercx.forget();
|
||||
|
||||
if (!task->init(cx, options))
|
||||
if (!task->init(cx, options) || !QueueOffThreadParseTask(cx, task))
|
||||
return false;
|
||||
|
||||
if (OffThreadParsingMustWaitForGC(cx->runtime())) {
|
||||
AutoLockHelperThreadState lock;
|
||||
if (!HelperThreadState().parseWaitingOnGC().append(task.get())) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
AutoLockHelperThreadState lock;
|
||||
if (!HelperThreadState().parseWorklist().append(task.get())) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
task.forget();
|
||||
|
||||
task->activate(cx->runtime());
|
||||
HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::StartOffThreadParseModule(JSContext* cx, const ReadOnlyCompileOptions& options,
|
||||
const char16_t* chars, size_t length,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData)
|
||||
{
|
||||
// Suppress GC so that calls below do not trigger a new incremental GC
|
||||
// which could require barriers on the atoms compartment.
|
||||
gc::AutoSuppressGC nogc(cx);
|
||||
|
||||
JSObject* global = CreateGlobalForOffThreadParse(cx, ParseTaskKind::Module, nogc);
|
||||
if (!global)
|
||||
return false;
|
||||
|
||||
ScopedJSDeletePtr<ExclusiveContext> helpercx(
|
||||
cx->new_<ExclusiveContext>(cx->runtime(), (PerThreadData*) nullptr,
|
||||
ExclusiveContext::Context_Exclusive));
|
||||
if (!helpercx)
|
||||
return false;
|
||||
|
||||
ScopedJSDeletePtr<ParseTask> task(
|
||||
cx->new_<ModuleParseTask>(helpercx.get(), global, cx, chars, length,
|
||||
callback, callbackData));
|
||||
if (!task)
|
||||
return false;
|
||||
|
||||
helpercx.forget();
|
||||
|
||||
if (!task->init(cx, options) || !QueueOffThreadParseTask(cx, task))
|
||||
return false;
|
||||
|
||||
task.forget();
|
||||
|
||||
|
@ -1015,7 +1116,8 @@ LeaveParseTaskZone(JSRuntime* rt, ParseTask* task)
|
|||
}
|
||||
|
||||
JSScript*
|
||||
GlobalHelperThreadState::finishParseTask(JSContext* maybecx, JSRuntime* rt, void* token)
|
||||
GlobalHelperThreadState::finishParseTask(JSContext* maybecx, JSRuntime* rt, ParseTaskKind kind,
|
||||
void* token)
|
||||
{
|
||||
ScopedJSDeletePtr<ParseTask> parseTask;
|
||||
|
||||
|
@ -1033,6 +1135,7 @@ GlobalHelperThreadState::finishParseTask(JSContext* maybecx, JSRuntime* rt, void
|
|||
}
|
||||
}
|
||||
MOZ_ASSERT(parseTask);
|
||||
MOZ_ASSERT(parseTask->kind == kind);
|
||||
|
||||
if (!maybecx) {
|
||||
LeaveParseTaskZone(rt, parseTask);
|
||||
|
@ -1045,7 +1148,7 @@ GlobalHelperThreadState::finishParseTask(JSContext* maybecx, JSRuntime* rt, void
|
|||
// Make sure we have all the constructors we need for the prototype
|
||||
// remapping below, since we can't GC while that's happening.
|
||||
Rooted<GlobalObject*> global(cx, &cx->global()->as<GlobalObject>());
|
||||
if (!EnsureParserCreatedClasses(cx)) {
|
||||
if (!EnsureParserCreatedClasses(cx, kind)) {
|
||||
LeaveParseTaskZone(rt, parseTask);
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -1091,6 +1194,34 @@ GlobalHelperThreadState::finishParseTask(JSContext* maybecx, JSRuntime* rt, void
|
|||
return script;
|
||||
}
|
||||
|
||||
JSScript*
|
||||
GlobalHelperThreadState::finishScriptParseTask(JSContext* maybecx, JSRuntime* rt, void* token)
|
||||
{
|
||||
JSScript* script = finishParseTask(maybecx, rt, ParseTaskKind::Script, token);
|
||||
MOZ_ASSERT_IF(script, script->isGlobalCode());
|
||||
return script;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
GlobalHelperThreadState::finishModuleParseTask(JSContext* maybecx, JSRuntime* rt, void* token)
|
||||
{
|
||||
JSScript* script = finishParseTask(maybecx, rt, ParseTaskKind::Module, token);
|
||||
if (!script)
|
||||
return nullptr;
|
||||
|
||||
MOZ_ASSERT(script->module());
|
||||
if (!maybecx)
|
||||
return nullptr;
|
||||
|
||||
JSContext* cx = maybecx;
|
||||
RootedModuleObject module(cx, script->module());
|
||||
module->fixScopesAfterCompartmentMerge(cx);
|
||||
if (!ModuleObject::FreezeArrayProperties(cx, module))
|
||||
return nullptr;
|
||||
|
||||
return module;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
GlobalObject::getStarGeneratorFunctionPrototype()
|
||||
{
|
||||
|
@ -1118,8 +1249,13 @@ GlobalHelperThreadState::mergeParseTaskCompartment(JSRuntime* rt, ParseTask* par
|
|||
// Generator functions don't have Function.prototype as prototype but a
|
||||
// different function object, so the IdentifyStandardPrototype trick
|
||||
// below won't work. Just special-case it.
|
||||
JSObject* parseTaskStarGenFunctionProto =
|
||||
parseTask->exclusiveContextGlobal->as<GlobalObject>().getStarGeneratorFunctionPrototype();
|
||||
GlobalObject* parseGlobal = &parseTask->exclusiveContextGlobal->as<GlobalObject>();
|
||||
JSObject* parseTaskStarGenFunctionProto = parseGlobal->getStarGeneratorFunctionPrototype();
|
||||
|
||||
// Module objects don't have standard prototypes either.
|
||||
JSObject* moduleProto = parseGlobal->maybeGetModulePrototype();
|
||||
JSObject* importEntryProto = parseGlobal->maybeGetImportEntryPrototype();
|
||||
JSObject* exportEntryProto = parseGlobal->maybeGetExportEntryPrototype();
|
||||
|
||||
// Point the prototypes of any objects in the script's compartment to refer
|
||||
// to the corresponding prototype in the new compartment. This will briefly
|
||||
|
@ -1134,21 +1270,24 @@ GlobalHelperThreadState::mergeParseTaskCompartment(JSRuntime* rt, ParseTask* par
|
|||
JSObject* protoObj = proto.toObject();
|
||||
|
||||
JSObject* newProto;
|
||||
if (protoObj == parseTaskStarGenFunctionProto) {
|
||||
newProto = global->getStarGeneratorFunctionPrototype();
|
||||
} else {
|
||||
JSProtoKey key = JS::IdentifyStandardPrototype(protoObj);
|
||||
if (key == JSProto_Null)
|
||||
continue;
|
||||
|
||||
JSProtoKey key = JS::IdentifyStandardPrototype(protoObj);
|
||||
if (key != JSProto_Null) {
|
||||
MOZ_ASSERT(key == JSProto_Object || key == JSProto_Array ||
|
||||
key == JSProto_Function || key == JSProto_RegExp ||
|
||||
key == JSProto_Iterator);
|
||||
|
||||
newProto = GetBuiltinPrototypePure(global, key);
|
||||
} else if (protoObj == parseTaskStarGenFunctionProto) {
|
||||
newProto = global->getStarGeneratorFunctionPrototype();
|
||||
} else if (protoObj == moduleProto) {
|
||||
newProto = global->getModulePrototype();
|
||||
} else if (protoObj == importEntryProto) {
|
||||
newProto = global->getImportEntryPrototype();
|
||||
} else if (protoObj == exportEntryProto) {
|
||||
newProto = global->getExportEntryPrototype();
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(newProto);
|
||||
group->setProtoUnchecked(TaggedProto(newProto));
|
||||
}
|
||||
}
|
||||
|
@ -1389,25 +1528,7 @@ HelperThread::handleParseWorkload()
|
|||
AutoUnlockHelperThreadState unlock;
|
||||
PerThreadData::AutoEnterRuntime enter(threadData.ptr(),
|
||||
task->exclusiveContextGlobal->runtimeFromAnyThread());
|
||||
SourceBufferHolder srcBuf(task->chars, task->length,
|
||||
SourceBufferHolder::NoOwnership);
|
||||
|
||||
// ! WARNING WARNING WARNING !
|
||||
//
|
||||
// See comment in Parser::bindLexical about optimizing global lexical
|
||||
// bindings. If we start optimizing them, passing in task->cx's
|
||||
// global lexical scope would be incorrect!
|
||||
//
|
||||
// ! WARNING WARNING WARNING !
|
||||
ExclusiveContext* parseCx = task->cx;
|
||||
Rooted<ClonedBlockObject*> globalLexical(parseCx, &parseCx->global()->lexicalScope());
|
||||
Rooted<StaticScope*> staticScope(parseCx, &globalLexical->staticBlock());
|
||||
task->script = frontend::CompileScript(parseCx, &task->alloc,
|
||||
globalLexical, staticScope, nullptr,
|
||||
task->options, srcBuf,
|
||||
/* source_ = */ nullptr,
|
||||
/* extraSct = */ nullptr,
|
||||
/* sourceObjectOut = */ task->sourceObject.address());
|
||||
task->parse();
|
||||
}
|
||||
|
||||
// The callback is invoked while we are still off the main thread.
|
||||
|
|
|
@ -37,6 +37,12 @@ namespace wasm {
|
|||
typedef Vector<IonCompileTask*, 0, SystemAllocPolicy> IonCompileTaskVector;
|
||||
} // namespace wasm
|
||||
|
||||
enum class ParseTaskKind
|
||||
{
|
||||
Script,
|
||||
Module
|
||||
};
|
||||
|
||||
// Per-process state for off thread work items.
|
||||
class GlobalHelperThreadState
|
||||
{
|
||||
|
@ -219,6 +225,11 @@ class GlobalHelperThreadState
|
|||
return bool(numWasmFailedJobs);
|
||||
}
|
||||
|
||||
JSScript* finishParseTask(JSContext* maybecx, JSRuntime* rt, ParseTaskKind kind, void* token);
|
||||
void mergeParseTaskCompartment(JSRuntime* rt, ParseTask* parseTask,
|
||||
Handle<GlobalObject*> global,
|
||||
JSCompartment* dest);
|
||||
|
||||
private:
|
||||
/*
|
||||
* Number of wasm jobs that encountered failure for the active module.
|
||||
|
@ -227,10 +238,8 @@ class GlobalHelperThreadState
|
|||
uint32_t numWasmFailedJobs;
|
||||
|
||||
public:
|
||||
JSScript* finishParseTask(JSContext* maybecx, JSRuntime* rt, void* token);
|
||||
void mergeParseTaskCompartment(JSRuntime* rt, ParseTask* parseTask,
|
||||
Handle<GlobalObject*> global,
|
||||
JSCompartment* dest);
|
||||
JSScript* finishScriptParseTask(JSContext* maybecx, JSRuntime* rt, void* token);
|
||||
JSObject* finishModuleParseTask(JSContext* maybecx, JSRuntime* rt, void* token);
|
||||
bool compressionInProgress(SourceCompressionTask* task);
|
||||
SourceCompressionTask* compressionTaskForSource(ScriptSource* ss);
|
||||
|
||||
|
@ -410,6 +419,11 @@ StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& options,
|
|||
const char16_t* chars, size_t length,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData);
|
||||
|
||||
bool
|
||||
StartOffThreadParseModule(JSContext* cx, const ReadOnlyCompileOptions& options,
|
||||
const char16_t* chars, size_t length,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData);
|
||||
|
||||
/*
|
||||
* Called at the end of GC to enqueue any Parse tasks that were waiting on an
|
||||
* atoms-zone GC to finish.
|
||||
|
@ -463,6 +477,7 @@ class MOZ_RAII AutoUnlockHelperThreadState
|
|||
|
||||
struct ParseTask
|
||||
{
|
||||
ParseTaskKind kind;
|
||||
ExclusiveContext* cx;
|
||||
OwningCompileOptions options;
|
||||
const char16_t* chars;
|
||||
|
@ -490,19 +505,36 @@ struct ParseTask
|
|||
bool overRecursed;
|
||||
bool outOfMemory;
|
||||
|
||||
ParseTask(ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
|
||||
ParseTask(ParseTaskKind kind, ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
|
||||
JSContext* initCx, const char16_t* chars, size_t length,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData);
|
||||
bool init(JSContext* cx, const ReadOnlyCompileOptions& options);
|
||||
|
||||
void activate(JSRuntime* rt);
|
||||
virtual void parse() = 0;
|
||||
bool finish(JSContext* cx);
|
||||
|
||||
bool runtimeMatches(JSRuntime* rt) {
|
||||
return exclusiveContextGlobal->runtimeFromAnyThread() == rt;
|
||||
}
|
||||
|
||||
~ParseTask();
|
||||
virtual ~ParseTask();
|
||||
};
|
||||
|
||||
struct ScriptParseTask : public ParseTask
|
||||
{
|
||||
ScriptParseTask(ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
|
||||
JSContext* initCx, const char16_t* chars, size_t length,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData);
|
||||
void parse() override;
|
||||
};
|
||||
|
||||
struct ModuleParseTask : public ParseTask
|
||||
{
|
||||
ModuleParseTask(ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
|
||||
JSContext* initCx, const char16_t* chars, size_t length,
|
||||
JS::OffThreadCompileCallback callback, void* callbackData);
|
||||
void parse() override;
|
||||
};
|
||||
|
||||
// Return whether, if a new parse task was started, it would need to wait for
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "js/GCAPI.h"
|
||||
#include "vm/Debugger.h"
|
||||
#include "vm/Opcodes.h"
|
||||
#include "vm/ScopeObject.h"
|
||||
|
||||
#include "jit/JitFrameIterator-inl.h"
|
||||
#include "vm/Interpreter-inl.h"
|
||||
|
|
Загрузка…
Ссылка в новой задаче