Bug 1642708 - Remove code guarded by JS_BUILD_BINAST r=arai

Also remove js/src/frontend/BinAST* sources.

Differential Revision: https://phabricator.services.mozilla.com/D77945
This commit is contained in:
Ted Campbell 2020-06-17 14:26:09 +00:00
Родитель f4c1e09650
Коммит 8898cf7914
51 изменённых файлов: 5 добавлений и 22572 удалений

Просмотреть файл

@ -6988,13 +6988,6 @@ bool nsContentUtils::IsJavascriptMIMEType(const nsAString& aMIMEType) {
}
}
#ifdef JS_BUILD_BINAST
if (nsJSUtils::BinASTEncodingEnabled() &&
aMIMEType.LowerCaseEqualsASCII(APPLICATION_JAVASCRIPT_BINAST)) {
return true;
}
#endif
return false;
}

Просмотреть файл

@ -14,7 +14,6 @@
#include "nsJSUtils.h"
#include "jsapi.h"
#include "jsfriendapi.h"
#include "js/BinASTFormat.h" // JS::BinASTFormat
#include "js/CompilationAndEvaluation.h"
#include "js/Date.h"
#include "js/Modules.h" // JS::CompileModule, JS::GetModuleScript, JS::Module{Instantiate,Evaluate}
@ -308,70 +307,12 @@ nsresult nsJSUtils::ExecutionContext::JoinDecode(
nsresult nsJSUtils::ExecutionContext::JoinDecodeBinAST(
JS::OffThreadToken** aOffThreadToken) {
#ifdef JS_BUILD_BINAST
if (mSkip) {
return mRv;
}
MOZ_ASSERT(!mWantsReturnValue);
MOZ_ASSERT(!mExpectScopeChain);
mScript.set(JS::FinishOffThreadBinASTDecode(mCx, *aOffThreadToken));
*aOffThreadToken = nullptr; // Mark the token as having been finished.
if (!mScript) {
mSkip = true;
mRv = EvaluationExceptionToNSResult(mCx);
return mRv;
}
if (mEncodeBytecode && !StartIncrementalEncoding(mCx, mScript)) {
mSkip = true;
mRv = EvaluationExceptionToNSResult(mCx);
return mRv;
}
return NS_OK;
#else
return NS_ERROR_NOT_IMPLEMENTED;
#endif
}
nsresult nsJSUtils::ExecutionContext::DecodeBinAST(
JS::CompileOptions& aCompileOptions, const uint8_t* aBuf, size_t aLength) {
#ifdef JS_BUILD_BINAST
MOZ_ASSERT(mScopeChain.length() == 0,
"BinAST decoding is not supported in non-syntactic scopes");
if (mSkip) {
return mRv;
}
MOZ_ASSERT(aBuf);
MOZ_ASSERT(mRetValue.isUndefined());
# ifdef DEBUG
mWantsReturnValue = !aCompileOptions.noScriptRval;
# endif
mScript.set(JS::DecodeBinAST(mCx, aCompileOptions, aBuf, aLength,
JS::BinASTFormat::Multipart));
if (!mScript) {
mSkip = true;
mRv = EvaluationExceptionToNSResult(mCx);
return mRv;
}
if (mEncodeBytecode && !StartIncrementalEncoding(mCx, mScript)) {
mSkip = true;
mRv = EvaluationExceptionToNSResult(mCx);
return mRv;
}
return NS_OK;
#else
return NS_ERROR_NOT_IMPLEMENTED;
#endif
}
JSScript* nsJSUtils::ExecutionContext::GetScript() {

Просмотреть файл

@ -206,13 +206,7 @@ class nsJSUtils {
MOZ_MUST_USE nsresult ExecScript(JS::MutableHandle<JS::Value> aRetValue);
};
static bool BinASTEncodingEnabled() {
#ifdef JS_BUILD_BINAST
return mozilla::StaticPrefs::dom_script_loader_binast_encoding_enabled();
#else
return false;
#endif
}
static bool BinASTEncodingEnabled() { return false; }
static nsresult CompileModule(JSContext* aCx,
JS::SourceText<char16_t>& aSrcBuf,

Просмотреть файл

@ -190,15 +190,7 @@ void ScriptLoadRequest::SetTextSource() {
}
}
void ScriptLoadRequest::SetBinASTSource() {
#ifdef JS_BUILD_BINAST
MOZ_ASSERT(IsUnknownDataType());
mDataType = DataType::eBinASTSource;
mScriptData.emplace(VariantType<BinASTSourceBuffer>());
#else
MOZ_CRASH("BinAST not supported");
#endif
}
void ScriptLoadRequest::SetBinASTSource() { MOZ_CRASH("BinAST not supported"); }
void ScriptLoadRequest::SetBytecode() {
MOZ_ASSERT(IsUnknownDataType());
@ -206,24 +198,7 @@ void ScriptLoadRequest::SetBytecode() {
}
bool ScriptLoadRequest::ShouldAcceptBinASTEncoding() const {
#ifdef JS_BUILD_BINAST
// We accept the BinAST encoding if we're using a secure connection.
if (!mURI->SchemeIs("https")) {
return false;
}
if (StaticPrefs::dom_script_loader_binast_encoding_domain_restrict()) {
if (!nsContentUtils::IsURIInPrefList(
mURI, "dom.script_loader.binast_encoding.domain.restrict.list")) {
return false;
}
}
return true;
#else
MOZ_CRASH("BinAST not supported");
#endif
}
void ScriptLoadRequest::ClearScriptSource() {

Просмотреть файл

@ -167,13 +167,7 @@ class ScriptLoadRequest
bool IsUnknownDataType() const { return mDataType == DataType::eUnknown; }
bool IsTextSource() const { return mDataType == DataType::eTextSource; }
bool IsBinASTSource() const {
#ifdef JS_BUILD_BINAST
return mDataType == DataType::eBinASTSource;
#else
return false;
#endif
}
bool IsBinASTSource() const { return false; }
bool IsSource() const { return IsTextSource() || IsBinASTSource(); }
bool IsBytecode() const { return mDataType == DataType::eBytecode; }

Просмотреть файл

@ -2273,13 +2273,6 @@ nsresult ScriptLoader::AttemptAsyncScriptCompile(ScriptLoadRequest* aRequest,
if (!JS::CanCompileOffThread(cx, options, aRequest->ScriptTextLength())) {
return NS_OK;
}
#ifdef JS_BUILD_BINAST
} else if (aRequest->IsBinASTSource()) {
if (!JS::CanDecodeBinASTOffThread(cx, options,
aRequest->ScriptBinASTData().length())) {
return NS_OK;
}
#endif
} else {
MOZ_ASSERT(aRequest->IsBytecode());
size_t length =
@ -2318,16 +2311,6 @@ nsresult ScriptLoader::AttemptAsyncScriptCompile(ScriptLoadRequest* aRequest,
OffThreadScriptLoaderCallback, static_cast<void*>(runnable))) {
return NS_ERROR_OUT_OF_MEMORY;
}
#ifdef JS_BUILD_BINAST
} else if (aRequest->IsBinASTSource()) {
MOZ_ASSERT(aRequest->IsSource());
if (!JS::DecodeBinASTOffThread(
cx, options, aRequest->ScriptBinASTData().begin(),
aRequest->ScriptBinASTData().length(), JS::BinASTFormat::Multipart,
OffThreadScriptLoaderCallback, static_cast<void*>(runnable))) {
return NS_ERROR_OUT_OF_MEMORY;
}
#endif
} else {
MOZ_ASSERT(aRequest->IsTextSource());
MaybeSourceText maybeSource;

Просмотреть файл

@ -1,16 +0,0 @@
/* -*- 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/. */
#ifndef js_BinASTFormat_h
#define js_BinASTFormat_h
namespace JS {
enum class BinASTFormat { Multipart, Context };
} /* namespace JS */
#endif /* js_BinASTFormat_h */

Просмотреть файл

@ -19,7 +19,6 @@
#include "jstypes.h" // JS_PUBLIC_API
#include "js/BinASTFormat.h" // JS::BinASTFormat
#include "js/CompileOptions.h" // JS::ReadOnlyCompileOptions
#include "js/GCVector.h" // JS::GCVector
#include "js/Transcoding.h" // JS::TranscodeSource
@ -132,20 +131,6 @@ extern JS_PUBLIC_API bool FinishMultiOffThreadScriptsDecoder(
extern JS_PUBLIC_API void CancelMultiOffThreadScriptsDecoder(
JSContext* cx, OffThreadToken* token);
// This returns false if built without JS_BUILD_BINAST.
extern JS_PUBLIC_API bool CanDecodeBinASTOffThread(
JSContext* cx, const ReadOnlyCompileOptions& options, size_t length);
// This throws an exception if built without JS_BUILD_BINAST.
extern JS_PUBLIC_API bool DecodeBinASTOffThread(
JSContext* cx, const ReadOnlyCompileOptions& options, const uint8_t* buf,
size_t length, JS::BinASTFormat format, OffThreadCompileCallback callback,
void* callbackData);
// This throws an exception if built without JS_BUILD_BINAST.
extern JS_PUBLIC_API JSScript* FinishOffThreadBinASTDecode(
JSContext* cx, OffThreadToken* token);
} // namespace JS
#endif /* js_OffThreadScriptCompilation_h */

Просмотреть файл

@ -434,11 +434,7 @@ static bool GetBuildConfiguration(JSContext* cx, unsigned argc, Value* vp) {
return false;
}
#if defined(JS_BUILD_BINAST)
value = BooleanValue(true);
#else
value = BooleanValue(false);
#endif
if (!JS_SetProperty(cx, info, "binast", value)) {
return false;
}

Просмотреть файл

@ -1,73 +0,0 @@
/* -*- 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/. */
#ifndef frontend_BinAST_macros_h
#define frontend_BinAST_macros_h
#include "vm/JSContext.h"
// Evaluate an expression EXPR, checking that the result is not falsy.
//
// Throw `cx->alreadyReportedError()` if it returns 0/nullptr.
#define BINJS_TRY(EXPR) \
do { \
if (MOZ_UNLIKELY(!(EXPR))) { \
return cx_->alreadyReportedError(); \
} \
} while (false)
// Evaluate an expression EXPR, checking that the result is not falsy.
// In case of success, assign the result to VAR.
//
// Throw `cx->alreadyReportedError()` if it returns 0/nullptr.
#define BINJS_TRY_VAR(VAR, EXPR) \
do { \
VAR = (EXPR); \
if (MOZ_UNLIKELY(!(VAR))) { \
return cx_->alreadyReportedError(); \
} \
} while (false)
// Evaluate an expression EXPR, checking that the result is not falsy.
// In case of success, assign the result to a new variable VAR.
//
// Throw `cx->alreadyReportedError()` if it returns 0/nullptr.
#define BINJS_TRY_DECL(VAR, EXPR) \
auto VAR = (EXPR); \
if (MOZ_UNLIKELY(!(VAR))) { \
return cx_->alreadyReportedError(); \
}
#define BINJS_TRY_EMPL(VAR, EXPR) \
do { \
auto _tryEmplResult = (EXPR); \
if (MOZ_UNLIKELY(!_tryEmplResult)) { \
return cx_->alreadyReportedError(); \
} \
(VAR).emplace(_tryEmplResult.unwrap()); \
} while (false)
#define BINJS_MOZ_TRY_EMPLACE(VAR, EXPR) \
do { \
auto _tryEmplResult = (EXPR); \
if (MOZ_UNLIKELY(_tryEmplResult.isErr())) { \
return ::mozilla::Err(_tryEmplResult.unwrapErr()); \
} \
(VAR).emplace(_tryEmplResult.unwrap()); \
} while (false)
// Evaluate an expression EXPR, checking that the result is a success.
// In case of success, unwrap and assign the result to a new variable VAR.
//
// In case of error, propagate the error.
#define BINJS_MOZ_TRY_DECL(VAR, EXPR) \
auto _##VAR = (EXPR); \
if (MOZ_UNLIKELY(_##VAR.isErr())) { \
return ::mozilla::Err(_##VAR.unwrapErr()); \
} \
auto VAR = _##VAR.unwrap();
#endif // frontend_BinAST_macros_h

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,147 +0,0 @@
// This file was autogenerated by binjs_generate_spidermonkey,
// please DO NOT EDIT BY HAND.
/* -*- 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/. */
// To generate this file, see the documentation in
// js/src/frontend/binast/README.md.
#ifndef frontend_BinASTEnum_h
#define frontend_BinASTEnum_h
namespace js {
namespace frontend {
namespace binast {
// ----- Declaring string enums (by lexicographical order)
enum class AssertedDeclaredKind {
// "var"
Var,
// "non-const lexical"
NonConstLexical,
// "const lexical"
ConstLexical,
};
enum class BinaryOperator {
// ","
Comma,
// "||"
LogicalOr,
// "&&"
LogicalAnd,
// "|"
BitOr,
// "^"
BitXor,
// "&"
BitAnd,
// "=="
Eq,
// "!="
Neq,
// "==="
StrictEq,
// "!=="
StrictNeq,
// "<"
LessThan,
// "<="
LeqThan,
// ">"
GreaterThan,
// ">="
GeqThan,
// "in"
In,
// "instanceof"
Instanceof,
// "<<"
Lsh,
// ">>"
Rsh,
// ">>>"
Ursh,
// "+"
Plus,
// "-"
Minus,
// "*"
Mul,
// "/"
Div,
// "%"
Mod,
// "**"
Pow,
};
enum class CompoundAssignmentOperator {
// "+="
PlusAssign,
// "-="
MinusAssign,
// "*="
MulAssign,
// "/="
DivAssign,
// "%="
ModAssign,
// "**="
PowAssign,
// "<<="
LshAssign,
// ">>="
RshAssign,
// ">>>="
UrshAssign,
// "|="
BitOrAssign,
// "^="
BitXorAssign,
// "&="
BitAndAssign,
};
enum class UnaryOperator {
// "+"
Plus,
// "-"
Minus,
// "!"
Not,
// "~"
BitNot,
// "typeof"
Typeof,
// "void"
Void,
// "delete"
Delete,
};
enum class UpdateOperator {
// "++"
Incr,
// "--"
Decr,
};
enum class VariableDeclarationKind {
// "var"
Var,
// "let"
Let,
// "const"
Const,
};
} // namespace binast
} // namespace frontend
} // namespace js
#endif // frontend_BinASTEnum_h

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,493 +0,0 @@
// This file was autogenerated by binjs_generate_spidermonkey,
// please DO NOT EDIT BY HAND.
/* -*- 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/. */
// To generate this file, see the documentation in
// js/src/frontend/binast/README.md.
#ifndef frontend_BinASTParser_h
#define frontend_BinASTParser_h
#include "mozilla/Maybe.h"
#include "mozilla/Variant.h"
#include "frontend/BCEParserHandle.h"
#include "frontend/BinASTParserPerTokenizer.h"
#include "frontend/BinASTToken.h"
#include "frontend/BinASTTokenReaderContext.h"
#include "frontend/BinASTTokenReaderMultipart.h"
#include "frontend/FullParseHandler.h"
#include "frontend/ParseContext.h"
#include "frontend/ParseNode.h"
#include "frontend/SharedContext.h"
#include "js/CompileOptions.h"
#include "js/GCHashTable.h"
#include "js/GCVector.h"
#include "js/Result.h"
namespace js {
namespace frontend {
template <typename Tok>
class BinASTParser : public BinASTParserPerTokenizer<Tok> {
public:
using Base = BinASTParserPerTokenizer<Tok>;
using Tokenizer = Tok;
using AutoList = typename Tokenizer::AutoList;
using AutoTaggedTuple = typename Tokenizer::AutoTaggedTuple;
using Chars = typename Tokenizer::Chars;
using ListContext = typename BinASTTokenReaderBase::ListContext;
using FieldContext = typename BinASTTokenReaderBase::FieldContext;
using RootContext = typename BinASTTokenReaderBase::RootContext;
using FieldOrRootContext = BinASTTokenReaderBase::FieldOrRootContext;
using FieldOrListContext = BinASTTokenReaderBase::FieldOrListContext;
public:
// Auto-generated types.
using AssertedDeclaredKind = binast::AssertedDeclaredKind;
using BinaryOperator = binast::BinaryOperator;
using CompoundAssignmentOperator = binast::CompoundAssignmentOperator;
using UnaryOperator = binast::UnaryOperator;
using UpdateOperator = binast::UpdateOperator;
using VariableDeclarationKind = binast::VariableDeclarationKind;
public:
// BinASTParserPerTokenizer types.
using AssertedScopeKind = typename Base::AssertedScopeKind;
public:
BinASTParser(JSContext* cx, CompilationInfo& compilationInfo,
const JS::ReadOnlyCompileOptions& options,
Handle<BaseScript*> lazyScript = nullptr)
: BinASTParserPerTokenizer<Tok>(cx, compilationInfo, options,
lazyScript) {}
~BinASTParser() = default;
protected:
// BinASTParserBase fields.
using Base::cx_;
using Base::alloc_;
using Base::usedNames_;
using Base::compilationInfo_;
using Base::handler_;
using Base::pc_;
protected:
// BinASTParserPerTokenizer types.
using AutoVariableDeclarationKind =
typename Base::AutoVariableDeclarationKind;
protected:
// BinASTParserPerTokenizer fields.
using Base::tokenizer_;
using Base::variableDeclarationKind_;
protected:
// BinASTParserPerTokenizer methods.
using Base::raiseEmpty;
using Base::raiseError;
using Base::raiseInvalidClosedVar;
using Base::raiseInvalidKind;
using Base::raiseInvalidVariant;
using Base::raiseMissingDirectEvalInAssertedScope;
using Base::raiseMissingField;
using Base::raiseMissingVariableInAssertedScope;
using Base::raiseOOM;
using Base::buildFunctionBox;
using Base::finishEagerFunction;
using Base::finishLazyFunction;
using Base::makeEmptyFunctionNode;
using Base::setFunctionParametersAndBody;
using Base::addScopeName;
using Base::captureFunctionName;
using Base::checkBinding;
using Base::checkPositionalParameterIndices;
using Base::getBoundScope;
using Base::getDeclaredScope;
using Base::checkClosedVars;
using Base::checkFunctionLength;
using Base::prependDirectivesToBody;
using Base::forceStrictIfNecessary;
using Base::isInvalidKindPossible;
using Base::isInvalidVariantPossible;
public:
// ----- Sums of interfaces (by lexicographical order)
// `ParseNode*` may never be nullptr
JS::Result<Ok> parseAssertedMaybePositionalParameterName(
AssertedScopeKind scopeKind,
MutableHandle<GCVector<JSAtom*>> positionalParams,
const ListContext& context);
JS::Result<ParseNode*> parseAssignmentTarget(const FieldContext& context);
JS::Result<ParseNode*> parseAssignmentTargetOrForInOfBinding(
const FieldContext& context);
JS::Result<ParseNode*> parseBinding(const FieldContext& context);
JS::Result<ParseNode*> parseExpression(const FieldContext& context);
JS::Result<ParseNode*> parseExpressionOrSpreadElement(
const ListContext& context);
JS::Result<ParseNode*> parseExpressionOrSuper(const FieldContext& context);
JS::Result<ParseNode*> parseObjectProperty(const ListContext& context);
JS::Result<ParseNode*> parseParameter(const FieldOrListContext& context);
JS::Result<ParseNode*> parseProgram(const RootContext& context);
JS::Result<ParseNode*> parsePropertyName(const FieldContext& context);
JS::Result<ParseNode*> parseSimpleAssignmentTarget(
const FieldContext& context);
JS::Result<ParseNode*> parseStatement(const FieldOrListContext& context);
JS::Result<Ok> parseSumAssertedMaybePositionalParameterName(
const size_t start, const BinASTKind kind, AssertedScopeKind scopeKind,
MutableHandle<GCVector<JSAtom*>> positionalParams,
const ListContext& context);
JS::Result<ParseNode*> parseSumAssignmentTarget(const size_t start,
const BinASTKind kind,
const FieldContext& context);
JS::Result<ParseNode*> parseSumAssignmentTargetOrForInOfBinding(
const size_t start, const BinASTKind kind, const FieldContext& context);
JS::Result<ParseNode*> parseSumBinding(const size_t start,
const BinASTKind kind,
const FieldContext& context);
JS::Result<ParseNode*> parseSumExpression(const size_t start,
const BinASTKind kind,
const FieldContext& context);
JS::Result<ParseNode*> parseSumExpressionOrSpreadElement(
const size_t start, const BinASTKind kind, const ListContext& context);
JS::Result<ParseNode*> parseSumExpressionOrSuper(const size_t start,
const BinASTKind kind,
const FieldContext& context);
JS::Result<ParseNode*> parseSumExpressionOrVariableDeclaration(
const size_t start, const BinASTKind kind, const FieldContext& context);
JS::Result<ParseNode*> parseSumObjectProperty(const size_t start,
const BinASTKind kind,
const ListContext& context);
JS::Result<ParseNode*> parseSumParameter(const size_t start,
const BinASTKind kind,
const FieldOrListContext& context);
JS::Result<ParseNode*> parseSumProgram(const size_t start,
const BinASTKind kind,
const RootContext& context);
JS::Result<ParseNode*> parseSumPropertyName(const size_t start,
const BinASTKind kind,
const FieldContext& context);
JS::Result<ParseNode*> parseSumSimpleAssignmentTarget(
const size_t start, const BinASTKind kind, const FieldContext& context);
JS::Result<ParseNode*> parseSumStatement(const size_t start,
const BinASTKind kind,
const FieldOrListContext& context);
// ----- Interfaces (by lexicographical order)
// `ParseNode*` may never be nullptr
JS::Result<Ok> parseAssertedBlockScope(const FieldContext& context);
JS::Result<Ok> parseAssertedBoundName(AssertedScopeKind scopeKind,
const ListContext& context);
JS::Result<Ok> parseAssertedBoundNamesScope(const FieldContext& context);
JS::Result<Ok> parseAssertedDeclaredName(AssertedScopeKind scopeKind,
const ListContext& context);
JS::Result<Ok> parseAssertedParameterScope(
MutableHandle<GCVector<JSAtom*>> positionalParams,
const FieldContext& context);
JS::Result<Ok> parseAssertedScriptGlobalScope(const FieldContext& context);
JS::Result<Ok> parseAssertedVarScope(const FieldContext& context);
JS::Result<ParseNode*> parseBindingIdentifier(
const FieldOrListContext& context);
JS::Result<ParseNode*> parseBlock(const FieldOrListContext& context);
JS::Result<LexicalScopeNode*> parseCatchClause(const FieldContext& context);
JS::Result<ParseNode*> parseDirective(const ListContext& context);
JS::Result<ListNode*> parseFormalParameters(const FieldContext& context);
JS::Result<Ok> parseFunctionExpressionContents(
uint32_t funLength, ListNode** paramsOut, ListNode** bodyOut,
const FieldOrRootContext& context);
JS::Result<Ok> parseFunctionOrMethodContents(
uint32_t funLength, ListNode** paramsOut, ListNode** bodyOut,
const FieldOrRootContext& context);
JS::Result<Ok> parseGetterContents(uint32_t funLength, ListNode** paramsOut,
ListNode** bodyOut,
const FieldContext& context);
JS::Result<ParseNode*> parseIdentifierExpression(
const FieldOrListContext& context);
JS::Result<Ok> parseSetterContents(uint32_t funLength, ListNode** paramsOut,
ListNode** bodyOut,
const FieldContext& context);
JS::Result<CaseClause*> parseSwitchCase(const ListContext& context);
JS::Result<ParseNode*> parseSwitchDefault(const FieldContext& context);
JS::Result<ParseNode*> parseVariableDeclarator(const ListContext& context);
JS::Result<ParseNode*> parseInterfaceArrayAssignmentTarget(
const size_t start, const FieldContext& context);
JS::Result<ParseNode*> parseInterfaceArrayBinding(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceArrayExpression(
const size_t start, const FieldOrListContext& context);
JS::Result<Ok> parseInterfaceAssertedBlockScope(const size_t start,
const FieldContext& context);
JS::Result<Ok> parseInterfaceAssertedBoundName(const size_t start,
AssertedScopeKind scopeKind,
const ListContext& context);
JS::Result<Ok> parseInterfaceAssertedBoundNamesScope(
const size_t start, const FieldContext& context);
JS::Result<Ok> parseInterfaceAssertedDeclaredName(const size_t start,
AssertedScopeKind scopeKind,
const ListContext& context);
JS::Result<Ok> parseInterfaceAssertedParameterScope(
const size_t start, MutableHandle<GCVector<JSAtom*>> positionalParams,
const FieldContext& context);
JS::Result<Ok> parseInterfaceAssertedPositionalParameterName(
const size_t start, AssertedScopeKind scopeKind,
MutableHandle<GCVector<JSAtom*>> positionalParams,
const ListContext& context);
JS::Result<Ok> parseInterfaceAssertedScriptGlobalScope(
const size_t start, const FieldContext& context);
JS::Result<Ok> parseInterfaceAssertedVarScope(const size_t start,
const FieldContext& context);
JS::Result<ParseNode*> parseInterfaceAssignmentExpression(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceAssignmentTargetIdentifier(
const size_t start, const FieldContext& context);
JS::Result<ParseNode*> parseInterfaceAwaitExpression(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceBinaryExpression(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceBindingIdentifier(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceBindingWithInitializer(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceBlock(const size_t start,
const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceBreakStatement(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceCallExpression(
const size_t start, const FieldOrListContext& context);
JS::Result<LexicalScopeNode*> parseInterfaceCatchClause(
const size_t start, const FieldContext& context);
JS::Result<ParseNode*> parseInterfaceClassDeclaration(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceClassExpression(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceCompoundAssignmentExpression(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceComputedMemberAssignmentTarget(
const size_t start, const FieldContext& context);
JS::Result<ParseNode*> parseInterfaceComputedMemberExpression(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceComputedPropertyName(
const size_t start, const FieldContext& context);
JS::Result<ParseNode*> parseInterfaceConditionalExpression(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceContinueStatement(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceDataProperty(const size_t start,
const ListContext& context);
JS::Result<ParseNode*> parseInterfaceDebuggerStatement(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceDirective(const size_t start,
const ListContext& context);
JS::Result<ParseNode*> parseInterfaceDoWhileStatement(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceEagerArrowExpressionWithExpression(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceEagerArrowExpressionWithFunctionBody(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceEagerFunctionDeclaration(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceEagerFunctionExpression(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceEagerGetter(const size_t start,
const ListContext& context);
JS::Result<ParseNode*> parseInterfaceEagerMethod(const size_t start,
const ListContext& context);
JS::Result<ParseNode*> parseInterfaceEagerSetter(const size_t start,
const ListContext& context);
JS::Result<ParseNode*> parseInterfaceEmptyStatement(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceExpressionStatement(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceForInOfBinding(
const size_t start, const FieldContext& context);
JS::Result<ParseNode*> parseInterfaceForInStatement(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceForOfStatement(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceForStatement(
const size_t start, const FieldOrListContext& context);
JS::Result<ListNode*> parseInterfaceFormalParameters(
const size_t start, const FieldContext& context);
JS::Result<Ok> parseInterfaceFunctionExpressionContents(
const size_t start, uint32_t funLength, ListNode** paramsOut,
ListNode** bodyOut, const FieldOrRootContext& context);
JS::Result<Ok> parseInterfaceFunctionOrMethodContents(
const size_t start, uint32_t funLength, ListNode** paramsOut,
ListNode** bodyOut, const FieldOrRootContext& context);
JS::Result<Ok> parseInterfaceGetterContents(const size_t start,
uint32_t funLength,
ListNode** paramsOut,
ListNode** bodyOut,
const FieldContext& context);
JS::Result<ParseNode*> parseInterfaceIdentifierExpression(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceIfStatement(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceLabelledStatement(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceLazyArrowExpressionWithExpression(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceLazyArrowExpressionWithFunctionBody(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceLazyFunctionDeclaration(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceLazyFunctionExpression(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceLazyGetter(const size_t start,
const ListContext& context);
JS::Result<ParseNode*> parseInterfaceLazyMethod(const size_t start,
const ListContext& context);
JS::Result<ParseNode*> parseInterfaceLazySetter(const size_t start,
const ListContext& context);
JS::Result<ParseNode*> parseInterfaceLiteralBooleanExpression(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceLiteralInfinityExpression(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceLiteralNullExpression(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceLiteralNumericExpression(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceLiteralPropertyName(
const size_t start, const FieldContext& context);
JS::Result<ParseNode*> parseInterfaceLiteralRegExpExpression(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceLiteralStringExpression(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceModule(const size_t start,
const RootContext& context);
JS::Result<ParseNode*> parseInterfaceNewExpression(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceNewTargetExpression(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceObjectAssignmentTarget(
const size_t start, const FieldContext& context);
JS::Result<ParseNode*> parseInterfaceObjectBinding(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceObjectExpression(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceReturnStatement(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceScript(const size_t start,
const RootContext& context);
JS::Result<Ok> parseInterfaceSetterContents(const size_t start,
uint32_t funLength,
ListNode** paramsOut,
ListNode** bodyOut,
const FieldContext& context);
JS::Result<ParseNode*> parseInterfaceShorthandProperty(
const size_t start, const ListContext& context);
JS::Result<ParseNode*> parseInterfaceSpreadElement(
const size_t start, const ListContext& context);
JS::Result<ParseNode*> parseInterfaceStaticMemberAssignmentTarget(
const size_t start, const FieldContext& context);
JS::Result<ParseNode*> parseInterfaceStaticMemberExpression(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceSuper(const size_t start,
const FieldContext& context);
JS::Result<CaseClause*> parseInterfaceSwitchCase(const size_t start,
const ListContext& context);
JS::Result<ParseNode*> parseInterfaceSwitchDefault(
const size_t start, const FieldContext& context);
JS::Result<ParseNode*> parseInterfaceSwitchStatement(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceSwitchStatementWithDefault(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceTemplateExpression(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceThisExpression(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceThrowStatement(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceTryCatchStatement(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceTryFinallyStatement(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceUnaryExpression(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceUpdateExpression(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceVariableDeclaration(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceVariableDeclarator(
const size_t start, const ListContext& context);
JS::Result<ParseNode*> parseInterfaceWhileStatement(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceWithStatement(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceYieldExpression(
const size_t start, const FieldOrListContext& context);
JS::Result<ParseNode*> parseInterfaceYieldStarExpression(
const size_t start, const FieldOrListContext& context);
// ----- String enums (by lexicographical order)
JS::Result<typename BinASTParser<Tok>::AssertedDeclaredKind>
parseAssertedDeclaredKind(const FieldContext& context);
JS::Result<typename BinASTParser<Tok>::BinaryOperator> parseBinaryOperator(
const FieldContext& context);
JS::Result<typename BinASTParser<Tok>::CompoundAssignmentOperator>
parseCompoundAssignmentOperator(const FieldContext& context);
JS::Result<typename BinASTParser<Tok>::UnaryOperator> parseUnaryOperator(
const FieldContext& context);
JS::Result<typename BinASTParser<Tok>::UpdateOperator> parseUpdateOperator(
const FieldContext& context);
JS::Result<typename BinASTParser<Tok>::VariableDeclarationKind>
parseVariableDeclarationKind(const FieldContext& context);
// ----- Lists (by lexicographical order)
JS::Result<ParseNode*> parseArguments(const FieldContext& context);
JS::Result<ListNode*> parseFunctionBody(const FieldContext& context);
JS::Result<Ok> parseListOfAssertedBoundName(AssertedScopeKind scopeKind,
const FieldContext& context);
JS::Result<Ok> parseListOfAssertedDeclaredName(AssertedScopeKind scopeKind,
const FieldContext& context);
JS::Result<Ok> parseListOfAssertedMaybePositionalParameterName(
AssertedScopeKind scopeKind,
MutableHandle<GCVector<JSAtom*>> positionalParams,
const FieldContext& context);
JS::Result<ListNode*> parseListOfDirective(const FieldContext& context);
JS::Result<ListNode*> parseListOfObjectProperty(const FieldContext& context);
JS::Result<ListNode*> parseListOfOptionalExpressionOrSpreadElement(
const FieldContext& context);
JS::Result<ListNode*> parseListOfParameter(const FieldContext& context);
JS::Result<ListNode*> parseListOfStatement(const FieldContext& context);
JS::Result<ListNode*> parseListOfSwitchCase(const FieldContext& context);
JS::Result<ListNode*> parseListOfVariableDeclarator(
ParseNodeKind declarationListKind, const FieldContext& context);
// ----- Default values (by lexicographical order)
JS::Result<ParseNode*> parseOptionalBinding(const FieldContext& context);
JS::Result<ParseNode*> parseOptionalBindingIdentifier(
const FieldContext& context);
JS::Result<LexicalScopeNode*> parseOptionalCatchClause(
const FieldContext& context);
JS::Result<ParseNode*> parseOptionalExpression(const FieldContext& context);
JS::Result<ParseNode*> parseOptionalExpressionOrSpreadElement(
const ListContext& context);
JS::Result<ParseNode*> parseOptionalExpressionOrVariableDeclaration(
const FieldContext& context);
JS::Result<ParseNode*> parseOptionalStatement(const FieldContext& context);
};
extern template class BinASTParser<BinASTTokenReaderContext>;
extern template class BinASTParser<BinASTTokenReaderMultipart>;
} // namespace frontend
} // namespace js
#endif // frontend_BinASTParser_h

Просмотреть файл

@ -1,18 +0,0 @@
/* -*- 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 "frontend/BinASTParserBase.h"
#include "vm/JSContext-inl.h"
namespace js::frontend {
BinASTParserBase::BinASTParserBase(JSContext* cx,
CompilationInfo& compilationInfo)
: ParserSharedBase(cx, compilationInfo,
ParserSharedBase::Kind::BinASTParser) {}
} // namespace js::frontend

Просмотреть файл

@ -1,39 +0,0 @@
/* -*- 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/. */
#ifndef frontend_BinASTParserBase_h
#define frontend_BinASTParserBase_h
#include <stddef.h>
#include "ds/LifoAlloc.h"
#include "frontend/FullParseHandler.h"
#include "frontend/ParseContext.h"
#include "frontend/ParseNode.h"
#include "frontend/Parser.h"
#include "frontend/SharedContext.h"
#include "js/RootingAPI.h"
#include "js/TracingAPI.h"
#include "js/Utility.h"
#include "vm/JSContext.h"
#include "vm/JSScript.h"
namespace js {
namespace frontend {
class BinASTParserBase : public ParserSharedBase {
public:
BinASTParserBase(JSContext* cx, CompilationInfo& compilationInfo);
~BinASTParserBase() = default;
public:
void trace(JSTracer* trc) override {}
};
} // namespace frontend
} // namespace js
#endif // frontend_BinASTParserBase_h

Просмотреть файл

@ -1,831 +0,0 @@
/* -*- 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 "frontend/BinASTParserPerTokenizer.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Casting.h"
#include "mozilla/Maybe.h"
#include "mozilla/PodOperations.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/Vector.h"
#include <utility>
#include "frontend/BinAST-macros.h"
#include "frontend/BinASTParser.h"
#include "frontend/BinASTTokenReaderContext.h"
#include "frontend/BinASTTokenReaderMultipart.h"
#include "frontend/FullParseHandler.h"
#include "frontend/FunctionSyntaxKind.h" // FunctionSyntaxKind
#include "frontend/ParseNode.h"
#include "frontend/Parser.h"
#include "frontend/SharedContext.h"
#include "js/Result.h"
#include "vm/FunctionFlags.h" // js::FunctionFlags
#include "vm/GeneratorAndAsyncKind.h" // js::GeneratorKind, js::FunctionAsyncKind
#include "vm/RegExpObject.h"
#include "frontend/ParseContext-inl.h"
#include "frontend/SharedContext-inl.h"
#include "vm/JSContext-inl.h"
// # About compliance with EcmaScript
//
// For the moment, this parser implements ES5. Future versions will be extended
// to ES6 and further on.
//
// By design, it does NOT implement Annex B.3.3. If possible, we would like
// to avoid going down that rabbit hole.
//
//
// # About the AST
//
// At this stage of experimentation, the AST specifications change often. This
// version of the parser attempts to implement
// https://gist.github.com/Yoric/2390f0367515c079172be2526349b294
//
//
// # About validating the AST
//
// Normally, this implementation validates all properties of the AST *except*
// the order of fields, which is partially constrained by the AST spec (e.g. in
// a block, field `scope` must appear before field `body`, etc.).
//
//
// # About names and scopes
//
// One of the key objectives of the BinAST syntax is to be able to entirely skip
// parsing inner functions until they are needed. With a purely syntactic AST,
// this is generally impossible, as we would need to walk the AST to find
// lexically-bound/var-bound variables, instances of direct eval, etc.
//
// To achieve this, BinAST files contain scope data, as instances of
// `BinJS:Scope` nodes. Rather than walking the AST to assign bindings
// to scopes, we extract data from the `BinJS:Scope` and check it lazily,
// once we actually need to walk the AST.
//
// WARNING: The current implementation DOES NOT perform the check yet. It
// is therefore unsafe.
//
// # About directives
//
// Currently, directives are ignored and treated as regular strings.
//
// They should be treated lazily (whenever we open a subscope), like bindings.
namespace js::frontend {
using UsedNamePtr = UsedNameTracker::UsedNameMap::Ptr;
// ------------- Toplevel constructions
template <typename Tok>
BinASTParserPerTokenizer<Tok>::BinASTParserPerTokenizer(
JSContext* cx, CompilationInfo& compilationInfo,
const JS::ReadOnlyCompileOptions& options,
Handle<BaseScript*> lazyScript /* = nullptr */)
: BinASTParserBase(cx, compilationInfo),
options_(options),
lazyScript_(cx, lazyScript),
handler_(cx, compilationInfo.allocScope.alloc(), nullptr,
SourceKind::Binary),
variableDeclarationKind_(VariableDeclarationKind::Var) {
MOZ_ASSERT_IF(lazyScript_, lazyScript_->isBinAST());
}
template <typename Tok>
JS::Result<ParseNode*> BinASTParserPerTokenizer<Tok>::parse(
GlobalSharedContext* globalsc, const Vector<uint8_t>& data,
BinASTSourceMetadata** metadataPtr) {
return parse(globalsc, data.begin(), data.length(), metadataPtr);
}
template <typename Tok>
JS::Result<ParseNode*> BinASTParserPerTokenizer<Tok>::parse(
GlobalSharedContext* globalsc, const uint8_t* start, const size_t length,
BinASTSourceMetadata** metadataPtr) {
auto result = parseAux(globalsc, start, length, metadataPtr);
poison(); // Make sure that the parser is never used again accidentally.
return result;
}
template <typename Tok>
JS::Result<ParseNode*> BinASTParserPerTokenizer<Tok>::parseAux(
GlobalSharedContext* globalsc, const uint8_t* start, const size_t length,
BinASTSourceMetadata** metadataPtr) {
MOZ_ASSERT(globalsc);
tokenizer_.emplace(cx_, this, start, length);
BinASTParseContext globalpc(cx_, this, globalsc,
/* newDirectives = */ nullptr);
if (MOZ_UNLIKELY(!globalpc.init())) {
return cx_->alreadyReportedError();
}
ParseContext::VarScope varScope(cx_, &globalpc, usedNames_);
if (MOZ_UNLIKELY(!varScope.init(&globalpc))) {
return cx_->alreadyReportedError();
}
MOZ_TRY(tokenizer_->readHeader());
ParseNode* result(nullptr);
const auto topContext = RootContext();
MOZ_TRY_VAR(result, asFinalParser()->parseProgram(topContext));
MOZ_TRY(tokenizer_->readTreeFooter());
mozilla::Maybe<GlobalScope::Data*> bindings =
NewGlobalScopeData(cx_, varScope, alloc_, pc_);
if (MOZ_UNLIKELY(!bindings)) {
return cx_->alreadyReportedError();
}
globalsc->bindings = *bindings;
if (metadataPtr) {
*metadataPtr = tokenizer_->takeMetadata();
}
return result; // Magic conversion to Ok.
}
template <typename Tok>
JS::Result<FunctionNode*> BinASTParserPerTokenizer<Tok>::parseLazyFunction(
ScriptSource* scriptSource, const size_t firstOffset) {
MOZ_ASSERT(lazyScript_);
MOZ_ASSERT(scriptSource->length() > firstOffset);
tokenizer_.emplace(cx_, this, scriptSource->binASTSource(),
scriptSource->length());
MOZ_TRY(tokenizer_->initFromScriptSource(scriptSource));
tokenizer_->seek(firstOffset);
// For now, only function declarations and function expression are supported.
RootedFunction func(cx_, lazyScript_->function());
bool isExpr = func->isLambda();
MOZ_ASSERT(func->kind() == FunctionFlags::FunctionKind::NormalFunction);
// Poison the tokenizer when we leave to ensure that it's not used again by
// accident.
auto onExit = mozilla::MakeScopeExit([&]() { poison(); });
// TODO: This should be actually shared with the auto-generated version.
auto syntaxKind =
isExpr ? FunctionSyntaxKind::Expression : FunctionSyntaxKind::Statement;
BINJS_MOZ_TRY_DECL(
funbox, buildFunctionBox(lazyScript_->generatorKind(),
lazyScript_->asyncKind(), syntaxKind, nullptr));
// Push a new ParseContext. It will be used to parse `scope`, the arguments,
// the function.
BinASTParseContext funpc(cx_, this, funbox, /* newDirectives = */ nullptr);
BINJS_TRY(funpc.init());
pc_->functionScope().useAsVarScope(pc_);
MOZ_ASSERT(pc_->isFunctionBox());
ParseContext::Scope lexicalScope(cx_, pc_, usedNames_);
BINJS_TRY(lexicalScope.init(pc_));
ListNode* params;
ListNode* tmpBody;
auto parseFunc = isExpr ? &FinalParser::parseFunctionExpressionContents
: &FinalParser::parseFunctionOrMethodContents;
// Inject a toplevel context (i.e. no parent) to parse the lazy content.
// In the future, we may move this to a more specific context.
const auto context = FieldOrRootContext(RootContext());
MOZ_TRY(
(asFinalParser()->*parseFunc)(func->nargs(), &params, &tmpBody, context));
uint32_t nargs = params->count();
BINJS_TRY_DECL(lexicalScopeData,
NewLexicalScopeData(cx_, lexicalScope, alloc_, pc_));
BINJS_TRY_DECL(body, handler_.newLexicalScope(*lexicalScopeData, tmpBody));
BINJS_MOZ_TRY_DECL(result,
makeEmptyFunctionNode(firstOffset, syntaxKind, funbox));
MOZ_TRY(setFunctionParametersAndBody(result, params, body));
MOZ_TRY(finishEagerFunction(funbox, nargs));
return result;
}
template <typename Tok>
void BinASTParserPerTokenizer<Tok>::forceStrictIfNecessary(
SharedContext* sc, ListNode* directives) {
JSAtom* useStrict = cx_->names().useStrict;
for (const ParseNode* directive : directives->contents()) {
if (directive->as<NameNode>().atom() == useStrict) {
sc->setStrictScript();
break;
}
}
}
template <typename Tok>
JS::Result<FunctionBox*> BinASTParserPerTokenizer<Tok>::buildFunctionBox(
GeneratorKind generatorKind, FunctionAsyncKind functionAsyncKind,
FunctionSyntaxKind syntax, ParseNode* name) {
MOZ_ASSERT_IF(!pc_, lazyScript_);
RootedFunction fun(cx_);
RootedAtom atom(cx_);
FunctionFlags flags;
// Name might be any of {Identifier,ComputedPropertyName,LiteralPropertyName}
if (name && name->is<NameNode>()) {
atom = name->as<NameNode>().atom();
}
if (pc_) {
if (syntax == FunctionSyntaxKind::Statement) {
auto ptr = pc_->varScope().lookupDeclaredName(atom);
if (MOZ_UNLIKELY(!ptr)) {
return raiseError(
"FunctionDeclaration without corresponding AssertedDeclaredName.");
}
DeclarationKind declaredKind = ptr->value()->kind();
if (DeclarationKindIsVar(declaredKind)) {
if (MOZ_UNLIKELY(!pc_->atBodyLevel())) {
return raiseError(
"body-level FunctionDeclaration inside non-body-level context.");
}
RedeclareVar(ptr, DeclarationKind::BodyLevelFunction);
}
}
flags = InitialFunctionFlags(syntax, generatorKind, functionAsyncKind);
} else {
fun = lazyScript_->function();
atom = fun->displayAtom();
flags = fun->flags();
}
mozilla::Maybe<Directives> directives;
if (pc_) {
directives.emplace(pc_);
} else {
directives.emplace(lazyScript_->strict());
}
size_t index = this->getCompilationInfo().funcData.length();
if (!this->getCompilationInfo().functions.emplaceBack(fun)) {
return nullptr;
}
if (!this->getCompilationInfo().funcData.emplaceBack(cx_)) {
return nullptr;
}
// This extent will be further filled in during parse.
SourceExtent extent;
auto* funbox = alloc_.new_<FunctionBox>(
cx_, compilationInfo_.traceListHead, extent, getCompilationInfo(),
*directives, generatorKind, functionAsyncKind, atom, flags, index);
if (MOZ_UNLIKELY(!funbox)) {
return raiseOOM();
}
compilationInfo_.traceListHead = funbox;
if (pc_) {
funbox->initWithEnclosingParseContext(pc_, flags, syntax);
} else {
frontend::ScopeContext scopeContext(fun->enclosingScope());
funbox->initFromLazyFunction(fun);
funbox->initWithEnclosingScope(scopeContext, fun->enclosingScope(), flags,
syntax);
}
return funbox;
}
template <typename Tok>
JS::Result<FunctionNode*> BinASTParserPerTokenizer<Tok>::makeEmptyFunctionNode(
const size_t start, const FunctionSyntaxKind syntaxKind,
FunctionBox* funbox) {
// Lazy script compilation requires basically none of the fields filled out.
TokenPos pos = tokenizer_->pos(start);
BINJS_TRY_DECL(result, handler_.newFunction(syntaxKind, pos));
funbox->setStart(start, 0, pos.begin);
funbox->setEnd(pos.end);
handler_.setFunctionBox(result, funbox);
return result;
}
template <typename Tok>
JS::Result<Ok> BinASTParserPerTokenizer<Tok>::setFunctionParametersAndBody(
FunctionNode* fun, ListNode* params, ParseNode* body) {
params->appendWithoutOrderAssumption(body);
handler_.setFunctionFormalParametersAndBody(fun, params);
return Ok();
}
template <typename Tok>
JS::Result<Ok> BinASTParserPerTokenizer<Tok>::finishEagerFunction(
FunctionBox* funbox, uint32_t nargs) {
// If this is delazification of a canonical function, the JSFunction object
// already has correct `nargs_`.
if (!lazyScript_ || lazyScript_->function() != funbox->function()) {
funbox->setArgCount(nargs);
} else {
MOZ_ASSERT(funbox->function()->nargs() == nargs);
funbox->setArgCount(nargs);
}
// BCE will need to generate bytecode for this.
funbox->emitBytecode = true;
const bool canSkipLazyClosedOverBindings = false;
BINJS_TRY(pc_->declareFunctionArgumentsObject(usedNames_,
canSkipLazyClosedOverBindings));
BINJS_TRY(
pc_->declareFunctionThis(usedNames_, canSkipLazyClosedOverBindings));
// Check all our bindings after maybe adding function metavars.
MOZ_TRY(checkFunctionClosedVars());
BINJS_TRY_DECL(bindings, NewFunctionScopeData(cx_, pc_->functionScope(),
/* hasParameterExprs = */ false,
alloc_, pc_));
funbox->functionScopeBindings().set(*bindings);
// See: JSFunction::needsCallObject()
if (FunctionScopeHasClosedOverBindings(pc_) ||
funbox->needsCallObjectRegardlessOfBindings()) {
funbox->setNeedsFunctionEnvironmentObjects();
}
if (funbox->isNamedLambda()) {
BINJS_TRY_DECL(
recursiveBinding,
NewLexicalScopeData(cx_, pc_->namedLambdaScope(), alloc_, pc_));
funbox->namedLambdaBindings().set(*recursiveBinding);
// See: JSFunction::needsNamedLambdaEnvironment()
if (LexicalScopeHasClosedOverBindings(pc_, pc_->namedLambdaScope())) {
funbox->setNeedsFunctionEnvironmentObjects();
}
}
return Ok();
}
template <typename Tok>
JS::Result<Ok> BinASTParserPerTokenizer<Tok>::finishLazyFunction(
FunctionBox* funbox, uint32_t nargs, size_t start, size_t end) {
funbox->setArgCount(nargs);
ScriptStencil& stencil = funbox->functionStencil().get();
// Compute the flags for the BaseScript.
using ImmutableFlags = ImmutableScriptFlagsEnum;
stencil.immutableFlags = funbox->immutableFlags();
stencil.immutableFlags.setFlag(ImmutableFlags::HasMappedArgsObj,
funbox->hasMappedArgsObj());
stencil.immutableFlags.setFlag(ImmutableFlags::IsLikelyConstructorWrapper,
funbox->isLikelyConstructorWrapper());
funbox->extent = SourceExtent(start, end, start, end,
/* lineno = */ 0, start);
MOZ_TRY(tokenizer_->registerLazyScript(funbox));
return Ok();
}
template <typename Tok>
JS::Result<Ok> BinASTParserPerTokenizer<Tok>::addScopeName(
AssertedScopeKind scopeKind, HandleAtom name, ParseContext::Scope* scope,
DeclarationKind declKind, bool isCaptured, bool allowDuplicateName) {
auto ptr = scope->lookupDeclaredNameForAdd(name);
if (ptr) {
if (allowDuplicateName) {
return Ok();
}
return raiseError("Variable redeclaration");
}
BINJS_TRY(scope->addDeclaredName(pc_, ptr, name.get(), declKind,
tokenizer_->offset()));
if (isCaptured) {
auto declaredPtr = scope->lookupDeclaredName(name);
MOZ_ASSERT(declaredPtr);
declaredPtr->value()->setClosedOver();
}
return Ok();
}
template <typename Tok>
void BinASTParserPerTokenizer<Tok>::captureFunctionName() {
MOZ_ASSERT(pc_->isFunctionBox());
MOZ_ASSERT(pc_->functionBox()->isNamedLambda());
RootedAtom funName(cx_, pc_->functionBox()->explicitName());
MOZ_ASSERT(funName);
auto ptr = pc_->namedLambdaScope().lookupDeclaredName(funName);
MOZ_ASSERT(ptr);
ptr->value()->setClosedOver();
}
template <typename Tok>
JS::Result<Ok> BinASTParserPerTokenizer<Tok>::getDeclaredScope(
AssertedScopeKind scopeKind, AssertedDeclaredKind kind,
ParseContext::Scope*& scope, DeclarationKind& declKind) {
MOZ_ASSERT(scopeKind == AssertedScopeKind::Block ||
scopeKind == AssertedScopeKind::Global ||
scopeKind == AssertedScopeKind::Var);
switch (kind) {
case AssertedDeclaredKind::Var:
if (MOZ_UNLIKELY(scopeKind == AssertedScopeKind::Block)) {
return raiseError("AssertedBlockScope cannot contain 'var' binding");
}
declKind = DeclarationKind::Var;
scope = &pc_->varScope();
break;
case AssertedDeclaredKind::NonConstLexical:
declKind = DeclarationKind::Let;
scope = pc_->innermostScope();
break;
case AssertedDeclaredKind::ConstLexical:
declKind = DeclarationKind::Const;
scope = pc_->innermostScope();
break;
}
return Ok();
}
template <typename Tok>
JS::Result<Ok> BinASTParserPerTokenizer<Tok>::getBoundScope(
AssertedScopeKind scopeKind, ParseContext::Scope*& scope,
DeclarationKind& declKind) {
MOZ_ASSERT(scopeKind == AssertedScopeKind::Catch ||
scopeKind == AssertedScopeKind::Parameter);
switch (scopeKind) {
case AssertedScopeKind::Catch:
declKind = DeclarationKind::CatchParameter;
scope = pc_->innermostScope();
break;
case AssertedScopeKind::Parameter:
MOZ_ASSERT(pc_->isFunctionBox());
declKind = DeclarationKind::PositionalFormalParameter;
scope = &pc_->functionScope();
break;
default:
MOZ_ASSERT_UNREACHABLE("Unexpected AssertedScopeKind");
break;
}
return Ok();
}
template <typename Tok>
JS::Result<Ok> BinASTParserPerTokenizer<Tok>::checkBinding(JSAtom* name) {
// Check that the variable appears in the corresponding scope.
ParseContext::Scope& scope =
variableDeclarationKind_ == VariableDeclarationKind::Var
? pc_->varScope()
: *pc_->innermostScope();
auto ptr = scope.lookupDeclaredName(name->asPropertyName());
if (MOZ_UNLIKELY(!ptr)) {
return raiseMissingVariableInAssertedScope(name);
}
return Ok();
}
// Binary AST (revision 8eab67e0c434929a66ff6abe99ff790bca087dda)
// 3.1.5 CheckPositionalParameterIndices.
template <typename Tok>
JS::Result<Ok> BinASTParserPerTokenizer<Tok>::checkPositionalParameterIndices(
Handle<GCVector<JSAtom*>> positionalParams, ListNode* params) {
// positionalParams should have the corresponding entry up to the last
// positional parameter.
// `positionalParams` corresponds to `expectedParams` parameter in the spec.
// `params` corresponds to `parseTree` in 3.1.9 CheckAssertedScope, and
// `positionalParamNames` parameter
// Steps 1-3.
// PositionalParameterNames (3.1.9 CheckAssertedScope step 5.d) and
// CreatePositionalParameterIndices (3.1.5 CheckPositionalParameterIndices
// step 1) are done implicitly.
uint32_t i = 0;
const bool hasRest = pc_->functionBox()->hasRest();
for (ParseNode* param : params->contents()) {
if (param->isKind(ParseNodeKind::AssignExpr)) {
param = param->as<AssignmentNode>().left();
}
// At this point, function body is not part of params list.
const bool isRest = hasRest && !param->pn_next;
if (isRest) {
// Rest parameter
// Step 3.
if (i >= positionalParams.get().length()) {
continue;
}
if (MOZ_UNLIKELY(positionalParams.get()[i])) {
return raiseError(
"Expected positional parameter per "
"AssertedParameterScope.paramNames, got rest parameter");
}
} else if (param->isKind(ParseNodeKind::Name)) {
// Simple or default parameter.
// Step 2.a.
if (MOZ_UNLIKELY(i >= positionalParams.get().length())) {
return raiseError(
"AssertedParameterScope.paramNames doesn't have corresponding "
"entry to positional parameter");
}
JSAtom* name = positionalParams.get()[i];
if (MOZ_UNLIKELY(!name)) {
// Step 2.a.ii.1.
return raiseError(
"Expected destructuring/rest parameter per "
"AssertedParameterScope.paramNames, got positional parameter");
}
// Step 2.a.i.
if (MOZ_UNLIKELY(param->as<NameNode>().name() != name)) {
// Step 2.a.ii.1.
return raiseError(
"Name mismatch between AssertedPositionalParameterName in "
"AssertedParameterScope.paramNames and actual parameter");
}
// Step 2.a.i.1.
// Implicitly done.
} else {
// Destructuring parameter.
MOZ_ASSERT(param->isKind(ParseNodeKind::ObjectExpr) ||
param->isKind(ParseNodeKind::ArrayExpr));
// Step 3.
if (i >= positionalParams.get().length()) {
continue;
}
if (MOZ_UNLIKELY(positionalParams.get()[i])) {
return raiseError(
"Expected positional parameter per "
"AssertedParameterScope.paramNames, got destructuring parameter");
}
}
i++;
}
// Step 3.
if (MOZ_UNLIKELY(positionalParams.get().length() > params->count())) {
// `positionalParams` has unchecked entries.
return raiseError(
"AssertedParameterScope.paramNames has unmatching items than the "
"actual parameters");
}
return Ok();
}
// Binary AST (revision 8eab67e0c434929a66ff6abe99ff790bca087dda)
// 3.1.13 CheckFunctionLength.
template <typename Tok>
JS::Result<Ok> BinASTParserPerTokenizer<Tok>::checkFunctionLength(
uint32_t expectedLength) {
if (MOZ_UNLIKELY(pc_->functionBox()->length != expectedLength)) {
return raiseError("Function length does't match");
}
return Ok();
}
template <typename Tok>
JS::Result<Ok> BinASTParserPerTokenizer<Tok>::checkClosedVars(
ParseContext::Scope& scope) {
for (ParseContext::Scope::BindingIter bi = scope.bindings(pc_); bi; bi++) {
if (UsedNamePtr p = usedNames_.lookup(bi.name())) {
bool closedOver;
p->value().noteBoundInScope(pc_->scriptId(), scope.id(), &closedOver);
if (MOZ_UNLIKELY(closedOver && !bi.closedOver())) {
return raiseInvalidClosedVar(bi.name());
}
}
}
return Ok();
}
template <typename Tok>
JS::Result<Ok> BinASTParserPerTokenizer<Tok>::checkFunctionClosedVars() {
MOZ_ASSERT(pc_->isFunctionBox());
MOZ_TRY(checkClosedVars(*pc_->innermostScope()));
MOZ_TRY(checkClosedVars(pc_->functionScope()));
if (pc_->functionBox()->isNamedLambda()) {
MOZ_TRY(checkClosedVars(pc_->namedLambdaScope()));
}
return Ok();
}
template <typename Tok>
JS::Result<Ok> BinASTParserPerTokenizer<Tok>::prependDirectivesToBody(
ListNode* body, ListNode* directives) {
if (!directives) {
return Ok();
}
if (directives->empty()) {
return Ok();
}
MOZ_TRY(prependDirectivesImpl(body, directives->head()));
return Ok();
}
template <typename Tok>
JS::Result<Ok> BinASTParserPerTokenizer<Tok>::prependDirectivesImpl(
ListNode* body, ParseNode* directive) {
BINJS_TRY(CheckRecursionLimit(cx_));
// Prepend in the reverse order by using stack, so that the directives are
// prepended in the original order.
if (ParseNode* next = directive->pn_next) {
MOZ_TRY(prependDirectivesImpl(body, next));
}
BINJS_TRY_DECL(statement,
handler_.newExprStatement(directive, directive->pn_pos.end));
body->prependAndUpdatePos(statement);
return Ok();
}
template <typename Tok>
mozilla::GenericErrorResult<JS::Error&>
BinASTParserPerTokenizer<Tok>::raiseInvalidClosedVar(JSAtom* name) {
return raiseError("Captured variable was not declared as captured");
}
template <typename Tok>
mozilla::GenericErrorResult<JS::Error&>
BinASTParserPerTokenizer<Tok>::raiseMissingVariableInAssertedScope(
JSAtom* name) {
// For the moment, we don't trust inputs sufficiently to put the name
// in an error message.
return raiseError("Missing variable in AssertedScope");
}
template <typename Tok>
mozilla::GenericErrorResult<JS::Error&>
BinASTParserPerTokenizer<Tok>::raiseMissingDirectEvalInAssertedScope() {
return raiseError("Direct call to `eval` was not declared in AssertedScope");
}
template <typename Tok>
mozilla::GenericErrorResult<JS::Error&>
BinASTParserPerTokenizer<Tok>::raiseInvalidKind(const char* superKind,
const BinASTKind kind) {
Sprinter out(cx_);
BINJS_TRY(out.init());
BINJS_TRY(out.printf("In %s, invalid kind %s", superKind,
describeBinASTKind(kind)));
return raiseError(out.string());
}
template <typename Tok>
mozilla::GenericErrorResult<JS::Error&>
BinASTParserPerTokenizer<Tok>::raiseInvalidVariant(const char* kind,
const BinASTVariant value) {
Sprinter out(cx_);
BINJS_TRY(out.init());
BINJS_TRY(out.printf("In %s, invalid variant '%s'", kind,
describeBinASTVariant(value)));
return raiseError(out.string());
}
template <typename Tok>
mozilla::GenericErrorResult<JS::Error&>
BinASTParserPerTokenizer<Tok>::raiseMissingField(const char* kind,
const BinASTField field) {
Sprinter out(cx_);
BINJS_TRY(out.init());
BINJS_TRY(out.printf("In %s, missing field '%s'", kind,
describeBinASTField(field)));
return raiseError(out.string());
}
template <typename Tok>
mozilla::GenericErrorResult<JS::Error&>
BinASTParserPerTokenizer<Tok>::raiseEmpty(const char* description) {
Sprinter out(cx_);
BINJS_TRY(out.init());
BINJS_TRY(out.printf("Empty %s", description));
return raiseError(out.string());
}
template <typename Tok>
mozilla::GenericErrorResult<JS::Error&>
BinASTParserPerTokenizer<Tok>::raiseOOM() {
return tokenizer_->raiseOOM();
}
template <typename Tok>
mozilla::GenericErrorResult<JS::Error&>
BinASTParserPerTokenizer<Tok>::raiseError(BinASTKind kind,
const char* description) {
Sprinter out(cx_);
BINJS_TRY(out.init());
BINJS_TRY(out.printf("In %s, %s", describeBinASTKind(kind), description));
return tokenizer_->raiseError(out.string());
}
template <typename Tok>
mozilla::GenericErrorResult<JS::Error&>
BinASTParserPerTokenizer<Tok>::raiseError(const char* description) {
return tokenizer_->raiseError(description);
}
template <typename Tok>
void BinASTParserPerTokenizer<Tok>::poison() {
tokenizer_.reset();
}
template <typename Tok>
bool BinASTParserPerTokenizer<Tok>::computeErrorMetadata(
ErrorMetadata* err, const ErrorOffset& errorOffset) {
err->filename = getFilename();
err->lineNumber = 0;
if (errorOffset.is<uint32_t>()) {
err->columnNumber = errorOffset.as<uint32_t>();
} else if (errorOffset.is<Current>()) {
err->columnNumber = offset();
} else {
errorOffset.is<NoOffset>();
err->columnNumber = 0;
}
err->isMuted = options().mutedErrors();
return true;
}
template <typename Tok>
void BinASTParserPerTokenizer<Tok>::trace(JSTracer* trc) {
BinASTParserBase::trace(trc);
if (tokenizer_) {
tokenizer_->traceMetadata(trc);
}
}
template <typename Tok>
inline typename BinASTParserPerTokenizer<Tok>::FinalParser*
BinASTParserPerTokenizer<Tok>::asFinalParser() {
// Same as GeneralParser::asFinalParser, verify the inheritance to
// make sure the static downcast works.
static_assert(std::is_base_of_v<BinASTParserPerTokenizer<Tok>, FinalParser>,
"inheritance relationship required by the static_cast<> below");
return static_cast<FinalParser*>(this);
}
template <typename Tok>
inline const typename BinASTParserPerTokenizer<Tok>::FinalParser*
BinASTParserPerTokenizer<Tok>::asFinalParser() const {
static_assert(std::is_base_of_v<BinASTParserPerTokenizer<Tok>, FinalParser>,
"inheritance relationship required by the static_cast<> below");
return static_cast<const FinalParser*>(this);
}
// Force class instantiation.
// This ensures that the symbols are built, without having to export all our
// code (and its baggage of #include and macros) in the header.
template class BinASTParserPerTokenizer<BinASTTokenReaderContext>;
template class BinASTParserPerTokenizer<BinASTTokenReaderMultipart>;
} // namespace js::frontend

Просмотреть файл

@ -1,367 +0,0 @@
/* -*- 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/. */
#ifndef frontend_BinASTParserPerTokenizer_h
#define frontend_BinASTParserPerTokenizer_h
/**
* A Binary AST parser.
*
* At the time of this writing, this parser implements the grammar of ES5
* and trusts its input (in particular, variable declarations).
*/
#include "mozilla/Maybe.h"
#include <type_traits>
#include "frontend/BCEParserHandle.h"
#include "frontend/BinASTEnum.h"
#include "frontend/BinASTParserBase.h"
#include "frontend/BinASTToken.h"
#include "frontend/BinASTTokenReaderContext.h"
#include "frontend/BinASTTokenReaderMultipart.h"
#include "frontend/FullParseHandler.h"
#include "frontend/FunctionSyntaxKind.h" // FunctionSyntaxKind
#include "frontend/ParseContext.h"
#include "frontend/ParseNode.h"
#include "frontend/SharedContext.h"
#include "js/CompileOptions.h"
#include "js/GCHashTable.h"
#include "js/GCVector.h"
#include "js/Result.h"
#include "vm/ErrorReporting.h"
#include "vm/GeneratorAndAsyncKind.h" // js::GeneratorKind, js::FunctionAsyncKind
namespace js {
namespace frontend {
template <typename Tok>
class BinASTParser;
/**
* The parser for a Binary AST.
*
* By design, this parser never needs to backtrack or look ahead. Errors are not
* recoverable.
*/
template <typename Tok>
class BinASTParserPerTokenizer : public BinASTParserBase,
public ErrorReporter,
public BCEParserHandle {
public:
using Tokenizer = Tok;
using AutoList = typename Tokenizer::AutoList;
using AutoTaggedTuple = typename Tokenizer::AutoTaggedTuple;
using Chars = typename Tokenizer::Chars;
using RootContext = BinASTTokenReaderBase::RootContext;
using FieldOrRootContext = BinASTTokenReaderBase::FieldOrRootContext;
public:
// Auto-generated types.
using AssertedDeclaredKind = binast::AssertedDeclaredKind;
using VariableDeclarationKind = binast::VariableDeclarationKind;
public:
BinASTParserPerTokenizer(JSContext* cx, CompilationInfo& compilationInfo,
const JS::ReadOnlyCompileOptions& options,
Handle<BaseScript*> lazyScript = nullptr);
/**
* Parse a buffer, returning a node (which may be nullptr) in case of success
* or Nothing() in case of error.
*
* The instance of `ParseNode` MAY NOT survive the
* `BinASTParserPerTokenizer`. Indeed, destruction of the
* `BinASTParserPerTokenizer` will also destroy the `ParseNode`.
*
* In case of error, the parser reports the JS error.
*/
JS::Result<ParseNode*> parse(GlobalSharedContext* globalsc,
const uint8_t* start, const size_t length,
BinASTSourceMetadata** metadataPtr = nullptr);
JS::Result<ParseNode*> parse(GlobalSharedContext* globalsc,
const Vector<uint8_t>& data,
BinASTSourceMetadata** metadataPtr = nullptr);
JS::Result<FunctionNode*> parseLazyFunction(ScriptSource* scriptSource,
const size_t firstOffset);
protected:
MOZ_MUST_USE JS::Result<ParseNode*> parseAux(
GlobalSharedContext* globalsc, const uint8_t* start, const size_t length,
BinASTSourceMetadata** metadataPtr = nullptr);
// --- Raise errors.
//
// These methods return a (failed) JS::Result for convenience.
MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseInvalidClosedVar(
JSAtom* name);
MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&>
raiseMissingVariableInAssertedScope(JSAtom* name);
MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&>
raiseMissingDirectEvalInAssertedScope();
MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseInvalidKind(
const char* superKind, const BinASTKind kind);
MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseInvalidVariant(
const char* kind, const BinASTVariant value);
MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseMissingField(
const char* kind, const BinASTField field);
MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseEmpty(
const char* description);
MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseOOM();
MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseError(
const char* description);
MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseError(
BinASTKind kind, const char* description);
// Ensure that this parser will never be used again.
void poison();
// The owner or the target of Asserted*Scope.
enum class AssertedScopeKind {
Block,
Catch,
Global,
Parameter,
Var,
};
// --- Auxiliary parsing functions
// Build a function object for a function-producing production. Called AFTER
// creating the scope.
JS::Result<FunctionBox*> buildFunctionBox(GeneratorKind generatorKind,
FunctionAsyncKind functionAsyncKind,
FunctionSyntaxKind syntax,
ParseNode* name);
JS::Result<FunctionNode*> makeEmptyFunctionNode(
const size_t start, const FunctionSyntaxKind syntaxKind,
FunctionBox* funbox);
MOZ_MUST_USE JS::Result<Ok> setFunctionParametersAndBody(FunctionNode* fun,
ListNode* params,
ParseNode* body);
MOZ_MUST_USE JS::Result<Ok> finishEagerFunction(FunctionBox* funbox,
uint32_t nargs);
MOZ_MUST_USE JS::Result<Ok> finishLazyFunction(FunctionBox* funbox,
uint32_t nargs, size_t start,
size_t end);
// Add name to a given scope.
MOZ_MUST_USE JS::Result<Ok> addScopeName(
AssertedScopeKind scopeKind, HandleAtom name, ParseContext::Scope* scope,
DeclarationKind declKind, bool isCaptured, bool allowDuplicateName);
void captureFunctionName();
// Map AssertedScopeKind and AssertedDeclaredKind for single binding to
// corresponding ParseContext::Scope to store the binding, and
// DeclarationKind for the binding.
MOZ_MUST_USE JS::Result<Ok> getDeclaredScope(AssertedScopeKind scopeKind,
AssertedDeclaredKind kind,
ParseContext::Scope*& scope,
DeclarationKind& declKind);
MOZ_MUST_USE JS::Result<Ok> getBoundScope(AssertedScopeKind scopeKind,
ParseContext::Scope*& scope,
DeclarationKind& declKind);
MOZ_MUST_USE JS::Result<Ok> checkBinding(JSAtom* name);
MOZ_MUST_USE JS::Result<Ok> checkPositionalParameterIndices(
Handle<GCVector<JSAtom*>> positionalParams, ListNode* params);
MOZ_MUST_USE JS::Result<Ok> checkFunctionLength(uint32_t expectedLength);
// When leaving a scope, check that none of its bindings are known closed over
// and un-marked.
MOZ_MUST_USE JS::Result<Ok> checkClosedVars(ParseContext::Scope& scope);
// As a convenience, a helper that checks the body, parameter, and recursive
// binding scopes.
MOZ_MUST_USE JS::Result<Ok> checkFunctionClosedVars();
// --- Utilities.
MOZ_MUST_USE JS::Result<Ok> prependDirectivesToBody(ListNode* body,
ListNode* directives);
MOZ_MUST_USE JS::Result<Ok> prependDirectivesImpl(ListNode* body,
ParseNode* directive);
// Optionally force a strict context without restarting the parse when we see
// a strict directive.
void forceStrictIfNecessary(SharedContext* sc, ListNode* directives);
// Whether invalid BinASTKind/BinASTVariant can be encoded in the file.
// This is used to avoid generating unnecessary branches for more
// optimized format.
static constexpr bool isInvalidKindPossible() {
return std::is_same_v<Tok, BinASTTokenReaderMultipart>;
}
static constexpr bool isInvalidVariantPossible() {
return std::is_same_v<Tok, BinASTTokenReaderMultipart>;
}
protected:
// Implement ErrorReportMixin.
const JS::ReadOnlyCompileOptions& options_;
const JS::ReadOnlyCompileOptions& options() const override {
return this->options_;
}
JSContext* getContext() const override { return cx_; };
MOZ_MUST_USE bool strictMode() const override { return pc_->sc()->strict(); }
MOZ_MUST_USE bool computeErrorMetadata(ErrorMetadata* err,
const ErrorOffset& offset) override;
private:
void trace(JSTracer* trc) final;
public:
virtual ErrorReporter& errorReporter() override { return *this; }
virtual const ErrorReporter& errorReporter() const override { return *this; }
virtual FullParseHandler& astGenerator() override { return handler_; }
public:
// Implement ErrorReporter.
virtual void lineAndColumnAt(size_t offset, uint32_t* line,
uint32_t* column) const override {
*line = lineAt(offset);
*column = columnAt(offset);
}
virtual uint32_t lineAt(size_t offset) const override { return 0; }
virtual uint32_t columnAt(size_t offset) const override { return offset; }
virtual bool isOnThisLine(size_t offset, uint32_t lineNum,
bool* isOnSameLine) const override {
if (lineNum != 0) {
return false;
}
*isOnSameLine = true;
return true;
}
virtual void currentLineAndColumn(uint32_t* line,
uint32_t* column) const override {
*line = 0;
*column = offset();
}
size_t offset() const {
if (tokenizer_.isSome()) {
return tokenizer_->offset();
}
return 0;
}
virtual bool hasTokenizationStarted() const override {
return tokenizer_.isSome();
}
virtual const char* getFilename() const override {
return this->options_.filename();
}
protected:
Rooted<BaseScript*> lazyScript_;
FullParseHandler handler_;
mozilla::Maybe<Tokenizer> tokenizer_;
VariableDeclarationKind variableDeclarationKind_;
friend class BinASTParseContext;
friend class AutoVariableDeclarationKind;
// Helper class: Restore field `variableDeclarationKind` upon leaving a scope.
class MOZ_RAII AutoVariableDeclarationKind {
public:
explicit AutoVariableDeclarationKind(
BinASTParserPerTokenizer<Tok>* parser MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: parser_(parser), kind(parser->variableDeclarationKind_) {
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
}
~AutoVariableDeclarationKind() { parser_->variableDeclarationKind_ = kind; }
private:
BinASTParserPerTokenizer<Tok>* parser_;
BinASTParserPerTokenizer<Tok>::VariableDeclarationKind kind;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
};
template <typename CharsT>
bool parseRegExpFlags(CharsT flags, size_t length, JS::RegExpFlags* reflags) {
MOZ_ASSERT(*reflags == JS::RegExpFlag::NoFlags);
for (size_t i = 0; i < length; i++) {
auto c = flags[i];
if (c == 'g' && !reflags->global()) {
*reflags |= JS::RegExpFlag::Global;
} else if (c == 'i' && !reflags->ignoreCase()) {
*reflags |= JS::RegExpFlag::IgnoreCase;
} else if (c == 'm' && !reflags->multiline()) {
*reflags |= JS::RegExpFlag::Multiline;
} else if (c == 's' && !reflags->dotAll()) {
*reflags |= JS::RegExpFlag::DotAll;
} else if (c == 'u' && !reflags->unicode()) {
*reflags |= JS::RegExpFlag::Unicode;
} else if (c == 'y' && !reflags->sticky()) {
*reflags |= JS::RegExpFlag::Sticky;
} else {
return false;
}
}
return true;
}
bool parseRegExpFlags(Chars flags, JS::RegExpFlags* reflags) {
return parseRegExpFlags(flags.begin(), flags.end() - flags.begin(),
reflags);
}
bool parseRegExpFlags(HandleAtom flags, JS::RegExpFlags* reflags) {
JS::AutoCheckCannotGC nogc;
if (flags->hasLatin1Chars()) {
return parseRegExpFlags(flags->latin1Chars(nogc), flags->length(),
reflags);
}
return parseRegExpFlags(flags->twoByteChars(nogc), flags->length(),
reflags);
}
private:
// Some methods in this class require access to auto-generated methods in
// BinASTParser which derives this class.
// asFinalParser methods provide the access to BinASTParser class methods
// of this instance.
using FinalParser = BinASTParser<Tok>;
inline FinalParser* asFinalParser();
inline const FinalParser* asFinalParser() const;
};
class BinASTParseContext : public ParseContext {
public:
template <typename Tok>
BinASTParseContext(JSContext* cx, BinASTParserPerTokenizer<Tok>* parser,
SharedContext* sc, Directives* newDirectives)
: ParseContext(cx, parser->pc_, sc, *parser, parser->getCompilationInfo(),
newDirectives, /* isFull = */ true) {}
};
extern template class BinASTParserPerTokenizer<BinASTTokenReaderContext>;
extern template class BinASTParserPerTokenizer<BinASTTokenReaderMultipart>;
} // namespace frontend
} // namespace js
#endif // frontend_BinASTParserPerTokenizer_h

Просмотреть файл

@ -1,113 +0,0 @@
/* -*- 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 "frontend/BinASTRuntimeSupport.h"
#include <stdint.h> // uint32_t
#include "gc/Tracer.h" // TraceManuallyBarrieredEdge
#include "js/Utility.h" // js_malloc
#include "vm/StringType.h" // JSAtom
namespace js::frontend {
BinASTSourceMetadata::~BinASTSourceMetadata() {
if (isMultipart()) {
this->asMultipart()->release();
} else {
this->asContext()->release();
}
}
void BinASTSourceMetadata::trace(JSTracer* tracer) {
if (isMultipart()) {
this->asMultipart()->trace(tracer);
} else {
this->asContext()->trace(tracer);
}
}
/* static */
BinASTSourceMetadataMultipart* BinASTSourceMetadataMultipart::create(
const Vector<BinASTKind>& binASTKinds, uint32_t numStrings) {
uint32_t numBinASTKinds = binASTKinds.length();
// Given we can perform GC before filling all `JSAtom*` items
// in the payload, use js_calloc to initialize them with nullptr.
BinASTSourceMetadataMultipart* data =
static_cast<BinASTSourceMetadataMultipart*>(
js_calloc(BinASTSourceMetadataMultipart::totalSize(numBinASTKinds,
numStrings)));
if (MOZ_UNLIKELY(!data)) {
return nullptr;
}
new (data) BinASTSourceMetadataMultipart(numBinASTKinds, numStrings);
memcpy(data->binASTKindBase(), binASTKinds.begin(),
binASTKinds.length() * sizeof(BinASTKind));
return data;
}
/* static */
BinASTSourceMetadataMultipart* BinASTSourceMetadataMultipart::create(
uint32_t numBinASTKinds, uint32_t numStrings) {
// Given we can perform GC before filling all `JSAtom*` items
// in the payload, use js_calloc to initialize them with nullptr.
BinASTSourceMetadataMultipart* data =
static_cast<BinASTSourceMetadataMultipart*>(
js_calloc(BinASTSourceMetadataMultipart::totalSize(numBinASTKinds,
numStrings)));
if (MOZ_UNLIKELY(!data)) {
return nullptr;
}
new (data) BinASTSourceMetadataMultipart(numBinASTKinds, numStrings);
return data;
}
void BinASTSourceMetadataMultipart::trace(JSTracer* tracer) {
JSAtom** base = atomsBase();
for (uint32_t i = 0; i < numStrings_; i++) {
if (base[i]) {
TraceManuallyBarrieredEdge(tracer, &base[i], "BinAST Strings");
}
}
}
BinASTSourceMetadataContext* BinASTSourceMetadataContext::create(
uint32_t numStrings) {
// Given we can perform GC before filling all `JSAtom*` items
// in the payload, use js_calloc to initialize them with nullptr.
BinASTSourceMetadataContext* data = static_cast<BinASTSourceMetadataContext*>(
js_calloc(BinASTSourceMetadataContext::totalSize(numStrings)));
if (MOZ_UNLIKELY(!data)) {
return nullptr;
}
new (data) BinASTSourceMetadataContext(numStrings);
return data;
}
void BinASTSourceMetadataContext::trace(JSTracer* tracer) {
JSAtom** base = atomsBase();
for (uint32_t i = 0; i < numStrings_; i++) {
if (base[i]) {
TraceManuallyBarrieredEdge(tracer, &base[i], "BinAST Strings");
}
}
}
void BinASTSourceMetadataContext::release() {
if (dictionary_) {
js_free(dictionary_);
dictionary_ = nullptr;
}
}
} // namespace js::frontend

Просмотреть файл

@ -1,285 +0,0 @@
/* -*- 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/. */
#ifndef frontend_BinASTSupport_h
#define frontend_BinASTSupport_h
#include "mozilla/Assertions.h" // MOZ_ASSERT
#include "mozilla/HashFunctions.h" // mozilla::HashString
#include "mozilla/Likely.h" // MOZ_UNLIKELY
#include <stdint.h> // uint8_t, uint32_t
#include <string.h> // strncmp
#include "frontend/BinASTToken.h" // BinASTVariant, BinASTField, BinASTKind
#include "gc/DeletePolicy.h" // GCManagedDeletePolicy
#include "js/AllocPolicy.h" // SystemAllocPolicy
#include "js/HashTable.h" // HashMap, HashNumber
#include "js/Result.h" // JS::Result
#include "js/TracingAPI.h" // JSTracer
#include "js/UniquePtr.h" // UniquePtr
#include "js/Vector.h" // Vector
class JS_PUBLIC_API JSAtom;
struct JS_PUBLIC_API JSContext;
namespace js {
class ScriptSource;
// Support for parsing JS Binary ASTs.
struct BinaryASTSupport {
using BinASTVariant = js::frontend::BinASTVariant;
using BinASTField = js::frontend::BinASTField;
using BinASTKind = js::frontend::BinASTKind;
// A structure designed to perform fast char* + length lookup
// without copies.
struct CharSlice {
const char* start_;
uint32_t byteLen_;
CharSlice(const CharSlice& other) = default;
CharSlice(const char* start, const uint32_t byteLen)
: start_(start), byteLen_(byteLen) {}
explicit CharSlice(JSContext*) : CharSlice(nullptr, 0) {}
const char* begin() const { return start_; }
const char* end() const { return start_ + byteLen_; }
#ifdef DEBUG
void dump() const {
for (auto c : *this) {
fprintf(stderr, "%c", c);
}
fprintf(stderr, " (%u)", byteLen_);
}
#endif // DEBUG
using Lookup = const CharSlice;
static js::HashNumber hash(Lookup l) {
return mozilla::HashString(l.start_, l.byteLen_);
}
static bool match(const Lookup key, Lookup lookup) {
if (key.byteLen_ != lookup.byteLen_) {
return false;
}
return strncmp(key.start_, lookup.start_, key.byteLen_) == 0;
}
};
BinaryASTSupport();
JS::Result<const BinASTVariant*> binASTVariant(JSContext*, const CharSlice);
JS::Result<const BinASTKind*> binASTKind(JSContext*, const CharSlice);
bool ensureBinTablesInitialized(JSContext*);
private:
bool ensureBinASTKindsInitialized(JSContext*);
bool ensureBinASTVariantsInitialized(JSContext*);
private:
// A HashMap that can be queried without copies from a CharSlice key.
// Initialized on first call. Keys are CharSlices into static strings.
using BinASTKindMap = js::HashMap<const CharSlice, BinASTKind, CharSlice,
js::SystemAllocPolicy>;
BinASTKindMap binASTKindMap_;
using BinASTFieldMap = js::HashMap<const CharSlice, BinASTField, CharSlice,
js::SystemAllocPolicy>;
BinASTFieldMap binASTFieldMap_;
using BinASTVariantMap = js::HashMap<const CharSlice, BinASTVariant,
CharSlice, js::SystemAllocPolicy>;
BinASTVariantMap binASTVariantMap_;
};
namespace frontend {
class BinASTSourceMetadataMultipart;
class BinASTSourceMetadataContext;
class BinASTSourceMetadata {
public:
enum class Type : uint8_t {
Multipart = 0,
Context = 1,
};
private:
Type type_;
public:
BinASTSourceMetadata() = delete;
explicit BinASTSourceMetadata(Type type) : type_(type) {}
#ifdef JS_BUILD_BINAST
~BinASTSourceMetadata();
#else
// If JS_BUILD_BINAST isn't defined, BinASTRuntimeSupport.cpp isn't built,
// but still the destructor is referred from ScriptSource::BinAST.
~BinASTSourceMetadata() {}
#endif // JS_BUILD_BINAST
Type type() const { return type_; }
void setType(Type type) { type_ = type; }
bool isMultipart() const { return type_ == Type::Multipart; }
bool isContext() const { return type_ == Type::Context; }
inline BinASTSourceMetadataMultipart* asMultipart();
inline BinASTSourceMetadataContext* asContext();
void trace(JSTracer* tracer);
};
class alignas(uintptr_t) BinASTSourceMetadataMultipart
: public BinASTSourceMetadata {
using CharSlice = BinaryASTSupport::CharSlice;
const uint32_t numStrings_;
const uint32_t numBinASTKinds_;
// The data lives inline in the allocation, after this class.
inline JSAtom** atomsBase() {
return reinterpret_cast<JSAtom**>(reinterpret_cast<uintptr_t>(this + 1));
}
inline CharSlice* sliceBase() {
return reinterpret_cast<CharSlice*>(
reinterpret_cast<uintptr_t>(atomsBase()) +
numStrings_ * sizeof(JSAtom*));
}
inline BinASTKind* binASTKindBase() {
return reinterpret_cast<BinASTKind*>(
reinterpret_cast<uintptr_t>(sliceBase()) +
numStrings_ * sizeof(CharSlice));
}
static inline size_t totalSize(uint32_t numBinASTKinds, uint32_t numStrings) {
return sizeof(BinASTSourceMetadataMultipart) +
numStrings * sizeof(JSAtom*) + numStrings * sizeof(CharSlice) +
numBinASTKinds * sizeof(BinASTKind);
}
BinASTSourceMetadataMultipart(uint32_t numBinASTKinds, uint32_t numStrings)
: BinASTSourceMetadata(Type::Multipart),
numStrings_(numStrings),
numBinASTKinds_(numBinASTKinds) {}
void release() {}
friend class BinASTSourceMetadata;
friend class js::ScriptSource;
public:
// Create the BinASTSourceMetadataMultipart instance, with copying
// `binASTKinds`, and allocating `numStrings` atoms/slices.
//
// Use this when the list of BinASTKind is known at the point of allocating
// metadata, like when parsing .binjs file.
static BinASTSourceMetadataMultipart* create(
const Vector<BinASTKind>& binASTKinds, uint32_t numStrings);
// Create the BinASTSourceMetadataMultipart instance, with allocating
// `numBinASTKinds` BinASTKinds and `numStrings` atoms/slices.
//
// Use this when the list of BinASTKind is unknown at the point of allocating
// metadata, like when performing XDR decode.
static BinASTSourceMetadataMultipart* create(uint32_t numBinASTKinds,
uint32_t numStrings);
inline uint32_t numBinASTKinds() { return numBinASTKinds_; }
inline uint32_t numStrings() { return numStrings_; }
inline BinASTKind& getBinASTKind(uint32_t index) {
MOZ_ASSERT(index < numBinASTKinds_);
return binASTKindBase()[index];
}
inline CharSlice& getSlice(uint32_t index) {
MOZ_ASSERT(index < numStrings_);
return sliceBase()[index];
}
inline JSAtom*& getAtom(uint32_t index) {
MOZ_ASSERT(index < numStrings_);
return atomsBase()[index];
}
void trace(JSTracer* tracer);
};
class HuffmanDictionaryForMetadata;
class alignas(uintptr_t) BinASTSourceMetadataContext
: public BinASTSourceMetadata {
const uint32_t numStrings_;
HuffmanDictionaryForMetadata* dictionary_;
// The data lives inline in the allocation, after this class.
inline JSAtom** atomsBase() {
return reinterpret_cast<JSAtom**>(reinterpret_cast<uintptr_t>(this + 1));
}
static inline size_t totalSize(uint32_t numStrings) {
return sizeof(BinASTSourceMetadataContext) + numStrings * sizeof(JSAtom*);
}
explicit BinASTSourceMetadataContext(uint32_t numStrings)
: BinASTSourceMetadata(Type::Context),
numStrings_(numStrings),
dictionary_(nullptr) {}
void release();
friend class BinASTSourceMetadata;
friend class js::ScriptSource;
public:
// Create the BinASTSourceMetadataContext instance, with allocating
// `numStrings` atoms.
static BinASTSourceMetadataContext* create(uint32_t numStrings);
HuffmanDictionaryForMetadata* dictionary() { return dictionary_; }
void setDictionary(HuffmanDictionaryForMetadata* dictionary) {
MOZ_ASSERT(!dictionary_);
MOZ_ASSERT(dictionary);
dictionary_ = dictionary;
}
inline uint32_t numStrings() { return numStrings_; }
inline JSAtom*& getAtom(uint32_t index) {
MOZ_ASSERT(index < numStrings_);
return atomsBase()[index];
}
void trace(JSTracer* tracer);
};
BinASTSourceMetadataMultipart* BinASTSourceMetadata::asMultipart() {
MOZ_ASSERT(isMultipart());
return reinterpret_cast<BinASTSourceMetadataMultipart*>(this);
}
BinASTSourceMetadataContext* BinASTSourceMetadata::asContext() {
MOZ_ASSERT(isContext());
return reinterpret_cast<BinASTSourceMetadataContext*>(this);
}
} // namespace frontend
using UniqueBinASTSourceMetadataPtr =
UniquePtr<frontend::BinASTSourceMetadata,
GCManagedDeletePolicy<frontend::BinASTSourceMetadata>>;
} // namespace js
#endif // frontend_BinASTSupport_h

Просмотреть файл

@ -1,199 +0,0 @@
/* -*- 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 "frontend/BinASTToken.h"
#include "mozilla/Maybe.h"
#include <sys/types.h>
#include "frontend/BinASTRuntimeSupport.h"
#include "frontend/TokenStream.h"
#include "js/Result.h"
#include "vm/JSContext.h"
namespace js {
namespace frontend {
const BinaryASTSupport::CharSlice BINASTKIND_DESCRIPTIONS[] = {
#define WITH_VARIANT(_, SPEC_NAME, _2) \
BinaryASTSupport::CharSlice(SPEC_NAME, sizeof(SPEC_NAME) - 1),
FOR_EACH_BIN_KIND(WITH_VARIANT)
#undef WITH_VARIANT
};
const BinaryASTSupport::CharSlice BINASTFIELD_DESCRIPTIONS[] = {
#define WITH_VARIANT(_, SPEC_NAME) \
BinaryASTSupport::CharSlice(SPEC_NAME, sizeof(SPEC_NAME) - 1),
FOR_EACH_BIN_FIELD(WITH_VARIANT)
#undef WITH_VARIANT
};
const BinaryASTSupport::CharSlice BINASTVARIANT_DESCRIPTIONS[] = {
#define WITH_VARIANT(_, SPEC_NAME) \
BinaryASTSupport::CharSlice(SPEC_NAME, sizeof(SPEC_NAME) - 1),
FOR_EACH_BIN_VARIANT(WITH_VARIANT)
#undef WITH_VARIANT
};
const BinaryASTSupport::CharSlice BINASTLIST_DESCRIPTIONS[] = {
#define NOTHING(_)
#define EMIT_NAME(_list_name, _content, SPEC_NAME, _type_name) \
BinaryASTSupport::CharSlice(SPEC_NAME, sizeof(SPEC_NAME) - 1),
FOR_EACH_BIN_LIST(EMIT_NAME, NOTHING, NOTHING, NOTHING, NOTHING, NOTHING,
NOTHING, NOTHING, NOTHING)
#undef EMIT_NAME
#undef NOTHING
};
const BinaryASTSupport::CharSlice BINASTINTERFACEANDFIELD_DESCRIPTIONS[] = {
#define WITH_VARIANT(_, SPEC_NAME) \
BinaryASTSupport::CharSlice(SPEC_NAME, sizeof(SPEC_NAME) - 1),
FOR_EACH_BIN_INTERFACE_AND_FIELD(WITH_VARIANT)
#undef WITH_VARIANT
};
const BinaryASTSupport::CharSlice& getBinASTKind(const BinASTKind& variant) {
return BINASTKIND_DESCRIPTIONS[static_cast<size_t>(variant)];
}
const BinaryASTSupport::CharSlice& getBinASTVariant(
const BinASTVariant& variant) {
return BINASTVARIANT_DESCRIPTIONS[static_cast<size_t>(variant)];
}
const BinaryASTSupport::CharSlice& getBinASTField(const BinASTField& variant) {
return BINASTFIELD_DESCRIPTIONS[static_cast<size_t>(variant)];
}
const BinaryASTSupport::CharSlice& getBinASTList(const BinASTList& list) {
return BINASTLIST_DESCRIPTIONS[static_cast<size_t>(list)];
}
const BinaryASTSupport::CharSlice& getBinASTInterfaceAndField(
const BinASTInterfaceAndField& interfaceAndField) {
return BINASTINTERFACEANDFIELD_DESCRIPTIONS[static_cast<size_t>(
interfaceAndField)];
}
const char* describeBinASTKind(const BinASTKind& kind) {
return getBinASTKind(kind).begin();
}
const char* describeBinASTField(const BinASTField& field) {
return getBinASTField(field).begin();
}
const char* describeBinASTVariant(const BinASTVariant& variant) {
return getBinASTVariant(variant).begin();
}
const char* describeBinASTList(const BinASTList& list) {
return getBinASTList(list).begin();
}
const char* describeBinASTInterfaceAndField(
const BinASTInterfaceAndField& interfaceAndField) {
return getBinASTInterfaceAndField(interfaceAndField).begin();
}
size_t getBinASTKindSortKey(const BinASTKind& kind) {
return static_cast<size_t>(kind);
}
size_t getBinASTVariantSortKey(const BinASTVariant& variant) {
return static_cast<size_t>(variant);
}
} // namespace frontend
BinaryASTSupport::BinaryASTSupport()
: binASTKindMap_(frontend::BINASTKIND_LIMIT),
binASTFieldMap_(frontend::BINASTFIELD_LIMIT),
binASTVariantMap_(frontend::BINASTVARIANT_LIMIT) {}
/**
* It is expected that all bin tables are initialized on the main thread, and
* that any helper threads will find the read-only tables properly initialized,
* so that they can do their accesses safely without taking any locks.
*/
bool BinaryASTSupport::ensureBinTablesInitialized(JSContext* cx) {
return ensureBinASTKindsInitialized(cx) &&
ensureBinASTVariantsInitialized(cx);
}
bool BinaryASTSupport::ensureBinASTKindsInitialized(JSContext* cx) {
MOZ_ASSERT(!cx->isHelperThreadContext());
if (binASTKindMap_.empty()) {
for (size_t i = 0; i < frontend::BINASTKIND_LIMIT; ++i) {
const BinASTKind variant = static_cast<BinASTKind>(i);
const CharSlice& key = getBinASTKind(variant);
auto ptr = binASTKindMap_.lookupForAdd(key);
MOZ_ASSERT(!ptr);
if (MOZ_UNLIKELY(!binASTKindMap_.add(ptr, key, variant))) {
ReportOutOfMemory(cx);
return false;
}
}
}
return true;
}
bool BinaryASTSupport::ensureBinASTVariantsInitialized(JSContext* cx) {
MOZ_ASSERT(!cx->isHelperThreadContext());
if (binASTVariantMap_.empty()) {
for (size_t i = 0; i < frontend::BINASTVARIANT_LIMIT; ++i) {
const BinASTVariant variant = static_cast<BinASTVariant>(i);
const CharSlice& key = getBinASTVariant(variant);
auto ptr = binASTVariantMap_.lookupForAdd(key);
MOZ_ASSERT(!ptr);
if (MOZ_UNLIKELY(!binASTVariantMap_.add(ptr, key, variant))) {
ReportOutOfMemory(cx);
return false;
}
}
}
return true;
}
JS::Result<const js::frontend::BinASTKind*> BinaryASTSupport::binASTKind(
JSContext* cx, const CharSlice key) {
MOZ_ASSERT_IF(cx->isHelperThreadContext(), !binASTKindMap_.empty());
if (!cx->isHelperThreadContext()) {
// Initialize Lazily if on main thread.
if (MOZ_UNLIKELY(!ensureBinASTKindsInitialized(cx))) {
return cx->alreadyReportedError();
}
}
auto ptr = binASTKindMap_.readonlyThreadsafeLookup(key);
if (!ptr) {
return nullptr;
}
return &ptr->value();
}
JS::Result<const js::frontend::BinASTVariant*> BinaryASTSupport::binASTVariant(
JSContext* cx, const CharSlice key) {
MOZ_ASSERT_IF(cx->isHelperThreadContext(), !binASTVariantMap_.empty());
if (!cx->isHelperThreadContext()) {
// Initialize lazily if on main thread.
if (MOZ_UNLIKELY(!ensureBinASTVariantsInitialized(cx))) {
return cx->alreadyReportedError();
}
}
auto ptr = binASTVariantMap_.readonlyThreadsafeLookup(key);
if (!ptr) {
return nullptr;
}
return &ptr->value();
}
} // namespace js

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,107 +0,0 @@
/* -*- 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 "frontend/BinASTTokenReaderBase.h"
#include "frontend/BinAST-macros.h"
#include "js/Result.h"
namespace js::frontend {
template <typename T>
using ErrorResult = mozilla::GenericErrorResult<T>;
// We use signalling NaN (which doesn't exist in the JS syntax)
// to represent a `null` number.
const uint64_t NULL_FLOAT_REPRESENTATION = 0x7FF0000000000001;
void BinASTTokenReaderBase::updateLatestKnownGood() {
MOZ_ASSERT(current_ >= start_);
const size_t update = current_ - start_;
MOZ_ASSERT(update >= latestKnownGoodPos_);
latestKnownGoodPos_ = update;
}
ErrorResult<JS::Error&> BinASTTokenReaderBase::raiseError(
const char* description) {
MOZ_ASSERT(!hasRaisedError());
errorReporter_->errorNoOffset(JSMSG_BINAST, description);
return cx_->alreadyReportedError();
}
ErrorResult<JS::Error&> BinASTTokenReaderBase::raiseOOM() {
ReportOutOfMemory(cx_);
return cx_->alreadyReportedError();
}
ErrorResult<JS::Error&> BinASTTokenReaderBase::raiseInvalidNumberOfFields(
const BinASTKind kind, const uint32_t expected, const uint32_t got) {
Sprinter out(cx_);
BINJS_TRY(out.init());
BINJS_TRY(out.printf("In %s, invalid number of fields: expected %u, got %u",
describeBinASTKind(kind), expected, got));
return raiseError(out.string());
}
ErrorResult<JS::Error&> BinASTTokenReaderBase::raiseInvalidField(
const char* kind, const BinASTField field) {
Sprinter out(cx_);
BINJS_TRY(out.init());
BINJS_TRY(out.printf("In %s, invalid field '%s'", kind,
describeBinASTField(field)));
return raiseError(out.string());
}
bool BinASTTokenReaderBase::hasRaisedError() const {
if (cx_->isHelperThreadContext()) {
// When performing off-main-thread parsing, we don't set a pending
// exception but instead add a pending compile error.
return cx_->isCompileErrorPending();
}
return cx_->isExceptionPending();
}
TokenPos BinASTTokenReaderBase::pos() { return pos(offset()); }
TokenPos BinASTTokenReaderBase::pos(size_t start) {
TokenPos pos;
pos.begin = start;
pos.end = current_ - start_;
MOZ_ASSERT(pos.end >= pos.begin);
return pos;
}
void BinASTTokenReaderBase::seek(size_t offset) {
MOZ_ASSERT(start_ + offset >= start_ && start_ + offset < stop_);
current_ = start_ + offset;
}
JS::Result<Ok> BinASTTokenReaderBase::readBuf(uint8_t* bytes, uint32_t len) {
MOZ_ASSERT(!hasRaisedError());
if (MOZ_UNLIKELY(stop_ < current_ + len)) {
return raiseError("Buffer exceeds length");
}
for (uint32_t i = 0; i < len; ++i) {
*bytes++ = *current_++;
}
return Ok();
}
JS::Result<uint8_t> BinASTTokenReaderBase::readByte() {
MOZ_ASSERT(!hasRaisedError());
if (MOZ_UNLIKELY(stop_ == current_)) {
return raiseError("Buffer exceeds length");
}
return *current_++;
}
} // namespace js::frontend

Просмотреть файл

@ -1,239 +0,0 @@
/* -*- 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/. */
#ifndef frontend_BinASTTokenReaderBase_h
#define frontend_BinASTTokenReaderBase_h
#include "mozilla/Variant.h"
#include <string.h>
#include "frontend/BinASTToken.h"
#include "frontend/ErrorReporter.h"
#include "frontend/TokenStream.h"
#include "js/Result.h"
#include "js/TypeDecls.h"
namespace js {
namespace frontend {
// A constant used by tokenizers to represent a null float.
extern const uint64_t NULL_FLOAT_REPRESENTATION;
class MOZ_STACK_CLASS BinASTTokenReaderBase {
public:
template <typename T>
using ErrorResult = mozilla::GenericErrorResult<T>;
// Part of variant `Context`
// Reading the root of the tree, before we enter any tagged tuple.
struct RootContext {};
// Part of variant `Context`
// Reading an element from a list.
struct ListContext {
const BinASTInterfaceAndField position_;
const BinASTList content_;
ListContext(const BinASTInterfaceAndField position,
const BinASTList content)
: position_(position), content_(content) {}
};
// Part of variant `Context`
// Reading a field from an interface.
struct FieldContext {
const BinASTInterfaceAndField position_;
explicit FieldContext(const BinASTInterfaceAndField position)
: position_(position) {}
};
// The context in which we read a token.
using FieldOrRootContext = mozilla::Variant<FieldContext, RootContext>;
using FieldOrListContext = mozilla::Variant<FieldContext, ListContext>;
#ifdef DEBUG
// Utility matcher, used to print a `Context` during debugging.
struct ContextPrinter {
static void print(const char* text, const FieldOrRootContext& context) {
fprintf(stderr, "%s ", text);
context.match(ContextPrinter());
fprintf(stderr, "\n");
}
static void print(const char* text, const FieldOrListContext& context) {
fprintf(stderr, "%s ", text);
context.match(ContextPrinter());
fprintf(stderr, "\n");
}
void operator()(const RootContext&) { fprintf(stderr, "<Root context>"); }
void operator()(const ListContext& context) {
fprintf(stderr, "<List context>: %s => %s",
describeBinASTInterfaceAndField(context.position_),
describeBinASTList(context.content_));
}
void operator()(const FieldContext& context) {
fprintf(stderr, "<Field context>: %s",
describeBinASTInterfaceAndField(context.position_));
}
};
#endif // DEBUG
// The information needed to skip a subtree.
class SkippableSubTree {
public:
SkippableSubTree(const size_t startOffset, const size_t length)
: startOffset_(startOffset), length_(length) {}
// The position in the source buffer at which the subtree starts.
//
// `SkippableSubTree` does *not* attempt to keep anything alive.
size_t startOffset() const { return startOffset_; }
// The length of the subtree.
size_t length() const { return length_; }
private:
const size_t startOffset_;
const size_t length_;
};
/**
* Return the position of the latest token.
*/
TokenPos pos();
TokenPos pos(size_t startOffset);
size_t offset() const { return current_ - start_; }
// Set the tokenizer's cursor in the file. Use with caution.
void seek(size_t offset);
/**
* Poison this tokenizer.
*/
void poison();
/**
* Raise an error.
*
* Once `raiseError` has been called, the tokenizer is poisoned.
*/
MOZ_MUST_USE ErrorResult<JS::Error&> raiseError(const char* description);
MOZ_MUST_USE ErrorResult<JS::Error&> raiseOOM();
MOZ_MUST_USE ErrorResult<JS::Error&> raiseInvalidNumberOfFields(
const BinASTKind kind, const uint32_t expected, const uint32_t got);
MOZ_MUST_USE ErrorResult<JS::Error&> raiseInvalidField(
const char* kind, const BinASTField field);
protected:
BinASTTokenReaderBase(JSContext* cx, ErrorReporter* er, const uint8_t* start,
const size_t length)
: cx_(cx),
errorReporter_(er),
poisoned_(false),
start_(start),
current_(start),
stop_(start + length),
latestKnownGoodPos_(0) {
MOZ_ASSERT(errorReporter_);
}
/**
* Read a single byte.
*/
MOZ_MUST_USE JS::Result<uint8_t> readByte();
/**
* Read several bytes.
*
* If there is not enough data, or if the tokenizer has previously been
* poisoned, return an error.
*/
MOZ_MUST_USE JS::Result<Ok> readBuf(uint8_t* bytes, uint32_t len);
/**
* Read a sequence of chars, ensuring that they match an expected
* sequence of chars.
*
* @param value The sequence of chars to expect, NUL-terminated.
*/
template <size_t N>
MOZ_MUST_USE JS::Result<Ok> readConst(const char (&value)[N]) {
updateLatestKnownGood();
if (MOZ_UNLIKELY(!matchConst(value, false))) {
return raiseError("Could not find expected literal");
}
return Ok();
}
/**
* Read a sequence of chars, consuming the bytes only if they match an
* expected sequence of chars.
*
* @param value The sequence of chars to expect, NUL-terminated.
* @param expectNul If true, expect NUL in the stream, otherwise don't.
* @return true if `value` represents the next few chars in the
* internal buffer, false otherwise. If `true`, the chars are consumed,
* otherwise there is no side-effect.
*/
template <size_t N>
MOZ_MUST_USE bool matchConst(const char (&value)[N], bool expectNul) {
MOZ_ASSERT(N > 0);
MOZ_ASSERT(value[N - 1] == 0);
MOZ_ASSERT(!hasRaisedError());
if (MOZ_UNLIKELY(current_ + N - 1 > stop_)) {
return false;
}
#ifndef FUZZING
// Perform lookup, without side-effects.
if (memcmp(current_, value, N + (expectNul ? 0 : -1) /*implicit NUL*/) !=
0) {
return false;
}
#endif
// Looks like we have a match. Now perform side-effects
current_ += N + (expectNul ? 0 : -1);
updateLatestKnownGood();
return true;
}
void updateLatestKnownGood();
bool hasRaisedError() const;
JSContext* cx_;
ErrorReporter* errorReporter_;
// `true` if we have encountered an error. Errors are non recoverable.
// Attempting to read from a poisoned tokenizer will cause assertion errors.
bool poisoned_;
// The first byte of the buffer. Not owned.
const uint8_t* start_;
// The current position.
const uint8_t* current_;
// The last+1 byte of the buffer.
const uint8_t* stop_;
// Latest known good position. Used for error reporting.
size_t latestKnownGoodPos_;
private:
BinASTTokenReaderBase(const BinASTTokenReaderBase&) = delete;
BinASTTokenReaderBase(BinASTTokenReaderBase&&) = delete;
BinASTTokenReaderBase& operator=(BinASTTokenReaderBase&) = delete;
};
} // namespace frontend
} // namespace js
#endif // frontend_BinASTTokenReaderBase_h

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,476 +0,0 @@
/* -*- 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 "frontend/BinASTTokenReaderMultipart.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Casting.h"
#include "mozilla/EndianUtils.h"
#include "mozilla/Maybe.h"
#include "mozilla/ScopeExit.h"
#include <utility>
#include "frontend/BinAST-macros.h"
#include "frontend/BinASTRuntimeSupport.h"
#include "frontend/BytecodeCompiler.h" // IsIdentifier
#include "js/Result.h"
namespace js::frontend {
// The magic header, at the start of every binjs file.
const char MAGIC_HEADER[] = "BINJS";
// The latest format version understood by this tokenizer.
const uint32_t MAGIC_FORMAT_VERSION = 1;
// The headers at the start of each section of the binjs file.
const char SECTION_HEADER_GRAMMAR[] = "[GRAMMAR]";
const char SECTION_HEADER_STRINGS[] = "[STRINGS]";
const char SECTION_HEADER_TREE[] = "[TREE]";
// The (only) internal compression mechanism understood by this parser.
const char COMPRESSION_IDENTITY[] = "identity;";
// The maximal number of distinct strings that may be declared in a
// single file.
const uint32_t MAX_NUMBER_OF_STRINGS = 32768;
using AutoList = BinASTTokenReaderMultipart::AutoList;
using AutoTaggedTuple = BinASTTokenReaderMultipart::AutoTaggedTuple;
using CharSlice = BinaryASTSupport::CharSlice;
using Chars = BinASTTokenReaderMultipart::Chars;
BinASTTokenReaderMultipart::BinASTTokenReaderMultipart(JSContext* cx,
ErrorReporter* er,
const uint8_t* start,
const size_t length)
: BinASTTokenReaderBase(cx, er, start, length),
metadata_(nullptr),
posBeforeTree_(nullptr) {
MOZ_ASSERT(er);
}
BinASTTokenReaderMultipart::~BinASTTokenReaderMultipart() {
if (metadata_ && metadataOwned_ == MetadataOwnership::Owned) {
UniqueBinASTSourceMetadataPtr ptr(metadata_);
}
}
BinASTSourceMetadata* BinASTTokenReaderMultipart::takeMetadata() {
MOZ_ASSERT(metadataOwned_ == MetadataOwnership::Owned);
metadataOwned_ = MetadataOwnership::Unowned;
return metadata_;
}
JS::Result<Ok> BinASTTokenReaderMultipart::initFromScriptSource(
ScriptSource* scriptSource) {
metadata_ = scriptSource->binASTSourceMetadata()->asMultipart();
metadataOwned_ = MetadataOwnership::Unowned;
return Ok();
}
JS::Result<Ok> BinASTTokenReaderMultipart::readHeader() {
// Check that we don't call this function twice.
MOZ_ASSERT(!posBeforeTree_);
// Read global headers.
MOZ_TRY(readConst(MAGIC_HEADER));
BINJS_MOZ_TRY_DECL(version, readInternalUint32());
// For the moment, MAGIC_FORMAT_VERSION is 0. Once we have a story
// on backwards compatibility of the binary container, we will
// probably want to change this to `if (version > MAGIC_FORMAT_VERSION)`.
if (version != MAGIC_FORMAT_VERSION) {
return raiseError("Format version not implemented");
}
// Start reading grammar.
MOZ_TRY(readConst(SECTION_HEADER_GRAMMAR));
MOZ_TRY(readConst(COMPRESSION_IDENTITY)); // For the moment, we only support
// identity compression.
BINJS_MOZ_TRY_DECL(grammarByteLen, readInternalUint32());
const auto posBeforeGrammar = current_;
if (posBeforeGrammar + grammarByteLen > stop_ ||
posBeforeGrammar + grammarByteLen < current_) { // Sanity check.
return raiseError("Invalid byte length in grammar table");
}
BINJS_MOZ_TRY_DECL(grammarNumberOfEntries, readInternalUint32());
if (grammarNumberOfEntries > BINASTKIND_LIMIT) { // Sanity check.
return raiseError("Invalid number of entries in grammar table");
}
// This table maps BinASTKind index -> BinASTKind.
// Initialize and populate.
Vector<BinASTKind> grammarTable_(cx_);
if (!grammarTable_.reserve(grammarNumberOfEntries)) {
return raiseOOM();
}
for (uint32_t i = 0; i < grammarNumberOfEntries; ++i) {
BINJS_MOZ_TRY_DECL(byteLen, readInternalUint32());
if (current_ + byteLen > stop_) {
return raiseError("Invalid byte length in grammar table");
}
if (current_ + byteLen < current_) { // Overflow.
return raiseError("Invalid byte length in grammar table");
}
CharSlice name((const char*)current_, byteLen);
current_ += byteLen;
BINJS_MOZ_TRY_DECL(kind, cx_->runtime()->binast().binASTKind(cx_, name));
if (!kind) {
return raiseError("Invalid entry in grammar table");
}
grammarTable_.infallibleAppend(
*kind); // We called `reserve` before the loop.
}
if (current_ != grammarByteLen + posBeforeGrammar) {
return raiseError(
"The length of the grammar table didn't match its contents.");
}
// Start reading strings
MOZ_TRY(readConst(SECTION_HEADER_STRINGS));
MOZ_TRY(readConst(COMPRESSION_IDENTITY)); // For the moment, we only support
// identity compression.
BINJS_MOZ_TRY_DECL(stringsByteLen, readInternalUint32());
const auto posBeforeStrings = current_;
if (posBeforeStrings + stringsByteLen > stop_ ||
posBeforeStrings + stringsByteLen < current_) { // Sanity check.
return raiseError("Invalid byte length in strings table");
}
BINJS_MOZ_TRY_DECL(stringsNumberOfEntries, readInternalUint32());
if (stringsNumberOfEntries > MAX_NUMBER_OF_STRINGS) { // Sanity check.
return raiseError("Too many entries in strings table");
}
BinASTSourceMetadataMultipart* metadata =
BinASTSourceMetadataMultipart::create(grammarTable_,
stringsNumberOfEntries);
if (!metadata) {
return raiseOOM();
}
// Free it if we don't make it out of here alive. Since we don't want to
// calloc(), we need to avoid marking atoms that might not be there.
auto se = mozilla::MakeScopeExit([metadata]() { js_free(metadata); });
RootedAtom atom(cx_);
for (uint32_t i = 0; i < stringsNumberOfEntries; ++i) {
BINJS_MOZ_TRY_DECL(byteLen, readInternalUint32());
if (current_ + byteLen > stop_ || current_ + byteLen < current_) {
return raiseError("Invalid byte length in individual string");
}
// Check null string.
if (byteLen == 2 && *current_ == 255 && *(current_ + 1) == 0) {
atom = nullptr;
} else {
BINJS_TRY_VAR(atom,
AtomizeWTF8Chars(cx_, (const char*)current_, byteLen));
}
metadata->getAtom(i) = atom;
// Populate `slicesTable_`: i => slice
new (&metadata->getSlice(i)) Chars((const char*)current_, byteLen);
current_ += byteLen;
}
if (posBeforeStrings + stringsByteLen != current_) {
return raiseError(
"The length of the strings table didn't match its contents.");
}
MOZ_ASSERT(!metadata_);
se.release();
metadata_ = metadata;
metadataOwned_ = MetadataOwnership::Owned;
// Start reading AST.
MOZ_TRY(readConst(SECTION_HEADER_TREE));
MOZ_TRY(readConst(COMPRESSION_IDENTITY)); // For the moment, we only support
// identity compression.
posBeforeTree_ = current_;
BINJS_MOZ_TRY_DECL(treeByteLen, readInternalUint32());
if (posBeforeTree_ + treeByteLen > stop_ ||
posBeforeTree_ + treeByteLen < posBeforeTree_) { // Sanity check.
return raiseError("Invalid byte length in tree table");
}
// At this stage, we're ready to start reading the tree.
return Ok();
}
void BinASTTokenReaderMultipart::traceMetadata(JSTracer* trc) {
if (metadata_) {
metadata_->trace(trc);
}
}
JS::Result<bool> BinASTTokenReaderMultipart::readBool(const FieldContext&) {
updateLatestKnownGood();
BINJS_MOZ_TRY_DECL(byte, readByte());
switch (byte) {
case 0:
return false;
case 1:
return true;
case 2:
return raiseError("Not implemented: null boolean value");
default:
return raiseError("Invalid boolean value");
}
}
// Nullable doubles (little-endian)
//
// NULL_FLOAT_REPRESENTATION (signaling NaN) => null
// anything other 64 bit sequence => IEEE-764 64-bit floating point number
JS::Result<double> BinASTTokenReaderMultipart::readDouble(const FieldContext&) {
updateLatestKnownGood();
uint8_t bytes[8];
MOZ_ASSERT(sizeof(bytes) == sizeof(double));
MOZ_TRY(
readBuf(reinterpret_cast<uint8_t*>(bytes), mozilla::ArrayLength(bytes)));
// Decode little-endian.
const uint64_t asInt = mozilla::LittleEndian::readUint64(bytes);
if (asInt == NULL_FLOAT_REPRESENTATION) {
return raiseError("Not implemented: null double value");
}
// Canonicalize NaN, just to make sure another form of signalling NaN
// doesn't slip past us.
return JS::CanonicalizeNaN(mozilla::BitwiseCast<double>(asInt));
}
// A single atom is represented as an index into the table of strings.
JS::Result<JSAtom*> BinASTTokenReaderMultipart::readMaybeAtom(
const FieldContext&) {
updateLatestKnownGood();
BINJS_MOZ_TRY_DECL(index, readInternalUint32());
if (index >= metadata_->numStrings()) {
return raiseError("Invalid index to strings table");
}
return metadata_->getAtom(index);
}
JS::Result<JSAtom*> BinASTTokenReaderMultipart::readAtom(
const FieldContext& context) {
BINJS_MOZ_TRY_DECL(maybe, readMaybeAtom(context));
if (!maybe) {
return raiseError("Empty string");
}
return maybe;
}
JS::Result<JSAtom*> BinASTTokenReaderMultipart::readMaybeIdentifierName(
const FieldContext& context) {
BINJS_MOZ_TRY_DECL(result, readMaybeAtom(context));
if (result) {
if (!IsIdentifier(result)) {
return raiseError("Invalid identifier");
}
}
return result;
}
JS::Result<JSAtom*> BinASTTokenReaderMultipart::readIdentifierName(
const FieldContext& context) {
BINJS_MOZ_TRY_DECL(result, readAtom(context));
if (!IsIdentifier(result)) {
return raiseError("Invalid identifier");
}
return result;
}
JS::Result<JSAtom*> BinASTTokenReaderMultipart::readPropertyKey(
const FieldContext& context) {
return readAtom(context);
}
JS::Result<Ok> BinASTTokenReaderMultipart::readChars(Chars& out,
const FieldContext&) {
updateLatestKnownGood();
BINJS_MOZ_TRY_DECL(index, readInternalUint32());
if (index >= metadata_->numStrings()) {
return raiseError("Invalid index to strings table for string enum");
}
out = metadata_->getSlice(index);
return Ok();
}
JS::Result<BinASTVariant> BinASTTokenReaderMultipart::readVariant() {
updateLatestKnownGood();
BINJS_MOZ_TRY_DECL(index, readInternalUint32());
if (index >= metadata_->numStrings()) {
return raiseError("Invalid index to strings table for string enum");
}
auto variantsPtr = variantsTable_.lookupForAdd(index);
if (variantsPtr) {
return variantsPtr->value();
}
// Either we haven't cached the result yet or this is not a variant.
// Check in the slices table and, in case of success, cache the result.
// Note that we stop parsing if we attempt to readVariant() with an
// ill-formed variant, so we don't run the risk of feching an ill-variant
// more than once.
Chars slice = metadata_->getSlice(index); // We have checked `index` above.
BINJS_MOZ_TRY_DECL(variant,
cx_->runtime()->binast().binASTVariant(cx_, slice));
if (!variant) {
return raiseError("Invalid string enum variant");
}
if (!variantsTable_.add(variantsPtr, index, *variant)) {
return raiseOOM();
}
return *variant;
}
JS::Result<BinASTTokenReaderBase::SkippableSubTree>
BinASTTokenReaderMultipart::readSkippableSubTree(const FieldContext&) {
updateLatestKnownGood();
BINJS_MOZ_TRY_DECL(byteLen, readInternalUint32());
if (current_ + byteLen > stop_ || current_ + byteLen < current_) {
return raiseError("Invalid byte length in readSkippableSubTree");
}
const auto start = offset();
current_ += byteLen;
return BinASTTokenReaderBase::SkippableSubTree(start, byteLen);
}
// Tagged tuples:
// - uint32_t index in table [grammar];
// - content (specified by the higher-level grammar);
JS::Result<Ok> BinASTTokenReaderMultipart::enterTaggedTuple(BinASTKind& tag) {
BINJS_MOZ_TRY_DECL(index, readInternalUint32());
if (index >= metadata_->numBinASTKinds()) {
return raiseError("Invalid index to grammar table");
}
tag = metadata_->getBinASTKind(index);
return Ok();
}
// List:
//
// - uint32_t number of items;
// - contents (specified by higher-level grammar);
//
// The total byte length of `number of items` + `contents` must be `byte
// length`.
JS::Result<Ok> BinASTTokenReaderMultipart::enterList(uint32_t& items,
const ListContext&) {
MOZ_TRY_VAR(items, readInternalUint32());
return Ok();
}
void BinASTTokenReaderMultipart::AutoBase::init() { initialized_ = true; }
BinASTTokenReaderMultipart::AutoBase::AutoBase(
BinASTTokenReaderMultipart& reader)
: initialized_(false), reader_(reader) {}
BinASTTokenReaderMultipart::AutoBase::~AutoBase() {
// By now, the `AutoBase` must have been deinitialized by calling `done()`.
// The only case in which we can accept not calling `done()` is if we have
// bailed out because of an error.
MOZ_ASSERT_IF(initialized_, reader_.hasRaisedError());
}
BinASTTokenReaderMultipart::AutoList::AutoList(
BinASTTokenReaderMultipart& reader)
: AutoBase(reader) {}
JS::Result<Ok> BinASTTokenReaderMultipart::AutoList::done() {
MOZ_ASSERT(initialized_);
initialized_ = false;
if (reader_.hasRaisedError()) {
// Already errored, no need to check further.
return reader_.cx_->alreadyReportedError();
}
return Ok();
}
// Internal uint32_t
//
// Encoded as variable length number.
MOZ_MUST_USE JS::Result<uint32_t>
BinASTTokenReaderMultipart::readInternalUint32() {
uint32_t result = 0;
uint32_t shift = 0;
while (true) {
MOZ_ASSERT(shift < 32);
uint32_t byte;
MOZ_TRY_VAR(byte, readByte());
const uint32_t newResult = result | (byte >> 1) << shift;
if (newResult < result) {
return raiseError("Overflow during readInternalUint32");
}
result = newResult;
shift += 7;
if ((byte & 1) == 0) {
return result;
}
if (shift >= 32) {
return raiseError("Overflow during readInternalUint32");
}
}
}
BinASTTokenReaderMultipart::AutoTaggedTuple::AutoTaggedTuple(
BinASTTokenReaderMultipart& reader)
: AutoBase(reader) {}
JS::Result<Ok> BinASTTokenReaderMultipart::AutoTaggedTuple::done() {
MOZ_ASSERT(initialized_);
initialized_ = false;
if (reader_.hasRaisedError()) {
// Already errored, no need to check further.
return reader_.cx_->alreadyReportedError();
}
return Ok();
}
} // namespace js::frontend

Просмотреть файл

@ -1,382 +0,0 @@
/* -*- 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/. */
#ifndef frontend_BinASTTokenReaderMultipart_h
#define frontend_BinASTTokenReaderMultipart_h
#include "mozilla/Maybe.h"
#include "frontend/BinASTRuntimeSupport.h"
#include "frontend/BinASTToken.h"
#include "frontend/BinASTTokenReaderBase.h"
#include "js/Result.h"
namespace js {
namespace frontend {
/**
* A token reader implementing the "multipart" serialization format for BinAST.
*
* This serialization format, which is also supported by the reference
* implementation of the BinAST compression suite, is designed to be
* space- and time-efficient.
*
* As other token readers for the BinAST:
*
* - the reader does not support error recovery;
* - the reader does not support lookahead or pushback.
*/
class MOZ_STACK_CLASS BinASTTokenReaderMultipart
: public BinASTTokenReaderBase {
public:
class AutoList;
class AutoTaggedTuple;
using CharSlice = BinaryASTSupport::CharSlice;
using RootContext = BinASTTokenReaderBase::RootContext;
using ListContext = BinASTTokenReaderBase::ListContext;
using FieldContext = BinASTTokenReaderBase::FieldContext;
using FieldOrRootContext = BinASTTokenReaderBase::FieldOrRootContext;
using FieldOrListContext = BinASTTokenReaderBase::FieldOrListContext;
using Chars = CharSlice;
public:
/**
* Construct a token reader.
*
* Does NOT copy the buffer.
*/
BinASTTokenReaderMultipart(JSContext* cx, ErrorReporter* er,
const uint8_t* start, const size_t length);
/**
* Construct a token reader.
*
* Does NOT copy the buffer.
*/
BinASTTokenReaderMultipart(JSContext* cx, ErrorReporter* er,
const Vector<uint8_t>& chars);
~BinASTTokenReaderMultipart();
/**
* Read the header of the file.
*/
MOZ_MUST_USE JS::Result<Ok> readHeader();
/**
* Read the footer of the tree, that multipart format doesn't have.
*/
MOZ_MUST_USE JS::Result<Ok> readTreeFooter() { return Ok(); }
// --- Primitive values.
//
// Note that the underlying format allows for a `null` value for primitive
// values.
//
// Reading will return an error either in case of I/O error or in case of
// a format problem. Reading if an exception in pending is an error and
// will cause assertion failures. Do NOT attempt to read once an exception
// has been cleared: the token reader does NOT support recovery, by design.
/**
* Read a single `true | false` value.
*/
MOZ_MUST_USE JS::Result<bool> readBool(const FieldContext&);
/**
* Read a single `number` value.
*/
MOZ_MUST_USE JS::Result<double> readDouble(const FieldContext&);
/**
* Read a single `string | null` value.
*
* Fails if that string is not valid UTF-8.
*/
MOZ_MUST_USE JS::Result<JSAtom*> readMaybeAtom(const FieldContext&);
MOZ_MUST_USE JS::Result<JSAtom*> readAtom(const FieldContext&);
/**
* Read a single IdentifierName value.
*/
MOZ_MUST_USE JS::Result<JSAtom*> readMaybeIdentifierName(const FieldContext&);
MOZ_MUST_USE JS::Result<JSAtom*> readIdentifierName(const FieldContext&);
/**
* Read a single PropertyKey value.
*/
MOZ_MUST_USE JS::Result<JSAtom*> readPropertyKey(const FieldContext&);
/**
* Read a single `string | null` value.
*
* MAY check if that string is not valid UTF-8.
*/
MOZ_MUST_USE JS::Result<Ok> readChars(Chars&, const FieldContext&);
/**
* Read a single `BinASTVariant | null` value.
*/
private:
MOZ_MUST_USE JS::Result<BinASTVariant> readVariant();
public:
MOZ_MUST_USE JS::Result<BinASTVariant> readVariant(
const ListContext& context) {
return readVariant();
}
MOZ_MUST_USE JS::Result<BinASTVariant> readVariant(
const FieldContext& context) {
return readVariant();
}
/**
* Read over a single `[Skippable]` subtree value.
*
* This does *not* attempt to parse the subtree itself. Rather, the
* returned `SkippableSubTree` contains the necessary information
* to parse/tokenize the subtree at a later stage
*/
MOZ_MUST_USE JS::Result<SkippableSubTree> readSkippableSubTree(
const FieldContext&);
/**
* Register lazy script for later modification.
* Not used in multipart format.
*/
MOZ_MUST_USE JS::Result<Ok> registerLazyScript(FunctionBox* lazy) {
return Ok();
}
// --- Composite values.
//
// The underlying format does NOT allows for a `null` composite value.
//
// Reading will return an error either in case of I/O error or in case of
// a format problem. Reading from a poisoned tokenizer is an error and
// will cause assertion failures.
/**
* Start reading a list.
*
* @param length (OUT) The number of elements in the list.
* @param guard (OUT) A guard, ensuring that we read the list correctly.
*
* The `guard` is dedicated to ensuring that reading the list has consumed
* exactly all the bytes from that list. The `guard` MUST therefore be
* destroyed at the point where the caller has reached the end of the list.
* If the caller has consumed too few/too many bytes, this will be reported
* in the call go `guard.done()`.
*/
MOZ_MUST_USE JS::Result<Ok> enterList(uint32_t& length, const ListContext&);
/**
* Start reading a tagged tuple.
*
* @param tag (OUT) The tag of the tuple.
* @param fields Ignored, provided for API compatibility.
* @param guard (OUT) A guard, ensuring that we read the tagged tuple
* correctly.
*
* The `guard` is dedicated to ensuring that reading the list has consumed
* exactly all the bytes from that tuple. The `guard` MUST therefore be
* destroyed at the point where the caller has reached the end of the tuple.
* If the caller has consumed too few/too many bytes, this will be reported
* in the call go `guard.done()`.
*
* @return out If the header of the tuple is invalid.
*/
MOZ_MUST_USE JS::Result<Ok> enterTaggedTuple(BinASTKind& tag);
MOZ_MUST_USE JS::Result<Ok> enterTaggedTuple(BinASTKind& tag,
const FieldOrRootContext&) {
return enterTaggedTuple(tag);
}
MOZ_MUST_USE JS::Result<Ok> enterTaggedTuple(BinASTKind& tag,
const FieldOrListContext&) {
return enterTaggedTuple(tag);
}
MOZ_MUST_USE JS::Result<Ok> enterTaggedTuple(BinASTKind& tag,
const RootContext&) {
return enterTaggedTuple(tag);
}
MOZ_MUST_USE JS::Result<Ok> enterTaggedTuple(BinASTKind& tag,
const ListContext&) {
return enterTaggedTuple(tag);
}
MOZ_MUST_USE JS::Result<Ok> enterTaggedTuple(BinASTKind& tag,
const FieldContext&) {
return enterTaggedTuple(tag);
}
MOZ_MUST_USE JS::Result<Ok> enterInterface(BinASTKind& tag,
const FieldOrRootContext&) {
return enterTaggedTuple(tag);
}
MOZ_MUST_USE JS::Result<Ok> enterInterface(BinASTKind& tag,
const FieldOrListContext&) {
return enterTaggedTuple(tag);
}
MOZ_MUST_USE JS::Result<Ok> enterInterface(BinASTKind& tag,
const RootContext&) {
return enterTaggedTuple(tag);
}
MOZ_MUST_USE JS::Result<Ok> enterInterface(BinASTKind& tag,
const ListContext&) {
return enterTaggedTuple(tag);
}
MOZ_MUST_USE JS::Result<Ok> enterInterface(BinASTKind& tag,
const FieldContext&) {
return enterTaggedTuple(tag);
}
MOZ_MUST_USE JS::Result<Ok> enterSum(BinASTKind& tag,
const FieldOrRootContext&) {
return enterTaggedTuple(tag);
}
MOZ_MUST_USE JS::Result<Ok> enterSum(BinASTKind& tag,
const FieldOrListContext&) {
return enterTaggedTuple(tag);
}
MOZ_MUST_USE JS::Result<Ok> enterSum(BinASTKind& tag, const RootContext&) {
return enterTaggedTuple(tag);
}
MOZ_MUST_USE JS::Result<Ok> enterSum(BinASTKind& tag, const ListContext&) {
return enterTaggedTuple(tag);
}
MOZ_MUST_USE JS::Result<Ok> enterSum(BinASTKind& tag, const FieldContext&) {
return enterTaggedTuple(tag);
}
MOZ_MUST_USE JS::Result<Ok> enterOptionalInterface(
BinASTKind& tag, const FieldOrRootContext&) {
return enterTaggedTuple(tag);
}
MOZ_MUST_USE JS::Result<Ok> enterOptionalInterface(
BinASTKind& tag, const FieldOrListContext&) {
return enterTaggedTuple(tag);
}
MOZ_MUST_USE JS::Result<Ok> enterOptionalInterface(BinASTKind& tag,
const RootContext&) {
return enterTaggedTuple(tag);
}
MOZ_MUST_USE JS::Result<Ok> enterOptionalInterface(BinASTKind& tag,
const ListContext&) {
return enterTaggedTuple(tag);
}
MOZ_MUST_USE JS::Result<Ok> enterOptionalInterface(BinASTKind& tag,
const FieldContext&) {
return enterTaggedTuple(tag);
}
/**
* Read a single unsigned long.
*/
MOZ_MUST_USE JS::Result<uint32_t> readUnsignedLong(const FieldContext&) {
return readInternalUint32();
}
private:
/**
* Read a single uint32_t.
*/
MOZ_MUST_USE JS::Result<uint32_t> readInternalUint32();
private:
// A mapping string index => BinASTVariant as extracted from the [STRINGS]
// section of the file. Populated lazily.
js::HashMap<uint32_t, BinASTVariant, DefaultHasher<uint32_t>,
SystemAllocPolicy>
variantsTable_;
enum class MetadataOwnership { Owned, Unowned };
MetadataOwnership metadataOwned_ = MetadataOwnership::Owned;
BinASTSourceMetadataMultipart* metadata_;
const uint8_t* posBeforeTree_;
BinASTTokenReaderMultipart(const BinASTTokenReaderMultipart&) = delete;
BinASTTokenReaderMultipart(BinASTTokenReaderMultipart&&) = delete;
BinASTTokenReaderMultipart& operator=(BinASTTokenReaderMultipart&) = delete;
public:
void traceMetadata(JSTracer* trc);
BinASTSourceMetadata* takeMetadata();
MOZ_MUST_USE JS::Result<Ok> initFromScriptSource(ScriptSource* scriptSource);
public:
// The following classes are used whenever we encounter a tuple/tagged
// tuple/list to make sure that:
//
// - if the construct "knows" its byte length, we have exactly consumed all
// the bytes (otherwise, this means that the file is corrupted, perhaps on
// purpose, so we need to reject the stream);
// - if the construct has a footer, once we are done reading it, we have
// reached the footer (this is to aid with debugging).
//
// In either case, the caller MUST call method `done()` of the guard once
// it is done reading the tuple/tagged tuple/list, to report any pending
// error.
// Base class used by other Auto* classes.
class MOZ_STACK_CLASS AutoBase {
protected:
explicit AutoBase(BinASTTokenReaderMultipart& reader);
~AutoBase();
friend BinASTTokenReaderMultipart;
public:
void init();
protected:
// Set to `true` if `init()` has been called. Reset to `false` once
// all conditions have been checked.
bool initialized_;
BinASTTokenReaderMultipart& reader_;
};
// Guard class used to ensure that `enterList` is used properly.
class MOZ_STACK_CLASS AutoList : public AutoBase {
public:
explicit AutoList(BinASTTokenReaderMultipart& reader);
// Check that we have properly read to the end of the list.
MOZ_MUST_USE JS::Result<Ok> done();
protected:
friend BinASTTokenReaderMultipart;
};
// Guard class used to ensure that `enterTaggedTuple` is used properly.
class MOZ_STACK_CLASS AutoTaggedTuple : public AutoBase {
public:
explicit AutoTaggedTuple(BinASTTokenReaderMultipart& reader);
// Check that we have properly read to the end of the tuple.
MOZ_MUST_USE JS::Result<Ok> done();
};
// Compare a `Chars` and a string literal (ONLY a string literal).
template <size_t N>
static bool equals(const Chars& left, const char (&right)[N]) {
MOZ_ASSERT(N > 0);
MOZ_ASSERT(right[N - 1] == 0);
if (left.byteLen_ + 1 /* implicit NUL */ != N) {
return false;
}
if (!std::equal(left.start_, left.start_ + left.byteLen_, right)) {
return false;
}
return true;
}
};
} // namespace frontend
} // namespace js
#endif // frontend_BinASTTokenReaderMultipart_h

Просмотреть файл

@ -12,9 +12,6 @@
#include "mozilla/Utf8.h" // mozilla::Utf8Unit
#include "builtin/ModuleObject.h"
#if defined(JS_BUILD_BINAST)
# include "frontend/BinASTParser.h"
#endif // JS_BUILD_BINAST
#include "frontend/BytecodeCompilation.h"
#include "frontend/BytecodeEmitter.h"
#include "frontend/EitherParser.h"
@ -704,86 +701,6 @@ ScriptSourceObject* frontend::CreateScriptSourceObject(
return sso;
}
#if defined(JS_BUILD_BINAST)
template <class ParserT>
static JSScript* CompileGlobalBinASTScriptImpl(
JSContext* cx, const ReadOnlyCompileOptions& options, const uint8_t* src,
size_t len, JS::BinASTFormat format, ScriptSourceObject** sourceObjectOut) {
AutoAssertReportedException assertException(cx);
LifoAllocScope allocScope(&cx->tempLifoAlloc());
CompilationInfo compilationInfo(cx, allocScope, options);
if (!compilationInfo.init(cx)) {
return nullptr;
}
if (!compilationInfo.sourceObject->source()->setBinASTSourceCopy(cx, src,
len)) {
return nullptr;
}
SourceExtent extent = SourceExtent::makeGlobalExtent(len);
extent.lineno = 0;
GlobalSharedContext globalsc(cx, ScopeKind::Global, compilationInfo,
compilationInfo.directives, extent);
frontend::BinASTParser<ParserT> parser(cx, compilationInfo, options);
// Metadata stores internal pointers, so we must use the same buffer every
// time, including for lazy parses
ScriptSource* ss = compilationInfo.sourceObject->source();
BinASTSourceMetadata* metadata = nullptr;
auto parsed =
parser.parse(&globalsc, ss->binASTSource(), ss->length(), &metadata);
if (parsed.isErr()) {
return nullptr;
}
compilationInfo.sourceObject->source()->setBinASTSourceMetadata(metadata);
BytecodeEmitter bce(nullptr, &parser, &globalsc, compilationInfo);
if (!bce.init()) {
return nullptr;
}
ParseNode* pn = parsed.unwrap();
if (!bce.emitScript(pn)) {
return nullptr;
}
if (!compilationInfo.instantiateStencils()) {
return nullptr;
}
if (sourceObjectOut) {
*sourceObjectOut = compilationInfo.sourceObject;
}
tellDebuggerAboutCompiledScript(cx, options.hideScriptFromDebugger,
compilationInfo.script);
assertException.reset();
return compilationInfo.script;
}
JSScript* frontend::CompileGlobalBinASTScript(
JSContext* cx, const ReadOnlyCompileOptions& options, const uint8_t* src,
size_t len, JS::BinASTFormat format, ScriptSourceObject** sourceObjectOut) {
if (format == JS::BinASTFormat::Multipart) {
return CompileGlobalBinASTScriptImpl<BinASTTokenReaderMultipart>(
cx, options, src, len, format, sourceObjectOut);
}
MOZ_ASSERT(format == JS::BinASTFormat::Context);
return CompileGlobalBinASTScriptImpl<BinASTTokenReaderContext>(
cx, options, src, len, format, sourceObjectOut);
}
#endif // JS_BUILD_BINAST
template <typename Unit>
static ModuleObject* InternalParseModule(
JSContext* cx, const ReadOnlyCompileOptions& optionsInput,
@ -973,87 +890,6 @@ bool frontend::CompileLazyFunction(JSContext* cx, Handle<BaseScript*> lazy,
return CompileLazyFunctionImpl(cx, lazy, units, length);
}
#ifdef JS_BUILD_BINAST
template <class ParserT>
static bool CompileLazyBinASTFunctionImpl(JSContext* cx,
Handle<BaseScript*> lazy,
const uint8_t* buf, size_t length) {
MOZ_ASSERT(cx->compartment() == lazy->compartment());
// We can only compile functions whose parents have previously been
// compiled, because compilation requires full information about the
// function's immediately enclosing scope.
MOZ_ASSERT(lazy->isReadyForDelazification());
MOZ_ASSERT(lazy->isBinAST());
AutoAssertReportedException assertException(cx);
Rooted<JSFunction*> fun(cx, lazy->function());
mozilla::DebugOnly<bool> lazyIsLikelyConstructorWrapper =
lazy->isLikelyConstructorWrapper();
CompileOptions options(cx);
options.setMutedErrors(lazy->mutedErrors())
.setFileAndLine(lazy->filename(), lazy->lineno())
.setColumn(lazy->column())
.setScriptSourceOffset(lazy->sourceStart())
.setNoScriptRval(false)
.setSelfHostingMode(false);
LifoAllocScope allocScope(&cx->tempLifoAlloc());
CompilationInfo compilationInfo(cx, allocScope, options);
compilationInfo.initFromLazy(lazy);
frontend::BinASTParser<ParserT> parser(cx, compilationInfo, options, lazy);
auto parsed =
parser.parseLazyFunction(lazy->scriptSource(), lazy->sourceStart());
if (parsed.isErr()) {
return false;
}
FunctionNode* pn = parsed.unwrap();
BytecodeEmitter bce(nullptr, &parser, pn->funbox(), compilationInfo,
BytecodeEmitter::LazyFunction);
if (!bce.init(pn->pn_pos)) {
return false;
}
if (!bce.emitFunctionScript(pn, TopLevelFunction::Yes)) {
return false;
}
if (!compilationInfo.instantiateStencils()) {
return false;
}
// This value *must* not change after the lazy function is first created.
MOZ_ASSERT(lazyIsLikelyConstructorWrapper ==
compilationInfo.script->isLikelyConstructorWrapper());
assertException.reset();
return true;
}
bool frontend::CompileLazyBinASTFunction(JSContext* cx,
Handle<BaseScript*> lazy,
const uint8_t* buf, size_t length) {
if (lazy->scriptSource()->binASTSourceMetadata()->isMultipart()) {
return CompileLazyBinASTFunctionImpl<BinASTTokenReaderMultipart>(
cx, lazy, buf, length);
}
MOZ_ASSERT(lazy->scriptSource()->binASTSourceMetadata()->isContext());
return CompileLazyBinASTFunctionImpl<BinASTTokenReaderContext>(cx, lazy, buf,
length);
}
#endif // JS_BUILD_BINAST
static JSFunction* CompileStandaloneFunction(
JSContext* cx, const JS::ReadOnlyCompileOptions& options,
JS::SourceText<char16_t>& srcBuf, const Maybe<uint32_t>& parameterListEnd,

Просмотреть файл

@ -13,7 +13,6 @@
#include "NamespaceImports.h"
#include "frontend/FunctionSyntaxKind.h"
#include "js/BinASTFormat.h" // JS::BinASTFormat
#include "js/CompileOptions.h"
#include "js/SourceText.h"
#include "vm/Scope.h"
@ -109,19 +108,6 @@ class ErrorReporter;
class FunctionBox;
class ParseNode;
#if defined(JS_BUILD_BINAST)
JSScript* CompileGlobalBinASTScript(
JSContext* cx, const JS::ReadOnlyCompileOptions& options,
const uint8_t* src, size_t len, JS::BinASTFormat format,
ScriptSourceObject** sourceObjectOut = nullptr);
MOZ_MUST_USE bool CompileLazyBinASTFunction(JSContext* cx,
Handle<BaseScript*> lazy,
const uint8_t* buf, size_t length);
#endif // JS_BUILD_BINAST
// Compile a module of the given source using the given options.
ModuleObject* CompileModule(JSContext* cx,
const JS::ReadOnlyCompileOptions& options,

Просмотреть файл

@ -80,35 +80,3 @@ if CONFIG['JS_ENABLE_SMOOSH']:
SOURCES += [
'Parser.cpp',
]
if CONFIG['JS_BUILD_BINAST']:
# Using SOURCES, as UNIFIED_SOURCES causes mysterious bugs on 32-bit
# platforms.
# These parts of BinAST should eventually move to release.
SOURCES += [
'BinASTParser.cpp',
'BinASTParserBase.cpp',
'BinASTParserPerTokenizer.cpp',
'BinASTRuntimeSupport.cpp',
'BinASTToken.cpp',
'BinASTTokenReaderBase.cpp',
'BinASTTokenReaderContext.cpp',
'BinASTTokenReaderMultipart.cpp',
]
if CONFIG['JS_STANDALONE']:
DIRS += [
'binast'
]
# Instrument BinAST files for fuzzing as we have a fuzzing target for BinAST.
if CONFIG['FUZZING_INTERFACES'] and CONFIG['LIBFUZZER']:
include('/tools/fuzzing/libfuzzer-flags.mozbuild')
SOURCES['BinASTParser.cpp'].flags += libfuzzer_flags
SOURCES['BinASTParserBase.cpp'].flags += libfuzzer_flags
SOURCES['BinASTParserPerTokenizer.cpp'].flags += libfuzzer_flags
SOURCES['BinASTToken.cpp'].flags += libfuzzer_flags
SOURCES['BinASTTokenReaderBase.cpp'].flags += libfuzzer_flags
SOURCES['BinASTTokenReaderMultipart.cpp'].flags += libfuzzer_flags

Просмотреть файл

@ -17,11 +17,6 @@ UNIFIED_SOURCES += [
'testWasm.cpp',
]
if CONFIG['JS_BUILD_BINAST']:
UNIFIED_SOURCES += [
'testBinASTReader.cpp',
]
DEFINES['EXPORT_JS_API'] = True
LOCAL_INCLUDES += [

Просмотреть файл

@ -1,86 +0,0 @@
/* -*- 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 "mozilla/ScopeExit.h"
#include "jsapi.h"
#include "frontend/BinASTParser.h"
#include "frontend/FullParseHandler.h"
#include "frontend/ParseContext.h"
#include "frontend/Parser.h"
#include "fuzz-tests/tests.h"
#include "js/CompileOptions.h"
#include "vm/Interpreter.h"
#include "vm/JSContext-inl.h"
using UsedNameTracker = js::frontend::UsedNameTracker;
using namespace js;
using JS::CompileOptions;
// These are defined and pre-initialized by the harness (in tests.cpp).
extern JS::PersistentRootedObject gGlobal;
extern JSContext* gCx;
static int testBinASTReaderInit(int* argc, char*** argv) { return 0; }
static int testBinASTReaderFuzz(const uint8_t* buf, size_t size) {
using namespace js::frontend;
auto gcGuard = mozilla::MakeScopeExit([&] {
JS::PrepareForFullGC(gCx);
JS::NonIncrementalGC(gCx, GC_NORMAL, JS::GCReason::API);
});
if (!size) return 0;
CompileOptions options(gCx);
options.setIntroductionType("fuzzing parse").setFileAndLine("<string>", 1);
js::Vector<uint8_t> binSource(gCx);
if (!binSource.append(buf, size)) {
ReportOutOfMemory(gCx);
return 0;
}
LifoAllocScope allocScope(&gCx->tempLifoAlloc());
CompilationInfo binCompilationInfo(gCx, allocScope, options);
if (!binCompilationInfo.init(gCx)) {
return 0;
}
SourceExtent extent;
Directives directives(false);
GlobalSharedContext globalsc(gCx, ScopeKind::Global, binCompilationInfo,
directives, extent);
BinASTParser<js::frontend::BinASTTokenReaderMultipart> reader(
gCx, binCompilationInfo, options);
// Will be deallocated once `reader` goes out of scope.
auto binParsed = reader.parse(&globalsc, binSource);
RootedValue binExn(gCx);
if (binParsed.isErr()) {
js::GetAndClearException(gCx, &binExn);
return 0;
}
#if defined(DEBUG) // Dumping an AST is only defined in DEBUG builds
Sprinter binPrinter(gCx);
if (!binPrinter.init()) {
ReportOutOfMemory(gCx);
return 0;
}
DumpParseTree(binParsed.unwrap(), binPrinter);
#endif // defined(DEBUG)
return 0;
}
MOZ_FUZZING_INTERFACE_RAW(testBinASTReaderInit, testBinASTReaderFuzz, BinAST);

Просмотреть файл

@ -12,7 +12,6 @@
#include "builtin/MapObject.h"
#include "debugger/DebugAPI.h"
#include "frontend/BinASTParserBase.h"
#include "frontend/BytecodeCompiler.h"
#include "frontend/Parser.h"
#include "gc/ClearEdgesTracer.h"

Просмотреть файл

@ -738,9 +738,6 @@ MSG_DEF(JSMSG_BIGINT_INVALID_SYNTAX, 0, JSEXN_SYNTAXERR, "invalid BigInt syntax"
MSG_DEF(JSMSG_BIGINT_NOT_SERIALIZABLE, 0, JSEXN_TYPEERR, "BigInt value can't be serialized in JSON")
MSG_DEF(JSMSG_SC_BIGINT_DISABLED, 0, JSEXN_ERR, "BigInt not cloned - feature disabled in receiver")
// BinAST
MSG_DEF(JSMSG_BINAST, 1, JSEXN_SYNTAXERR, "BinAST Parsing Error: {0}")
// FinalizationRegistry
MSG_DEF(JSMSG_NOT_A_FINALIZATION_REGISTRY, 1, JSEXN_TYPEERR, "{0} is not a FinalizationRegistry")
MSG_DEF(JSMSG_NOT_A_FINALIZATION_ITERATOR, 1, JSEXN_TYPEERR, "{0} is not a FinalizationRegistryCleanupIterator")

Просмотреть файл

@ -152,17 +152,6 @@ if CONFIG['NIGHTLY_BUILD']:
'testErrorInterceptorGC.cpp',
]
if CONFIG['JS_BUILD_BINAST'] and CONFIG['JS_STANDALONE'] and not CONFIG['FUZZING']:
# Standalone builds leave the source directory untouched,
# which lets us run tests with the data files intact.
# Otherwise, in the current state of the build system,
# we can't have data files in js/src tests.
# Also, fuzzing builds modify the const matching in the
# token reader and hence affect the correctness of the tests.
UNIFIED_SOURCES += [
'testBinASTReader.cpp',
]
if CONFIG['OS_ARCH'] not in ('WINNT', 'Darwin') and CONFIG['OS_TARGET'] != 'Android':
# open_memstream() not available on Windows, macOS, or Android
UNIFIED_SOURCES += [

Просмотреть файл

@ -1,550 +0,0 @@
/* -*- 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/. */
#if defined(XP_UNIX)
# include <dirent.h>
# include <fcntl.h>
# include <sys/stat.h>
# include <unistd.h>
#elif defined(XP_WIN)
# include <windows.h>
#endif
#include "mozilla/Maybe.h"
#include "jsapi.h"
#include "frontend/BinASTParser.h"
#include "frontend/FullParseHandler.h"
#include "frontend/ParseContext.h"
#include "frontend/Parser.h"
#include "js/Exception.h"
#include "js/Vector.h"
#include "jsapi-tests/tests.h"
#include "vm/Interpreter.h"
using UsedNameTracker = js::frontend::UsedNameTracker;
using namespace JS;
using namespace js;
// Hack: These tests need access to resources, which are present in the source
// dir but not copied by our build system. To simplify things, we chdir to the
// source dir at the start of each test and return to the previous directory
// afterwards.
#if defined(XP_UNIX)
# include <sys/param.h>
static int gJsDirectory(0);
void enterJsDirectory() {
// Save current directory.
MOZ_ASSERT(gJsDirectory == 0);
gJsDirectory = open(".", O_RDONLY);
MOZ_ASSERT(gJsDirectory != 0, "Could not open directory '.'");
// Go to the directory provided by the test harness, if any.
const char* destination = getenv("CPP_UNIT_TESTS_DIR_JS_SRC");
if (destination) {
if (chdir(destination) == -1) {
MOZ_CRASH_UNSAFE_PRINTF("Could not chdir to %s", destination);
}
}
}
void exitJsDirectory() {
MOZ_ASSERT(gJsDirectory);
if (fchdir(gJsDirectory) == -1) {
MOZ_CRASH("Could not return to original directory");
}
if (close(gJsDirectory) != 0) {
MOZ_CRASH("Could not close js directory");
}
gJsDirectory = 0;
}
#else
char gJsDirectory[MAX_PATH] = {0};
void enterJsDirectory() {
// Save current directory.
MOZ_ASSERT(strlen(gJsDirectory) == 0);
auto result = GetCurrentDirectory(MAX_PATH, gJsDirectory);
if (result <= 0) {
MOZ_CRASH("Could not get current directory");
}
if (result > MAX_PATH) {
MOZ_CRASH_UNSAFE_PRINTF(
"Could not get current directory: needed %ld bytes, got %ld\n", result,
MAX_PATH);
}
// Find destination directory, if any.
char destination[MAX_PATH];
result = GetEnvironmentVariable("CPP_UNIT_TESTS_DIR_JS_SRC", destination,
MAX_PATH);
if (result == 0) {
if (GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
return; // No need to chdir
} else {
MOZ_CRASH("Could not get CPP_UNIT_TESTS_DIR_JS_SRC");
}
}
if (result > MAX_PATH) {
MOZ_CRASH_UNSAFE_PRINTF(
"Could not get CPP_UNIT_TESTS_DIR_JS_SRC: needed %ld bytes, got %ld\n",
result, MAX_PATH);
}
// Go to the directory.
if (SetCurrentDirectory(destination) == 0) {
MOZ_CRASH_UNSAFE_PRINTF("Could not chdir to %s", destination);
}
}
void exitJsDirectory() {
MOZ_ASSERT(strlen(gJsDirectory) > 0);
if (SetCurrentDirectory(gJsDirectory) == 0) {
MOZ_CRASH("Could not return to original directory");
}
gJsDirectory[0] = 0;
}
#endif // defined(XP_UNIX) || defined(XP_WIN)
// Read a full file (binary).
void readFull(const char* path, js::Vector<uint8_t>& buf) {
enterJsDirectory();
buf.shrinkTo(0);
FILE* in = fopen(path, "rb");
if (!in) {
MOZ_CRASH_UNSAFE_PRINTF("Could not open %s: %s", path, strerror(errno));
}
struct stat info;
if (stat(path, &info) < 0) {
MOZ_CRASH_UNSAFE_PRINTF("Could not get stat on %s", path);
}
if (!buf.growBy(info.st_size)) {
MOZ_CRASH("OOM");
}
int result = fread(buf.begin(), 1, info.st_size, in);
if (fclose(in) != 0) {
MOZ_CRASH("Could not close input file");
}
if (result != info.st_size) {
MOZ_CRASH_UNSAFE_PRINTF(
"Read error while reading %s: expected %llu bytes, got %llu", path,
(unsigned long long)info.st_size, (unsigned long long)result);
}
exitJsDirectory();
}
// Read a full file (text)
void readFull(JSContext* cx, const char* path,
js::Vector<mozilla::Utf8Unit>& buf) {
buf.shrinkTo(0);
js::Vector<uint8_t> intermediate(cx);
readFull(path, intermediate);
if (!buf.appendAll(intermediate)) {
MOZ_CRASH("Couldn't read data");
}
}
// Represents absolute/relative path.
class Path {
Vector<char> path_;
char lastChar() const {
size_t pathlen = path_.length();
return path_[pathlen - 1];
}
bool empty() const { return path_.empty(); }
bool isTerminated() const { return !empty() && lastChar() == '\0'; }
void terminate() {
if (!path_.append('\0')) {
MOZ_CRASH();
}
}
public:
explicit Path(JSContext* cx) : path_(cx) {}
void init(const char* path) {
path_.shrinkTo(0);
if (!path_.append(path, strlen(path))) {
MOZ_CRASH();
}
}
void join(const char* component) {
MOZ_ASSERT(!isTerminated());
#if defined(XP_WIN)
const char separator = '\\';
#else
const char separator = '/';
#endif // defined(XP_XIN)
if (!empty() && lastChar() != separator) {
if (!path_.append(separator)) {
MOZ_CRASH();
}
}
if (!path_.append(component, strlen(component))) {
MOZ_CRASH();
}
}
Vector<char>& getBuffer() {
MOZ_ASSERT(!isTerminated());
return path_;
}
bool exists() {
if (!isTerminated()) {
terminate();
}
struct stat info;
if (stat(get(), &info) < 0) {
return false;
}
return true;
}
const char* get() {
if (!isTerminated()) {
terminate();
}
return path_.begin();
}
};
template <typename Tok>
void runTestFromPath(JSContext* cx, const char* path) {
const char BIN_SUFFIX[] = ".binjs";
const char TXT_SUFFIX[] = ".js";
fprintf(stderr, "runTestFromPath: entering directory '%s'\n", path);
#if defined(XP_UNIX)
// Read the list of files in the directory.
enterJsDirectory();
DIR* dir = opendir(path);
exitJsDirectory();
if (!dir) {
MOZ_CRASH("Couldn't open directory");
}
while (auto entry = readdir(dir)) {
const char* d_name = entry->d_name;
const bool isDirectory = entry->d_type == DT_DIR;
#elif defined(XP_WIN)
Path pattern(cx);
pattern.init(path);
pattern.join("*");
WIN32_FIND_DATA FindFileData;
enterJsDirectory();
HANDLE hFind = FindFirstFile(pattern.get(), &FindFileData);
exitJsDirectory();
for (bool found = (hFind != INVALID_HANDLE_VALUE); found;
found = FindNextFile(hFind, &FindFileData)) {
const char* d_name = FindFileData.cFileName;
const bool isDirectory =
FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
#endif // defined(XP_UNIX) || defined(XP_WIN)
const size_t namlen = strlen(d_name);
// Recurse through subdirectories.
if (isDirectory) {
if (strcmp(d_name, ".") == 0) {
continue;
}
if (strcmp(d_name, "..") == 0) {
continue;
}
Path subPath(cx);
subPath.init(path);
subPath.join(d_name);
runTestFromPath<Tok>(cx, subPath.get());
continue;
}
// Make sure that we run GC between two tests. Otherwise, since we're
// running everything from the same cx and without returning to JS, there is
// nothing to deallocate the ASTs.
JS_GC(cx);
LifoAllocScope allocScope(&cx->tempLifoAlloc());
// Find files whose name ends with ".binjs".
fprintf(stderr, "Considering %s\n", d_name);
if (namlen < sizeof(BIN_SUFFIX)) {
continue;
}
if (strncmp(d_name + namlen - (sizeof(BIN_SUFFIX) - 1), BIN_SUFFIX,
sizeof(BIN_SUFFIX)) != 0)
continue;
// Find text file.
Path txtPath(cx);
txtPath.init(path);
txtPath.join(d_name);
txtPath.getBuffer().shrinkBy(sizeof(BIN_SUFFIX) - 1);
if (!txtPath.getBuffer().append(TXT_SUFFIX, sizeof(TXT_SUFFIX))) {
MOZ_CRASH();
}
fprintf(stderr, "Testing %s\n", txtPath.get());
// Read text file.
js::Vector<mozilla::Utf8Unit> txtSource(cx);
readFull(cx, txtPath.get(), txtSource);
// Parse text file.
CompileOptions txtOptions(cx);
txtOptions.setFileAndLine(txtPath.get(), 0);
frontend::CompilationInfo compilationInfo(cx, allocScope, txtOptions);
if (!compilationInfo.init(cx)) {
MOZ_CRASH("Couldn't initialize CompilationInfo");
}
js::frontend::Parser<js::frontend::FullParseHandler, mozilla::Utf8Unit>
txtParser(cx, txtOptions, txtSource.begin(), txtSource.length(),
/* foldConstants = */ false, compilationInfo, nullptr,
nullptr);
if (!txtParser.checkOptions()) {
MOZ_CRASH("Bad options");
}
auto txtParsed =
txtParser
.parse(); // Will be deallocated once `parser` goes out of scope.
ExceptionStack txtExn(cx);
if (!txtParsed) {
// Save exception for more detailed error message, if necessary.
if (!StealPendingExceptionStack(cx, &txtExn)) {
MOZ_CRASH("Couldn't clear exception");
}
}
// Read binary file.
Path binPath(cx);
binPath.init(path);
binPath.join(d_name);
js::Vector<uint8_t> binSource(cx);
readFull(binPath.get(), binSource);
// Parse binary file.
CompileOptions binOptions(cx);
binOptions.setFileAndLine(binPath.get(), 0);
frontend::CompilationInfo binCompilationInfo(cx, allocScope, binOptions);
if (!binCompilationInfo.init(cx)) {
MOZ_CRASH();
}
SourceExtent extent;
frontend::Directives directives(false);
frontend::GlobalSharedContext globalsc(
cx, ScopeKind::Global, binCompilationInfo, directives, extent);
frontend::BinASTParser<Tok> binParser(cx, binCompilationInfo, binOptions);
auto binParsed = binParser.parse(
&globalsc,
binSource); // Will be deallocated once `reader` goes out of scope.
ExceptionStack binExn(cx);
if (binParsed.isErr()) {
// Save exception for more detailed error message, if necessary.
if (!StealPendingExceptionStack(cx, &binExn)) {
MOZ_CRASH("Couldn't clear binExn");
}
}
// The binary parser should accept the file iff the text parser has.
if (binParsed.isOk() && !txtParsed) {
fprintf(stderr, "Text file parsing failed: ");
JS::ErrorReportBuilder report(cx);
if (!report.init(cx, txtExn, JS::ErrorReportBuilder::WithSideEffects)) {
MOZ_CRASH("Couldn't report txtExn");
}
PrintError(cx, stderr, report, /* reportWarnings */ true);
MOZ_CRASH("Binary parser accepted a file that text parser rejected");
}
if (binParsed.isErr() && txtParsed) {
fprintf(stderr, "Binary file parsing failed: ");
JS::ErrorReportBuilder report(cx);
if (!report.init(cx, binExn, JS::ErrorReportBuilder::WithSideEffects)) {
MOZ_CRASH("Couldn't report binExn");
}
PrintError(cx, stderr, report, /* reportWarnings */ true);
MOZ_CRASH("Binary parser rejected a file that text parser accepted");
}
if (binParsed.isErr()) {
fprintf(stderr,
"Binary parser and text parser agree that %s is invalid\n",
txtPath.get());
continue;
}
#if defined(DEBUG) // Dumping an AST is only defined in DEBUG builds
// Compare ASTs.
Sprinter binPrinter(cx);
if (!binPrinter.init()) {
MOZ_CRASH("Couldn't display binParsed");
}
DumpParseTree(binParsed.unwrap(), binPrinter);
Sprinter txtPrinter(cx);
if (!txtPrinter.init()) {
MOZ_CRASH("Couldn't display txtParsed");
}
DumpParseTree(txtParsed, txtPrinter);
if (strcmp(binPrinter.string(), txtPrinter.string()) != 0) {
fprintf(stderr,
"Got distinct ASTs when parsing %s "
"(%p/%p):\n\tBINARY\n%s\n\n\tTEXT\n%s\n",
txtPath.get(), (void*)binPrinter.getOffset(),
(void*)txtPrinter.getOffset(), binPrinter.string(),
txtPrinter.string());
# if 0 // Not for release, but useful for debugging.
// In case of error, this dumps files to /tmp, so they may
// easily be diffed.
auto fd = open("/tmp/bin.ast", O_CREAT | O_TRUNC | O_WRONLY, 0666);
if (!fd) {
MOZ_CRASH("Could not open bin.ast");
}
auto result = write(fd, binPrinter.string(), binPrinter.stringEnd() - binPrinter.string());
if (result <= 0) {
MOZ_CRASH("Could not write to bin.ast");
}
result = close(fd);
if (result != 0) {
MOZ_CRASH("Could not close bin.ast");
}
fd = open("/tmp/txt.ast", O_CREAT | O_TRUNC | O_WRONLY, 0666);
if (!fd) {
MOZ_CRASH("Could not open txt.ast");
}
result = write(fd, txtPrinter.string(), txtPrinter.stringEnd() - txtPrinter.string());
if (result <= 0) {
MOZ_CRASH("Could not write to txt.ast");
}
result = close(fd);
if (result != 0) {
MOZ_CRASH("Could not close txt.ast");
}
# endif // 0
MOZ_CRASH("Got distinct ASTs");
}
fprintf(stderr, "Got the same AST when parsing %s\n", txtPath.get());
#endif // defined(DEBUG)
}
#if defined(XP_WIN)
if (!FindClose(hFind)) {
MOZ_CRASH("Could not close Find");
}
#elif defined(XP_UNIX)
if (closedir(dir) != 0) {
MOZ_CRASH("Could not close dir");
}
#endif // defined(XP_WIN)
}
// Construct either absolute or relative path to test directory into `path`.
//
// If TOPSRCDIR environment variable is given, it should point
// mozilla-central.
//
// Otherwise, searches for test path, assuming cwd is somewhere between
// the mozilla-central top and the test directory.
//
// NOTE: At least this should work without TOPSRCDIR environment variable,
// as long as cwd is either mozilla-central or mozilla-central/js/src.
template <size_t N>
void getTestPath(JSContext* cx, const char* (&pathComponents)[N], Path& path) {
char* topSrcDir = getenv("TOPSRCDIR");
if (!topSrcDir) {
for (size_t i = 0; i < N; i++) {
Path tmp(cx);
tmp.init(pathComponents[i]);
if (tmp.exists()) {
for (; i < N; i++) {
path.join(pathComponents[i]);
}
MOZ_ASSERT(path.exists());
return;
}
}
MOZ_CRASH(
"Could not find test path. Please specify TOPSRCDIR or run from "
"mozilla-central top.");
}
path.init(topSrcDir);
for (size_t i = 0; i < N; i++) {
path.join(pathComponents[i]);
}
}
BEGIN_TEST(testBinASTReaderMultipartECMAScript2) {
const char* pathComponents[6] = {
"js", "src", "jsapi-tests", "binast", "parser", "multipart",
};
Path path(cx);
getTestPath(cx, pathComponents, path);
runTestFromPath<js::frontend::BinASTTokenReaderMultipart>(cx, path.get());
return true;
}
END_TEST(testBinASTReaderMultipartECMAScript2)
BEGIN_TEST(testBinASTReaderContextECMAScript2) {
const char* pathComponents[] = {
"js", "src", "jsapi-tests", "binast", "parser", "context",
};
Path path(cx);
getTestPath(cx, pathComponents, path);
runTestFromPath<js::frontend::BinASTTokenReaderContext>(cx, path.get());
return true;
}
END_TEST(testBinASTReaderContextECMAScript2)

Просмотреть файл

@ -3602,39 +3602,6 @@ CompileOptions& CompileOptions::setIntroductionInfoToCaller(
}
}
JSScript* JS::DecodeBinAST(JSContext* cx, const ReadOnlyCompileOptions& options,
const uint8_t* buf, size_t length,
JS::BinASTFormat format) {
#if defined(JS_BUILD_BINAST)
MOZ_ASSERT(!cx->zone()->isAtomsZone());
AssertHeapIsIdle();
CHECK_THREAD(cx);
return frontend::CompileGlobalBinASTScript(cx, options, buf, length, format);
#else // !JS_BUILD_BINAST
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_SUPPORT_NOT_ENABLED, "BinAST");
return nullptr;
#endif // JS_BUILD_BINAST
}
JSScript* JS::DecodeBinAST(JSContext* cx, const ReadOnlyCompileOptions& options,
FILE* file, JS::BinASTFormat format) {
#if defined(JS_BUILD_BINAST)
FileContents fileContents(cx);
if (!ReadCompleteFile(cx, file, fileContents)) {
return nullptr;
}
return DecodeBinAST(cx, options, fileContents.begin(), fileContents.length(),
format);
#else // !JS_BUILD_BINAST
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_SUPPORT_NOT_ENABLED, "BinAST");
return nullptr;
#endif // JS_BUILD_BINAST
}
JS_PUBLIC_API JSObject* JS_GetGlobalFromScript(JSScript* script) {
return &script->global();
}

Просмотреть файл

@ -27,7 +27,6 @@
#include "jspubtd.h"
#include "js/AllocPolicy.h"
#include "js/BinASTFormat.h" // JS::BinASTFormat
#include "js/CallArgs.h"
#include "js/CharacterEncoding.h"
#include "js/Class.h"
@ -1800,20 +1799,6 @@ extern JS_PUBLIC_API void SetScriptPrivateReferenceHooks(
} /* namespace JS */
namespace JS {
// This throws an exception if built without JS_BUILD_BINAST.
extern JS_PUBLIC_API JSScript* DecodeBinAST(
JSContext* cx, const ReadOnlyCompileOptions& options, FILE* file,
JS::BinASTFormat format);
// This throws an exception if built without JS_BUILD_BINAST.
extern JS_PUBLIC_API JSScript* DecodeBinAST(
JSContext* cx, const ReadOnlyCompileOptions& options, const uint8_t* buf,
size_t length, JS::BinASTFormat format);
} /* namespace JS */
extern JS_PUBLIC_API bool JS_CheckForInterrupt(JSContext* cx);
/*

Просмотреть файл

@ -126,7 +126,6 @@ EXPORTS.js += [
'../public/Array.h',
'../public/ArrayBuffer.h',
'../public/ArrayBufferMaybeShared.h',
'../public/BinASTFormat.h',
'../public/BuildId.h',
'../public/CallArgs.h',
'../public/CallNonGenericMethod.h',

Просмотреть файл

@ -71,9 +71,6 @@
#include "builtin/RegExp.h"
#include "builtin/TestingFunctions.h"
#include "debugger/DebugAPI.h"
#if defined(JS_BUILD_BINAST)
# include "frontend/BinASTParser.h"
#endif // defined(JS_BUILD_BINAST)
#include "frontend/CompilationInfo.h"
#ifdef JS_ENABLE_SMOOSH
# include "frontend/Frontend2.h"
@ -965,38 +962,6 @@ static MOZ_MUST_USE bool RunFile(JSContext* cx, const char* filename,
return true;
}
#if defined(JS_BUILD_BINAST)
static MOZ_MUST_USE bool RunBinAST(JSContext* cx, const char* filename,
FILE* file, bool compileOnly,
JS::BinASTFormat format) {
RootedScript script(cx);
{
CompileOptions options(cx);
options.setFileAndLine(filename, 0)
.setIsRunOnce(true)
.setNoScriptRval(true);
script = JS::DecodeBinAST(cx, options, file, format);
if (!script) {
return false;
}
}
if (!RegisterScriptPathWithModuleLoader(cx, script, filename)) {
return false;
}
if (compileOnly) {
return true;
}
return JS_ExecuteScript(cx, script);
}
#endif // JS_BUILD_BINAST
static MOZ_MUST_USE bool RunModule(JSContext* cx, const char* filename,
bool compileOnly) {
ShellContext* sc = GetShellContext(cx);
@ -1529,20 +1494,6 @@ static MOZ_MUST_USE bool Process(JSContext* cx, const char* filename,
return false;
}
break;
#if defined(JS_BUILD_BINAST)
case FileBinASTMultipart:
if (!RunBinAST(cx, filename, file, compileOnly,
JS::BinASTFormat::Multipart)) {
return false;
}
break;
case FileBinASTContext:
if (!RunBinAST(cx, filename, file, compileOnly,
JS::BinASTFormat::Context)) {
return false;
}
break;
#endif // JS_BUILD_BINAST
default:
MOZ_CRASH("Impossible FileKind!");
}
@ -5171,146 +5122,6 @@ static bool SetModuleResolveHook(JSContext* cx, unsigned argc, Value* vp) {
return true;
}
#if defined(JS_BUILD_BINAST)
template <typename Tok>
static bool ParseBinASTData(JSContext* cx, uint8_t* buf_data,
uint32_t buf_length,
js::frontend::GlobalSharedContext* globalsc,
js::frontend::CompilationInfo& compilationInfo,
const JS::ReadOnlyCompileOptions& options) {
MOZ_ASSERT(globalsc);
// Note: We need to keep `reader` alive as long as we can use `parsed`.
js::frontend::BinASTParser<Tok> reader(cx, compilationInfo, options);
JS::Result<js::frontend::ParseNode*> parsed =
reader.parse(globalsc, buf_data, buf_length);
if (parsed.isErr()) {
return false;
}
# ifdef DEBUG
Fprinter out(stderr);
DumpParseTree(parsed.unwrap(), out);
# endif
return true;
}
static bool BinParse(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
if (!args.requireAtLeast(cx, "parseBin", 1)) {
return false;
}
// Extract argument 1: ArrayBuffer.
if (!args[0].isObject()) {
const char* typeName = InformalValueTypeName(args[0]);
JS_ReportErrorASCII(cx, "expected object (ArrayBuffer) to parse, got %s",
typeName);
return false;
}
RootedObject objBuf(cx, &args[0].toObject());
if (!JS::IsArrayBufferObject(objBuf)) {
const char* typeName = InformalValueTypeName(args[0]);
JS_ReportErrorASCII(cx, "expected ArrayBuffer to parse, got %s", typeName);
return false;
}
uint32_t buf_length = 0;
bool buf_isSharedMemory = false;
uint8_t* buf_data = nullptr;
JS::GetArrayBufferLengthAndData(objBuf, &buf_length, &buf_isSharedMemory,
&buf_data);
MOZ_ASSERT(buf_data);
// Extract argument 2: Options.
// BinAST currently supports 2 formats, multipart and context.
enum {
Multipart,
Context,
} mode = Multipart;
if (args.length() >= 2) {
if (!args[1].isObject()) {
const char* typeName = InformalValueTypeName(args[1]);
JS_ReportErrorASCII(cx, "expected object (options) to parse, got %s",
typeName);
return false;
}
RootedObject objOptions(cx, &args[1].toObject());
RootedValue optionFormat(cx);
if (!JS_GetProperty(cx, objOptions, "format", &optionFormat)) {
return false;
}
if (!optionFormat.isUndefined()) {
RootedLinearString linearFormat(
cx, optionFormat.toString()->ensureLinear(cx));
if (!linearFormat) {
return false;
}
// Currently not used, reserved for future.
if (StringEqualsLiteral(linearFormat, "multipart")) {
mode = Multipart;
} else if (StringEqualsLiteral(linearFormat, "context")) {
mode = Context;
} else {
UniqueChars printable = QuoteString(cx, linearFormat, '\'');
if (!printable) {
return false;
}
JS_ReportErrorASCII(
cx,
"Unknown value for option `format`, expected 'multipart', got %s",
printable.get());
return false;
}
} else {
const char* typeName = InformalValueTypeName(optionFormat);
JS_ReportErrorASCII(cx, "option `format` should be a string, got %s",
typeName);
return false;
}
}
CompileOptions options(cx);
options.setIntroductionType("js shell bin parse")
.setFileAndLine("<ArrayBuffer>", 1);
LifoAllocScope allocScope(&cx->tempLifoAlloc());
js::frontend::CompilationInfo compilationInfo(cx, allocScope, options);
if (!compilationInfo.init(cx)) {
return false;
}
js::SourceExtent extent = js::SourceExtent::makeGlobalExtent(0, options);
js::frontend::Directives directives(false);
js::frontend::GlobalSharedContext globalsc(
cx, ScopeKind::Global, compilationInfo, directives, extent);
auto parseFunc = mode == Multipart
? ParseBinASTData<frontend::BinASTTokenReaderMultipart>
: ParseBinASTData<frontend::BinASTTokenReaderContext>;
if (!parseFunc(cx, buf_data, buf_length, &globalsc, compilationInfo,
options)) {
return false;
}
args.rval().setUndefined();
return true;
}
#endif // defined(JS_BUILD_BINAST)
template <typename Unit>
static bool FullParseTest(JSContext* cx,
const JS::ReadOnlyCompileOptions& options,
@ -8743,17 +8554,6 @@ JS_FN_HELP("rateMyCacheIR", RateMyCacheIR, 0, 0,
" module loader for testing purposes. This hook is used to look up a\n"
" previously loaded module object."),
#if defined(JS_BUILD_BINAST)
JS_FN_HELP("parseBin", BinParse, 1, 0,
"parseBin(arraybuffer, [options])",
" Parses a Binary AST, potentially throwing. If present, |options| may\n"
" have properties saying how the passed |arraybuffer| should be handled:\n"
" format: the format of the BinAST file\n"
" (\"multipart\" or \"context\")"),
#endif // defined(JS_BUILD_BINAST)
JS_FN_HELP("parse", Parse, 1, 0,
"parse(code)",
" Parses a string, potentially throwing."),
@ -10117,18 +9917,6 @@ static MOZ_MUST_USE bool ProcessArgs(JSContext* cx, OptionParser* op) {
MultiStringRange modulePaths = op->getMultiStringOption('m');
MultiStringRange binASTPaths(nullptr, nullptr);
FileKind binASTFileKind = FileBinASTMultipart;
#if defined(JS_BUILD_BINAST)
binASTPaths = op->getMultiStringOption('B');
if (const char* str = op->getStringOption("binast-format")) {
if (strcmp(str, "multipart") == 0) {
binASTFileKind = FileBinASTMultipart;
} else if (strcmp(str, "context") == 0) {
binASTFileKind = FileBinASTContext;
} else {
return OptionFailure("binast-format", str);
}
}
#endif // JS_BUILD_BINAST
if (filePaths.empty() && utf16FilePaths.empty() && codeChunks.empty() &&
modulePaths.empty() && binASTPaths.empty() &&
@ -11064,14 +10852,6 @@ int main(int argc, char** argv, char** envp) {
"File path to run, inflating the file's UTF-8 contents to UTF-16 and "
"then parsing that") ||
!op.addMultiStringOption('m', "module", "PATH", "Module path to run") ||
#if defined(JS_BUILD_BINAST)
!op.addMultiStringOption('B', "binast", "PATH", "BinAST path to run") ||
!op.addStringOption('\0', "binast-format", "[format]",
"Format of BinAST file (multipart/context)") ||
#else
!op.addMultiStringOption('B', "binast", "", "No-op") ||
!op.addStringOption('\0', "binast-format", "[format]", "No-op") ||
#endif // JS_BUILD_BINAST
!op.addMultiStringOption('e', "execute", "CODE", "Inline code to run") ||
!op.addBoolOption('i', "shell", "Enter prompt after running code") ||
!op.addBoolOption('c', "compileonly",

Просмотреть файл

@ -670,34 +670,6 @@ void ScriptDecodeTask::parse(JSContext* cx) {
}
}
#if defined(JS_BUILD_BINAST)
BinASTDecodeTask::BinASTDecodeTask(JSContext* cx, const uint8_t* buf,
size_t length, JS::BinASTFormat format,
JS::OffThreadCompileCallback callback,
void* callbackData)
: ParseTask(ParseTaskKind::BinAST, cx, callback, callbackData),
data(buf, length),
format(format) {}
void BinASTDecodeTask::parse(JSContext* cx) {
MOZ_ASSERT(cx->isHelperThreadContext());
RootedScriptSourceObject sourceObject(cx);
JSScript* script = frontend::CompileGlobalBinASTScript(
cx, options, data.begin().get(), data.length(), format,
&sourceObject.get());
if (script) {
scripts.infallibleAppend(script);
if (sourceObject) {
sourceObjects.infallibleAppend(sourceObject);
}
}
}
#endif /* JS_BUILD_BINAST */
MultiScriptsDecodeTask::MultiScriptsDecodeTask(
JSContext* cx, JS::TranscodeSources& sources,
JS::OffThreadCompileCallback callback, void* callbackData)
@ -1056,29 +1028,6 @@ bool js::StartOffThreadDecodeMultiScripts(JSContext* cx,
return StartOffThreadParseTask(cx, std::move(task), options);
}
#if defined(JS_BUILD_BINAST)
bool js::StartOffThreadDecodeBinAST(JSContext* cx,
const ReadOnlyCompileOptions& options,
const uint8_t* buf, size_t length,
JS::BinASTFormat format,
JS::OffThreadCompileCallback callback,
void* callbackData) {
if (!cx->runtime()->binast().ensureBinTablesInitialized(cx)) {
return false;
}
auto task = cx->make_unique<BinASTDecodeTask>(cx, buf, length, format,
callback, callbackData);
if (!task) {
return false;
}
return StartOffThreadParseTask(cx, std::move(task), options);
}
#endif /* JS_BUILD_BINAST */
void js::EnqueuePendingParseTasksAfterGC(JSRuntime* rt) {
MOZ_ASSERT(!OffThreadParsingMustWaitForGC(rt));
@ -1945,17 +1894,6 @@ JSScript* GlobalHelperThreadState::finishScriptDecodeTask(
return script;
}
#if defined(JS_BUILD_BINAST)
JSScript* GlobalHelperThreadState::finishBinASTDecodeTask(
JSContext* cx, JS::OffThreadToken* token) {
JSScript* script = finishSingleParseTask(cx, ParseTaskKind::BinAST, token);
MOZ_ASSERT_IF(script, script->isGlobalCode());
return script;
}
#endif /* JS_BUILD_BINAST */
bool GlobalHelperThreadState::finishMultiScriptsDecodeTask(
JSContext* cx, JS::OffThreadToken* token,
MutableHandle<ScriptVector> scripts) {

Просмотреть файл

@ -24,7 +24,6 @@
#include "ds/Fifo.h"
#include "jit/JitContext.h"
#include "js/BinASTFormat.h" // JS::BinASTFormat
#include "js/CompileOptions.h"
#include "js/SourceText.h"
#include "js/TypeDecls.h"
@ -332,10 +331,6 @@ class GlobalHelperThreadState {
MutableHandle<ScriptVector> scripts);
JSObject* finishModuleParseTask(JSContext* cx, JS::OffThreadToken* token);
#if defined(JS_BUILD_BINAST)
JSScript* finishBinASTDecodeTask(JSContext* cx, JS::OffThreadToken* token);
#endif
bool hasActiveThreads(const AutoLockHelperThreadState&);
void waitForAllThreads();
void waitForAllThreadsLocked(AutoLockHelperThreadState&);
@ -636,17 +631,6 @@ bool StartOffThreadDecodeScript(JSContext* cx,
JS::OffThreadCompileCallback callback,
void* callbackData);
#if defined(JS_BUILD_BINAST)
bool StartOffThreadDecodeBinAST(JSContext* cx,
const JS::ReadOnlyCompileOptions& options,
const uint8_t* buf, size_t length,
JS::BinASTFormat format,
JS::OffThreadCompileCallback callback,
void* callbackData);
#endif /* JS_BUILD_BINAST */
bool StartOffThreadDecodeMultiScripts(JSContext* cx,
const JS::ReadOnlyCompileOptions& options,
JS::TranscodeSources& sources,
@ -787,20 +771,6 @@ struct ScriptDecodeTask : public ParseTask {
void parse(JSContext* cx) override;
};
#if defined(JS_BUILD_BINAST)
struct BinASTDecodeTask : public ParseTask {
mozilla::Range<const uint8_t> data;
JS::BinASTFormat format;
BinASTDecodeTask(JSContext* cx, const uint8_t* buf, size_t length,
JS::BinASTFormat format,
JS::OffThreadCompileCallback callback, void* callbackData);
void parse(JSContext* cx) override;
};
#endif /* JS_BUILD_BINAST */
struct MultiScriptsDecodeTask : public ParseTask {
JS::TranscodeSources* sources;

Просмотреть файл

@ -1571,16 +1571,7 @@ static bool DelazifyCanonicalScriptedFunction(JSContext* cx,
bool hadLazyScriptData = lazy->hasPrivateScriptData();
if (ss->hasBinASTSource()) {
#if defined(JS_BUILD_BINAST)
if (!frontend::CompileLazyBinASTFunction(
cx, lazy, ss->binASTSource() + sourceStart, sourceLength)) {
MOZ_ASSERT(fun->baseScript() == lazy);
MOZ_ASSERT(lazy->isReadyForDelazification());
return false;
}
#else
MOZ_CRASH("Trying to delazify BinAST function in non-BinAST build");
#endif /*JS_BUILD_BINAST */
} else {
MOZ_ASSERT(ss->hasSourceText());

Просмотреть файл

@ -31,8 +31,6 @@
#include "jsapi.h"
#include "jstypes.h"
#include "frontend/BinASTRuntimeSupport.h" // BinASTSourceMetadata{,Multipart,Context}
#include "frontend/BinASTTokenReaderContext.h" // HuffmanDictionaryForMetadata,AutoClearHuffmanDictionaryForMetadata
#include "frontend/BytecodeCompiler.h"
#include "frontend/BytecodeEmitter.h"
#include "frontend/SharedContext.h"
@ -2275,31 +2273,6 @@ MOZ_MUST_USE bool ScriptSource::setRetrievedSource(JSContext* cx,
SourceRetrievable::Yes);
}
#if defined(JS_BUILD_BINAST)
MOZ_MUST_USE bool ScriptSource::setBinASTSourceCopy(JSContext* cx,
const uint8_t* buf,
size_t len) {
MOZ_ASSERT(data.is<Missing>());
auto& cache = cx->zone()->runtimeFromAnyThread()->sharedImmutableStrings();
auto deduped = cache.getOrCreate(reinterpret_cast<const char*>(buf), len);
if (!deduped) {
ReportOutOfMemory(cx);
return false;
}
data = SourceType(BinAST(std::move(*deduped), nullptr));
return true;
}
const uint8_t* ScriptSource::binASTSource() {
MOZ_ASSERT(hasBinASTSource());
return reinterpret_cast<const uint8_t*>(data.as<BinAST>().string.chars());
}
#endif /* JS_BUILD_BINAST */
bool ScriptSource::tryCompressOffThread(JSContext* cx) {
// Beware: |js::SynchronouslyCompressSource| assumes that this function is
// only called once, just after a script has been compiled, and it's never
@ -2442,15 +2415,7 @@ template bool ScriptSource::assignSource(JSContext* cx,
void ScriptSource::trace(JSTracer* trc) {
// This should be kept in sync with ScriptSource::finalizeGCData below.
#ifdef JS_BUILD_BINAST
if (data.is<BinAST>()) {
if (auto& metadata = data.as<BinAST>().metadata) {
metadata->trace(trc);
}
}
#else
MOZ_ASSERT(!data.is<BinAST>());
#endif // JS_BUILD_BINAST
if (xdrEncoder_) {
xdrEncoder_->trace(trc);
}
@ -2467,13 +2432,6 @@ void ScriptSource::finalizeGCData() {
// offthread later will fail.
MOZ_ASSERT(TlsContext.get() && TlsContext.get()->isMainThreadContext());
#ifdef JS_BUILD_BINAST
if (hasBinASTSource()) {
if (auto& metadata = data.as<BinAST>().metadata) {
metadata.reset();
}
}
#endif // JS_BUILD_BINAST
if (xdrEncoder_) {
xdrEncoder_.reset();
}
@ -2954,520 +2912,7 @@ template <XDRMode mode>
/* static */
XDRResult ScriptSource::codeBinASTData(XDRState<mode>* const xdr,
ScriptSource* const ss) {
#if !defined(JS_BUILD_BINAST)
return xdr->fail(JS::TranscodeResult_Throw);
#else
if (mode == XDR_ENCODE) {
MOZ_ASSERT(ss->data.is<BinAST>());
} else {
MOZ_ASSERT(ss->data.is<Missing>());
}
// XDR the length of the BinAST data.
uint32_t binASTLength;
if (mode == XDR_ENCODE) {
binASTLength = ss->data.as<BinAST>().string.length();
}
MOZ_TRY(xdr->codeUint32(&binASTLength));
// XDR the BinAST data.
mozilla::Maybe<SharedImmutableString> binASTData;
if (mode == XDR_DECODE) {
auto bytes = xdr->cx()->template make_pod_array<char>(
std::max<size_t>(binASTLength, 1));
if (!bytes) {
return xdr->fail(JS::TranscodeResult_Throw);
}
MOZ_TRY(xdr->codeBytes(bytes.get(), binASTLength));
auto& cache =
xdr->cx()->zone()->runtimeFromAnyThread()->sharedImmutableStrings();
binASTData = cache.getOrCreate(std::move(bytes), binASTLength);
if (!binASTData) {
ReportOutOfMemory(xdr->cx());
return xdr->fail(JS::TranscodeResult_Throw);
}
} else {
void* bytes = ss->binASTData();
MOZ_TRY(xdr->codeBytes(bytes, binASTLength));
}
// XDR any BinAST metadata.
uint8_t hasMetadata;
if (mode == XDR_ENCODE) {
hasMetadata = ss->data.as<BinAST>().metadata != nullptr;
}
MOZ_TRY(xdr->codeUint8(&hasMetadata));
Rooted<UniquePtr<frontend::BinASTSourceMetadata>> freshMetadata(xdr->cx());
if (hasMetadata) {
// If we're decoding, this is a *mutable borrowed* reference to the
// |UniquePtr| stored in the |Rooted| above, and the |UniquePtr| will be
// filled with freshly allocated metadata.
//
// If we're encoding, this is an *immutable borrowed* reference to the
// |UniquePtr| stored in |ss|. (Immutable up to GCs transparently moving
// things around, that is.)
UniquePtr<frontend::BinASTSourceMetadata>& binASTMetadata =
mode == XDR_DECODE ? freshMetadata.get()
: ss->data.as<BinAST>().metadata;
frontend::BinASTSourceMetadata::Type metadataType;
uint8_t metadataTypeAsInt;
if (mode == XDR_ENCODE) {
metadataType = binASTMetadata->type();
metadataTypeAsInt = static_cast<uint8_t>(metadataType);
}
MOZ_TRY(xdr->codeUint8(&metadataTypeAsInt));
if (mode == XDR_DECODE) {
metadataType =
static_cast<frontend::BinASTSourceMetadata::Type>(metadataTypeAsInt);
}
if (metadataType == frontend::BinASTSourceMetadata::Type::Multipart) {
auto metadata = binASTMetadata->asMultipart();
uint32_t numBinASTKinds;
uint32_t numStrings;
if (mode == XDR_ENCODE) {
numBinASTKinds = metadata->numBinASTKinds();
numStrings = metadata->numStrings();
}
MOZ_TRY(xdr->codeUint32(&numBinASTKinds));
MOZ_TRY(xdr->codeUint32(&numStrings));
if (mode == XDR_DECODE) {
metadata = frontend::BinASTSourceMetadataMultipart::create(
numBinASTKinds, numStrings);
if (!metadata) {
return xdr->fail(JS::TranscodeResult_Throw);
}
binASTMetadata.reset(metadata);
}
MOZ_ASSERT(binASTMetadata != nullptr);
frontend::BinASTKind* binASTKindBase = metadata->binASTKindBase();
for (uint32_t i = 0; i < numBinASTKinds; i++) {
MOZ_TRY(xdr->codeEnum32(&binASTKindBase[i]));
}
Rooted<JSAtom*> atom(xdr->cx());
JSAtom** atomsBase = metadata->atomsBase();
auto slices = metadata->sliceBase();
const char* sourceBase =
(mode == XDR_ENCODE ? ss->data.as<BinAST>().string : *binASTData)
.chars();
for (uint32_t i = 0; i < numStrings; i++) {
uint8_t isNull;
if (mode == XDR_ENCODE) {
atom = metadata->getAtom(i);
isNull = !atom;
}
MOZ_TRY(xdr->codeUint8(&isNull));
if (isNull) {
atom = nullptr;
} else {
MOZ_TRY(XDRAtom(xdr, &atom));
}
if (mode == XDR_DECODE) {
atomsBase[i] = atom;
}
uint64_t sliceOffset;
uint32_t sliceLen;
if (mode == XDR_ENCODE) {
auto& slice = metadata->getSlice(i);
sliceOffset = slice.begin() - sourceBase;
sliceLen = slice.byteLen_;
}
MOZ_TRY(xdr->codeUint64(&sliceOffset));
MOZ_TRY(xdr->codeUint32(&sliceLen));
if (mode == XDR_DECODE) {
new (&slices[i]) frontend::BinASTSourceMetadataMultipart::CharSlice(
sourceBase + sliceOffset, sliceLen);
}
}
} else {
MOZ_ASSERT(metadataType == frontend::BinASTSourceMetadata::Type::Context);
auto metadata = binASTMetadata->asContext();
uint32_t numStrings;
if (mode == XDR_ENCODE) {
numStrings = metadata->numStrings();
}
MOZ_TRY(xdr->codeUint32(&numStrings));
if (mode == XDR_DECODE) {
metadata = frontend::BinASTSourceMetadataContext::create(numStrings);
if (!metadata) {
return xdr->fail(JS::TranscodeResult_Throw);
}
binASTMetadata.reset(metadata);
}
MOZ_ASSERT(binASTMetadata != nullptr);
Rooted<JSAtom*> atom(xdr->cx());
JSAtom** atomsBase = metadata->atomsBase();
for (uint32_t i = 0; i < numStrings; i++) {
uint8_t isNull;
if (mode == XDR_ENCODE) {
atom = metadata->getAtom(i);
isNull = !atom;
}
MOZ_TRY(xdr->codeUint8(&isNull));
if (isNull) {
atom = nullptr;
} else {
MOZ_TRY(XDRAtom(xdr, &atom));
}
if (mode == XDR_DECODE) {
atomsBase[i] = atom;
}
}
uint32_t numTables;
uint32_t numHuffmanEntries;
uint32_t numInternalIndices;
uint32_t numSingleTables;
uint32_t numTwoTables;
UniquePtr<frontend::HuffmanDictionaryForMetadata> newDictionary;
frontend::AutoClearHuffmanDictionaryForMetadata autoClear;
frontend::HuffmanDictionaryForMetadata* dictionary;
if (mode == XDR_ENCODE) {
dictionary = metadata->dictionary();
MOZ_ASSERT(metadata->dictionary());
numTables = dictionary->numTables();
numHuffmanEntries = dictionary->numHuffmanEntries();
numInternalIndices = dictionary->numInternalIndices();
numSingleTables = dictionary->numSingleTables();
numTwoTables = dictionary->numTwoTables();
}
MOZ_TRY(xdr->codeUint32(&numTables));
MOZ_TRY(xdr->codeUint32(&numHuffmanEntries));
MOZ_TRY(xdr->codeUint32(&numInternalIndices));
MOZ_TRY(xdr->codeUint32(&numSingleTables));
MOZ_TRY(xdr->codeUint32(&numTwoTables));
if (mode == XDR_DECODE) {
dictionary = frontend::HuffmanDictionaryForMetadata::create(
numTables, numHuffmanEntries, numInternalIndices, numSingleTables,
numTwoTables);
if (!dictionary) {
return xdr->fail(JS::TranscodeResult_Throw);
}
autoClear.set(dictionary);
newDictionary.reset(dictionary);
}
auto huffmanEntryPtr = dictionary->huffmanEntriesBase();
auto internalIndexPtr = dictionary->internalIndicesBase();
auto singleTablePtr = dictionary->singleTablesBase();
auto twoTablePtr = dictionary->twoTablesBase();
for (uint32_t i = 0; i < frontend::TableIdentity::Limit; i++) {
MOZ_TRY(xdr->codeUint16(&dictionary->tableIndices_[i]));
}
enum TableTag {
SingleEntryHuffmanTableTag = 0,
TwoEntriesHuffmanTableTag = 1,
SingleLookupHuffmanTableTag = 2,
TwoLookupsHuffmanTableTag = 3,
ThreeLookupsHuffmanTableTag = 4,
};
// Encode/decode tables and the content of those tables.
for (uint32_t i = 0; i < numTables; i++) {
auto& table = dictionary->tableAtIndex(i);
uint8_t tag;
if (mode == XDR_ENCODE) {
if (table.implementation_.is<frontend::SingleEntryHuffmanTable>()) {
tag = SingleEntryHuffmanTableTag;
} else if (table.implementation_
.is<frontend::TwoEntriesHuffmanTable>()) {
tag = TwoEntriesHuffmanTableTag;
} else if (table.implementation_
.is<frontend::SingleLookupHuffmanTable>()) {
tag = SingleLookupHuffmanTableTag;
} else if (table.implementation_
.is<frontend::TwoLookupsHuffmanTable>()) {
tag = TwoLookupsHuffmanTableTag;
} else {
MOZ_ASSERT(
table.implementation_.is<frontend::ThreeLookupsHuffmanTable>());
tag = ThreeLookupsHuffmanTableTag;
}
}
MOZ_TRY(xdr->codeUint8(&tag));
// Encode/decode HuffmanEntry, without moving `huffmanEntryPtr`.
// The caller is responsible to move `huffmanEntryPtr`.
auto codeHuffmanEntryWithoutMovePtr =
[xdr](frontend::HuffmanEntry* entry) -> XDRResult {
uint32_t keyBits;
uint8_t keyBitLength;
uint64_t bits;
if (mode == XDR_ENCODE) {
keyBits = entry->key().bits_;
keyBitLength = entry->key().bitLength_;
bits = entry->value().asBits_;
}
MOZ_TRY(xdr->codeUint32(&keyBits));
MOZ_TRY(xdr->codeUint8(&keyBitLength));
MOZ_TRY(xdr->codeUint64(&bits));
if (mode == XDR_DECODE) {
new (entry) frontend::HuffmanEntry(keyBits, keyBitLength,
frontend::BinASTSymbol(bits));
}
return Ok();
};
// Encode/decode SingleLookupHuffmanTable and its contents.
auto codeSingleTable =
[xdr, &huffmanEntryPtr, &internalIndexPtr,
codeHuffmanEntryWithoutMovePtr](
frontend::SingleLookupHuffmanTable* table) -> XDRResult {
uint32_t numValues;
if (mode == XDR_ENCODE) {
numValues = table->values_.size();
}
MOZ_TRY(xdr->codeUint32(&numValues));
if (mode == XDR_DECODE) {
table->values_ =
mozilla::MakeSpan(huffmanEntryPtr.get(), numValues);
huffmanEntryPtr += numValues;
}
for (size_t i = 0; i < numValues; i++) {
MOZ_TRY(codeHuffmanEntryWithoutMovePtr(&table->values_[i]));
}
uint32_t numSaturated;
if (mode == XDR_ENCODE) {
numSaturated = table->saturated_.size();
}
MOZ_TRY(xdr->codeUint32(&numSaturated));
if (mode == XDR_DECODE) {
table->saturated_ =
mozilla::MakeSpan(internalIndexPtr.get(), numSaturated);
internalIndexPtr += numSaturated;
}
for (size_t i = 0; i < numSaturated; i++) {
MOZ_TRY(xdr->codeUint8(&table->saturated_[i]));
}
MOZ_TRY(xdr->codeUint8(&table->largestBitLength_));
return Ok();
};
// Encode/decode TwoLookupsHuffmanTable and its contents,
// including suffixTables_ recursively.
auto codeTwoTable =
[xdr, &huffmanEntryPtr, &singleTablePtr,
codeHuffmanEntryWithoutMovePtr, codeSingleTable](
frontend::TwoLookupsHuffmanTable* table) -> XDRResult {
MOZ_TRY(codeSingleTable(&table->shortKeys_));
uint32_t numValues;
if (mode == XDR_ENCODE) {
numValues = table->values_.size();
}
MOZ_TRY(xdr->codeUint32(&numValues));
if (mode == XDR_DECODE) {
table->values_ =
mozilla::MakeSpan(huffmanEntryPtr.get(), numValues);
huffmanEntryPtr += numValues;
}
for (size_t i = 0; i < numValues; i++) {
MOZ_TRY(codeHuffmanEntryWithoutMovePtr(&table->values_[i]));
}
uint32_t numSuffixTables;
if (mode == XDR_ENCODE) {
numSuffixTables = table->suffixTables_.size();
}
MOZ_TRY(xdr->codeUint32(&numSuffixTables));
if (mode == XDR_DECODE) {
table->suffixTables_ =
mozilla::MakeSpan(singleTablePtr.get(), numSuffixTables);
singleTablePtr += numSuffixTables;
}
for (size_t i = 0; i < numSuffixTables; i++) {
auto suffixTable = &table->suffixTables_[i];
new (suffixTable) frontend::SingleLookupHuffmanTable();
MOZ_TRY(codeSingleTable(suffixTable));
}
MOZ_TRY(xdr->codeUint8(&table->largestBitLength_));
return Ok();
};
// Encode/decode ThreeLookupsHuffmanTable and its contents,
// including suffixTables_ recursively.
auto codeThreeTable =
[xdr, &huffmanEntryPtr, &twoTablePtr,
codeHuffmanEntryWithoutMovePtr, codeSingleTable, codeTwoTable](
frontend::ThreeLookupsHuffmanTable* table) -> XDRResult {
MOZ_TRY(codeSingleTable(&table->shortKeys_));
uint32_t numValues;
if (mode == XDR_ENCODE) {
numValues = table->values_.size();
}
MOZ_TRY(xdr->codeUint32(&numValues));
if (mode == XDR_DECODE) {
table->values_ =
mozilla::MakeSpan(huffmanEntryPtr.get(), numValues);
huffmanEntryPtr += numValues;
}
for (size_t i = 0; i < numValues; i++) {
MOZ_TRY(codeHuffmanEntryWithoutMovePtr(&table->values_[i]));
}
uint32_t numSuffixTables;
if (mode == XDR_ENCODE) {
numSuffixTables = table->suffixTables_.size();
}
MOZ_TRY(xdr->codeUint32(&numSuffixTables));
if (mode == XDR_DECODE) {
table->suffixTables_ =
mozilla::MakeSpan(twoTablePtr.get(), numSuffixTables);
twoTablePtr += numSuffixTables;
}
for (size_t i = 0; i < numSuffixTables; i++) {
auto suffixTable = &table->suffixTables_[i];
new (suffixTable) frontend::TwoLookupsHuffmanTable();
MOZ_TRY(codeTwoTable(suffixTable));
}
MOZ_TRY(xdr->codeUint8(&table->largestBitLength_));
return Ok();
};
if (tag == SingleEntryHuffmanTableTag) {
uint64_t bits;
if (mode == XDR_ENCODE) {
auto& specialized =
table.implementation_.as<frontend::SingleEntryHuffmanTable>();
bits = specialized.value_.asBits_;
}
MOZ_TRY(xdr->codeUint64(&bits));
if (mode == XDR_DECODE) {
new (&table) frontend::GenericHuffmanTable();
auto value = frontend::BinASTSymbol(bits);
table.implementation_ = {
mozilla::VariantType<frontend::SingleEntryHuffmanTable>{},
value};
}
} else if (tag == SingleEntryHuffmanTableTag) {
if (mode == XDR_DECODE) {
new (&table) frontend::GenericHuffmanTable();
table.implementation_ = {
mozilla::VariantType<frontend::TwoEntriesHuffmanTable>{}};
}
auto& specialized =
table.implementation_.as<frontend::TwoEntriesHuffmanTable>();
for (size_t i = 0; i < 2; i++) {
uint64_t bits;
if (mode == XDR_ENCODE) {
bits = specialized.values_[i].asBits_;
}
MOZ_TRY(xdr->codeUint64(&bits));
if (mode == XDR_DECODE) {
specialized.values_[i] = frontend::BinASTSymbol(bits);
}
}
} else if (tag == SingleLookupHuffmanTableTag) {
if (mode == XDR_DECODE) {
new (&table) frontend::GenericHuffmanTable();
table.implementation_ = {
mozilla::VariantType<frontend::SingleLookupHuffmanTable>{}};
}
auto& specialized =
table.implementation_.as<frontend::SingleLookupHuffmanTable>();
MOZ_TRY(codeSingleTable(&specialized));
} else if (tag == TwoLookupsHuffmanTableTag) {
if (mode == XDR_DECODE) {
new (&table) frontend::GenericHuffmanTable();
table.implementation_ = {
mozilla::VariantType<frontend::TwoLookupsHuffmanTable>{}};
}
auto& specialized =
table.implementation_.as<frontend::TwoLookupsHuffmanTable>();
MOZ_TRY(codeTwoTable(&specialized));
} else {
MOZ_ASSERT(tag == ThreeLookupsHuffmanTableTag);
if (mode == XDR_DECODE) {
new (&table) frontend::GenericHuffmanTable();
table.implementation_ = {
mozilla::VariantType<frontend::ThreeLookupsHuffmanTable>{}};
}
auto& specialized =
table.implementation_.as<frontend::ThreeLookupsHuffmanTable>();
MOZ_TRY(codeThreeTable(&specialized));
}
}
autoClear.reset();
metadata->setDictionary(newDictionary.release());
}
}
if (mode == XDR_DECODE) {
MOZ_ASSERT(binASTData.isSome());
MOZ_ASSERT(freshMetadata != nullptr);
MOZ_ASSERT(ss->data.is<Missing>(),
"should only be initializing a fresh ScriptSource");
ss->data = SourceType(
BinAST(std::move(*binASTData), std::move(freshMetadata.get())));
}
MOZ_ASSERT(binASTData.isNothing());
MOZ_ASSERT(freshMetadata == nullptr);
MOZ_ASSERT(ss->data.is<BinAST>());
return Ok();
#endif // !defined(JS_BUILD_BINAST)
}
template <typename Unit, XDRMode mode>

Просмотреть файл

@ -26,7 +26,6 @@
#include "jstypes.h"
#include "frontend/BinASTRuntimeSupport.h"
#include "frontend/NameAnalysisTypes.h"
#include "frontend/SourceNotes.h" // SrcNote
#include "gc/Barrier.h"
@ -85,6 +84,8 @@ class FunctionIndex;
class FunctionBox;
class ModuleSharedContext;
class ScriptStencil;
class BinASTSourceMetadata {};
} // namespace frontend
class ScriptCounts {
@ -961,20 +962,6 @@ class ScriptSource {
MOZ_MUST_USE bool initializeWithUnretrievableCompressedSource(
JSContext* cx, UniqueChars&& raw, size_t rawLength, size_t sourceLength);
#if defined(JS_BUILD_BINAST)
/*
* Do not take ownership of the given `buf`. Store the canonical, shared
* and de-duplicated version. If there is no extant shared version of
* `buf`, make a copy.
*/
MOZ_MUST_USE bool setBinASTSourceCopy(JSContext* cx, const uint8_t* buf,
size_t len);
const uint8_t* binASTSource();
#endif /* JS_BUILD_BINAST */
private:
void performTaskWork(SourceCompressionTask* task);

Просмотреть файл

@ -201,39 +201,3 @@ JS_PUBLIC_API void JS::CancelMultiOffThreadScriptsDecoder(
HelperThreadState().cancelParseTask(cx->runtime(),
ParseTaskKind::MultiScriptsDecode, token);
}
JS_PUBLIC_API bool JS::CanDecodeBinASTOffThread(
JSContext* cx, const ReadOnlyCompileOptions& options, size_t length) {
#ifdef JS_BUILD_BINAST
return CanDoOffThread(cx, options, length, OffThread::DecodeBinAST);
#else // !JS_BUILD_BINAST
return false;
#endif // JS_BUILD_BINAST
}
JS_PUBLIC_API bool JS::DecodeBinASTOffThread(
JSContext* cx, const ReadOnlyCompileOptions& options, const uint8_t* buf,
size_t length, JS::BinASTFormat format, OffThreadCompileCallback callback,
void* callbackData) {
#ifdef JS_BUILD_BINAST
return StartOffThreadDecodeBinAST(cx, options, buf, length, format, callback,
callbackData);
#else // !JS_BUILD_BINAST
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_SUPPORT_NOT_ENABLED, "BinAST");
return false;
#endif // JS_BUILD_BINAST
}
JS_PUBLIC_API JSScript* JS::FinishOffThreadBinASTDecode(
JSContext* cx, JS::OffThreadToken* token) {
#ifdef JS_BUILD_BINAST
MOZ_ASSERT(cx);
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
return HelperThreadState().finishBinASTDecodeTask(cx, token);
#else // !JS_BUILD_BINAST
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_SUPPORT_NOT_ENABLED, "BinAST");
return nullptr;
#endif // JS_BUILD_BINAST
}

Просмотреть файл

@ -27,7 +27,6 @@
#ifdef JS_HAS_INTL_API
# include "builtin/intl/SharedIntlData.h"
#endif
#include "frontend/BinASTRuntimeSupport.h"
#include "frontend/NameCollections.h"
#include "gc/GCRuntime.h"
#include "gc/Tracer.h"
@ -1025,14 +1024,6 @@ struct JSRuntime {
}
}
public:
#if defined(JS_BUILD_BINAST)
js::BinaryASTSupport& binast() { return binast_; }
private:
js::BinaryASTSupport binast_;
#endif // defined(JS_BUILD_BINAST)
public:
#if defined(NIGHTLY_BUILD)
// Support for informing the embedding of any error thrown.

Просмотреть файл

@ -2448,18 +2448,6 @@
value: true
mirror: always
#ifdef JS_BUILD_BINAST
- name: dom.script_loader.binast_encoding.domain.restrict
type: bool
value: true
mirror: always
- name: dom.script_loader.binast_encoding.enabled
type: RelaxedAtomicBool
value: false
mirror: always
#endif
# Whether to enable the JavaScript start-up cache. This causes one of the first
# execution to record the bytecode of the JavaScript function used, and save it
# in the existing cache entry. On the following loads of the same script, the

Просмотреть файл

@ -300,14 +300,6 @@ pref("dom.keyboardevent.keypress.hack.use_legacy_keycode_and_charcode.addl", "")
// explanation for the detail.
pref("dom.mouseevent.click.hack.use_legacy_non-primary_dispatch", "");
#ifdef JS_BUILD_BINAST
// Until we're satisfied that it works nicely, we're restricting
// BinAST to a few partner sites:
// - A subset of Facebook
// - A subset of Cloudflare
pref("dom.script_loader.binast_encoding.domain.restrict.list", "*.facebook.com,static.xx.fbcdn.net,*.cloudflare.com,*.cloudflarestream.com,unpkg.com");
#endif
// Fastback caching - if this pref is negative, then we calculate the number
// of content viewers to cache based on the amount of available memory.
pref("browser.sessionhistory.max_total_viewers", -1);