зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
f4c1e09650
Коммит
8898cf7914
|
@ -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(), ¶ms, &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);
|
||||
|
|
Загрузка…
Ссылка в новой задаче