/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: set ts=8 sts=2 et sw=2 tw=80: * 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/. */
#include"vm/Compartment-inl.h"// JS::Compartment::wrap #include"vm/GeckoProfiler-inl.h" #include"vm/JSContext-inl.h" #include"vm/JSObject-inl.h"// JSObject::maybeHasInterestingSymbolProperty for ObjectOperations-inl.h #include"vm/ObjectOperations-inl.h"// HasProperty
usingnamespace js; usingnamespace js::frontend;
using mozilla::Maybe; using mozilla::Utf8Unit;
using JS::CompileOptions; using JS::ReadOnlyCompileOptions; using JS::SourceText;
// RAII class to check the frontend reports an exception when it fails to // compile a script. class MOZ_RAII AutoAssertReportedException { #ifdef DEBUG
JSContext* maybeCx_;
FrontendContext* fc_; bool check_;
// Error while compiling self-hosted code isn't set as an exception. // TODO: Remove this once all errors are added to frontend context. if (maybeCx_ && !maybeCx_->runtime()->hasInitializedSelfHosting()) { return;
}
// TODO: Remove this once JSContext is removed from frontend. if (maybeCx_) {
MOZ_ASSERT(maybeCx_->isExceptionPending() || fc_->hadErrors());
} else {
MOZ_ASSERT(fc_->hadErrors());
}
} #else public: explicit AutoAssertReportedException(JSContext*, FrontendContext*) {} void reset() {} #endif
};
initialStencil = fc->getAllocator()->new_<CompilationStencil>(
std::move(extensibleStencilOnHeap)); if (!initialStencil) { returnfalse;
}
if (initialStencilOut) {
*initialStencilOut = initialStencil.get();
(*initialStencilOut)->AddRef();
}
}
RefPtr<InitialStencilAndDelazifications> stencils; if (input.options.populateDelazificationCache() || stencilsOut) {
stencils = CreateInitialStencilAndDelazifications(fc, initialStencil.get()); if (!stencils) { returnfalse;
}
if (stencilsOut) {
*stencilsOut = stencils.get();
(*stencilsOut)->AddRef();
}
}
if (input.options.populateDelazificationCache()) { // NOTE: Delazification can be triggered from off-thread compilation.
StartOffThreadDelazification(maybeCx, input.options, stencils.get());
// When we are trying to validate whether on-demand delazification // generate the same stencil as concurrent delazification, we want to // parse everything eagerly off-thread ahead of re-parsing everything on // demand, to compare the outcome. // // This option works only from main-thread compilation, to avoid // dead-lock. if (input.options.waitForDelazificationCache() && maybeCx) {
WaitForAllDelazifyTasks(maybeCx->runtime());
}
}
if (gcOutput) {
MOZ_ASSERT(maybeCx); if (stencils) { if (!InstantiateStencils(maybeCx, input, *stencils.get(), *gcOutput)) { returnfalse;
}
} else {
MOZ_ASSERT(!initialStencilOut);
BorrowingCompilationStencil borrowingStencil(extensibleStencil); if (!InstantiateStencils(maybeCx, input, borrowingStencil, *gcOutput)) { returnfalse;
}
}
}
NoScopeBindingCache scopeCache;
js::LifoAlloc tempLifoAlloc(JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE,
js::BackgroundMallocArena);
CompilationInput compilationInput(options);
RefPtr<InitialStencilAndDelazifications> stencils; if (!CompileGlobalScriptToStencilAndMaybeInstantiate(
nullptr, fc, tempLifoAlloc, compilationInput, &scopeCache, srcBuf,
scopeKind, NoExtraBindings, NoInitialStencilOut,
getter_AddRefs(stencils), NoGCOutput)) {
JS_HAZ_VALUE_IS_GC_SAFE(compilationInput); return nullptr;
} // CompilationInput initialized with CompileGlobalScriptToStencil only // references information from the JS::Stencil context and the // ref-counted ScriptSource, which are both GC-free.
JS_HAZ_VALUE_IS_GC_SAFE(compilationInput); return stencils.forget();
}
if (!extraBindings.reserve(unwrappedBindingKeys.length())) {
ReportOutOfMemory(cx); returnfalse;
}
JS::Rooted<JSObject*> globalLexical(cx, &cx->global()->lexicalEnvironment());
JS::Rooted<JS::PropertyKey> id(cx); for (size_t i = 0; i < unwrappedBindingKeys.length(); i++) { if (!unwrappedBindingKeys[i].isString()) {
JS_ReportErrorASCII(cx, "The bindings key should be a string."); returnfalse;
}
JS::Rooted<JSScript*> script(
cx, CompileGlobalScriptImpl(cx, fc, options, srcBuf,
ScopeKind::NonSyntactic, &extraBindings)); if (!script) { if (fc->extraBindingsAreNotUsed()) { // Compile the script as regular global script in global lexical.
fc->clearNoExtraBindingReferencesFound();
// Warnings can be reported. Clear them to avoid reporting twice.
fc->clearWarnings();
// No other error should be reported.
MOZ_ASSERT(!fc->hadErrors());
MOZ_ASSERT(!cx->isExceptionPending());
template <typename Unit> class MOZ_STACK_CLASS StandaloneFunctionCompiler final
: public SourceAwareCompiler<Unit> { using Base = SourceAwareCompiler<Unit>;
using Base::assertSourceAndParserCreated; using Base::canHandleParseFailure; using Base::compilationState_; using Base::emplaceEmitter; using Base::handleParseFailure; using Base::parser; using Base::sourceBuffer_;
template <typename Unit> bool SourceAwareCompiler<Unit>::canHandleParseFailure( const Directives& newDirectives) { // Try to reparse if no parse errors were thrown and the directives changed. // // NOTE: // Only the following two directive changes force us to reparse the script: // - The "use asm" directive was encountered. // - The "use strict" directive was encountered and duplicate parameter names // are present. We reparse in this case to display the error at the correct // source location. See |Parser::hasValidSimpleStrictParameterNames()|. return !parser->anyChars.hadError() &&
compilationState_.directives != newDirectives;
}
if (!pn) { // Global and eval scripts don't get reparsed after a new directive was // encountered: // - "use strict" doesn't require any special error reporting for scripts. // - "use asm" directives don't have an effect in global/eval contexts.
MOZ_ASSERT(!canHandleParseFailure(compilationState_.directives)); returnfalse;
}
if (sc->isGlobalContext() && compilationState_.input.hasExtraBindings()) { if (!popupateExtraBindingsFields(sc->asGlobalContext())) { returnfalse;
}
}
{ // Successfully parsed. Emit the script.
Maybe<AutoGeckoProfilerEntry> pseudoFrame; if (maybeCx) {
pseudoFrame.emplace(maybeCx, "script emit",
JS::ProfilingCategoryPair::JS_Parsing);
}
Maybe<BytecodeEmitter> emitter; if (!emplaceEmitter(emitter, sc)) { returnfalse;
}
// Parse a standalone JS function, which might appear as the value of an // event handler attribute in an HTML <INPUT> tag, or in a Function() // constructor. template <typename Unit>
FunctionNode* StandaloneFunctionCompiler<Unit>::parse(
JSContext* cx, FunctionSyntaxKind syntaxKind, GeneratorKind generatorKind,
FunctionAsyncKind asyncKind, const Maybe<uint32_t>& parameterListEnd) {
assertSourceAndParserCreated();
TokenStreamPosition startPosition(parser->tokenStream); auto startStatePosition = compilationState_.getPosition();
// Speculatively parse using the default directives implied by the context. // If a directive is encountered (e.g., "use strict") that changes how the // function should have been parsed, we backup and reparse with the new set // of directives.
if (funbox->isInterpreted()) {
Maybe<BytecodeEmitter> emitter; if (!emplaceEmitter(emitter, funbox)) { returnfalse;
}
if (!emitter->emitFunctionScript(parsedFunction)) { returnfalse;
}
// The parser extent has stripped off the leading `function...` but // we want the SourceExtent used in the final standalone script to // start from the beginning of the buffer, and use the provided // line and column. constauto& options = compilationState_.input.options;
compilationState_.scriptExtra[CompilationStencil::TopLevelIndex].extent =
SourceExtent{/* sourceStart = */ 0,
sourceBuffer_.length(),
funbox->extent().toStringStart,
funbox->extent().toStringEnd,
options.lineno,
JS::LimitedColumnNumberOneOrigin::fromUnlimited(
JS::ColumnNumberOneOrigin(options.column))};
} else { // The asm.js module was created by parser. Instantiation below will // allocate the JSFunction that wraps it.
MOZ_ASSERT(funbox->isAsmJSModule());
MOZ_ASSERT(compilationState_.asmJS->moduleMap.has(funbox->index()));
MOZ_ASSERT(compilationState_.scriptData[CompilationStencil::TopLevelIndex]
.functionFlags.isAsmJSNative());
}
returntrue;
}
// Compile module, and return it as one of: // * ExtensibleCompilationStencil (without instantiation) // * CompilationStencil (without instantiation, has no external dependency) // * CompilationGCOutput (with instantiation). template <typename Unit>
[[nodiscard]] staticbool ParseModuleToStencilAndMaybeInstantiate(
JSContext* maybeCx, FrontendContext* fc, js::LifoAlloc& tempLifoAlloc,
CompilationInput& input, ScopeBindingCache* scopeCache,
SourceText<Unit>& srcBuf, BytecodeCompilerOutput& output) {
MOZ_ASSERT(srcBuf.get());
MOZ_ASSERT(input.options.lineno != 0, "Module cannot be compiled with lineNumber == 0");
if (!compiler.compile(maybeCx, fc)) { returnfalse;
}
if (output.is<RefPtr<CompilationStencil>>()) {
Maybe<AutoGeckoProfilerEntry> pseudoFrame; if (maybeCx) {
pseudoFrame.emplace(maybeCx, "script emit",
JS::ProfilingCategoryPair::JS_Parsing);
}
auto extensibleStencil =
fc->getAllocator()->make_unique<frontend::ExtensibleCompilationStencil>(
std::move(compiler.stencil())); if (!extensibleStencil) { returnfalse;
}
RefPtr<CompilationStencil> stencil =
fc->getAllocator()->new_<CompilationStencil>(
std::move(extensibleStencil)); if (!stencil) { returnfalse;
}
NoScopeBindingCache scopeCache;
js::LifoAlloc tempLifoAlloc(JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE,
js::BackgroundMallocArena);
RefPtr<CompilationStencil> stencil = ParseModuleToStencilImpl(
nullptr, fc, tempLifoAlloc, compilationInput, &scopeCache, srcBuf); if (!stencil) {
JS_HAZ_VALUE_IS_GC_SAFE(compilationInput); return nullptr;
} // CompilationInput initialized with ParseModuleToStencil only // references information from the JS::Stencil context and the // ref-counted ScriptSource, which are both GC-free.
JS_HAZ_VALUE_IS_GC_SAFE(compilationInput); return CreateInitialStencilAndDelazifications(fc, stencil.get());
}
if (!CompilationStencil::instantiateStencils(cx, input, stencil,
gcOutput.get())) { returnfalse;
}
// NOTE: After instantiation succeeds and bytecode is attached, the rest of // this operation should be infallible. Any failure during // delazification should restore the function back to a consistent // lazy state.
// Compile lazy functinn specified by a pair of `units` + `length`, and // optionally instantiate. // // If `stencils` is provided, the result of delazification is stored into it. // // If `borrowOut` is provided, a borrowing pointer is returned. // // If `borrowOut` is not provided, the function is instantiated. // In this case, `maybeCx` should be provided and `input` should be initialized // with a BaseScript. template <typename Unit> staticbool CompileLazyFunctionToStencilMaybeInstantiate(
JSContext* maybeCx, FrontendContext* fc, js::LifoAlloc& tempLifoAlloc,
CompilationInput& input, ScopeBindingCache* scopeCache, const Unit* units,
size_t length, InitialStencilAndDelazifications* stencils, const CompilationStencil** borrowOut) {
MOZ_ASSERT(input.source);
// NOTE: Only allow relazification if there was no lazy PrivateScriptData. // This excludes non-leaf functions and all script class constructors. bool hadLazyScriptData = input.hasPrivateScriptData(); bool isRelazifiableAfterDelazify = input.isRelazifiable(); if (isRelazifiableAfterDelazify && !hadLazyScriptData) {
compilationState.scriptData[CompilationStencil::TopLevelIndex]
.setAllowRelazify();
}
if (stencils && input.options.checkDelazificationCache()) { const CompilationStencil* cached =
stencils->getDelazificationFor(input.extent()); if (cached) { auto& concurrentSharedData = cached->sharedData; auto concurrentData =
concurrentSharedData.isSingle()
? concurrentSharedData.asSingle()->get()->immutableData()
: concurrentSharedData.asBorrow()
->asSingle()
->get()
->immutableData(); auto ondemandData =
compilationState.sharedData.asSingle()->get()->immutableData();
MOZ_RELEASE_ASSERT(concurrentData.Length() == ondemandData.Length(), "Non-deterministic stencils"); for (size_t i = 0; i < concurrentData.Length(); i++) {
MOZ_RELEASE_ASSERT(concurrentData[i] == ondemandData[i], "Non-deterministic stencils");
}
}
}
if (borrowOut) { auto extensibleStencil =
fc->getAllocator()->make_unique<frontend::ExtensibleCompilationStencil>(
std::move(compilationState)); if (!extensibleStencil) { returnfalse;
}
RefPtr<CompilationStencil> stencil =
fc->getAllocator()->new_<CompilationStencil>(
std::move(extensibleStencil)); if (!stencil) { returnfalse;
}
*borrowOut = stencils->storeDelazification(std::move(stencil));
} else {
MOZ_ASSERT(maybeCx); if (stencils) { auto extensibleStencil =
maybeCx->make_unique<frontend::ExtensibleCompilationStencil>(
std::move(compilationState)); if (!extensibleStencil) { returnfalse;
}
RefPtr<CompilationStencil> stencil =
maybeCx->new_<CompilationStencil>(std::move(extensibleStencil)); if (!stencil) { returnfalse;
}
// CompilationInput initialized with initFromStencil only reference // information from the CompilationStencil context and the ref-counted // ScriptSource, which are both GC-free.
JS_HAZ_NON_GC_POINTER CompilationInput input(options);
input.initFromStencil(context, scriptIndex, ss);
fun = gcOutput.get().getFunctionNoBaseIndex(
CompilationStencil::TopLevelIndex);
MOZ_ASSERT(fun->hasBytecode() || IsAsmJSModule(fun));
// Enqueue an off-thread source compression task after finishing parsing. if (!source->tryCompressOffThread(cx)) { return nullptr;
}
// Note: If AsmJS successfully compiles, the into.script will still be // nullptr. In this case we have compiled to a native function instead of an // interpreted script. if (gcOutput.get().script) { if (parameterListEnd) {
source->setParameterListEnd(*parameterListEnd);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.