Merge inbound to mozilla-central. a=merge

This commit is contained in:
Margareta Eliza Balazs 2018-10-16 12:31:18 +03:00
commit 3d989d097f
40 changed files with 1262 additions and 725 deletions

View File

@ -61,32 +61,36 @@ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Location, mInnerWindow)
NS_IMPL_CYCLE_COLLECTING_ADDREF(Location)
NS_IMPL_CYCLE_COLLECTING_RELEASE(Location)
nsresult
Location::CheckURL(nsIURI* aURI, nsDocShellLoadInfo** aLoadInfo)
already_AddRefed<nsDocShellLoadInfo>
Location::CheckURL(nsIURI* aURI, nsIPrincipal& aSubjectPrincipal,
ErrorResult& aRv)
{
*aLoadInfo = nullptr;
nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mDocShell));
NS_ENSURE_TRUE(docShell, NS_ERROR_NOT_AVAILABLE);
if (NS_WARN_IF(!docShell)) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return nullptr;
}
nsCOMPtr<nsIPrincipal> triggeringPrincipal;
nsCOMPtr<nsIURI> sourceURI;
net::ReferrerPolicy referrerPolicy = net::RP_Unset;
if (JSContext *cx = nsContentUtils::GetCurrentJSContext()) {
// No cx means that there's no JS running, or at least no JS that
// was run through code that properly pushed a context onto the
// context stack (as all code that runs JS off of web pages
// does). We won't bother with security checks in this case, but
// we need to create the loadinfo etc.
// Get security manager.
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
NS_ENSURE_STATE(ssm);
if (NS_WARN_IF(!ssm)) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
// Check to see if URI is allowed.
nsresult rv = ssm->CheckLoadURIFromScript(cx, aURI);
NS_ENSURE_SUCCESS(rv, rv);
nsresult rv = ssm->CheckLoadURIWithPrincipal(&aSubjectPrincipal, aURI,
nsIScriptSecurityManager::STANDARD);
if (NS_WARN_IF(NS_FAILED(rv))) {
nsAutoCString spec;
aURI->GetSpec(spec);
aRv.ThrowTypeError<MSG_URL_NOT_LOADABLE>(NS_ConvertUTF8toUTF16(spec));
return nullptr;
}
// Make the load's referrer reflect changes to the document's URI caused by
// push/replaceState, if possible. First, get the document corresponding to
@ -112,7 +116,10 @@ Location::CheckURL(nsIURI* aURI, nsDocShellLoadInfo** aLoadInfo)
docOriginalURI = doc->GetOriginalURI();
docCurrentURI = doc->GetDocumentURI();
rv = doc->NodePrincipal()->GetURI(getter_AddRefs(principalURI));
NS_ENSURE_SUCCESS(rv, rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
return nullptr;
}
triggeringPrincipal = doc->NodePrincipal();
referrerPolicy = doc->GetReferrerPolicy();
@ -139,14 +146,9 @@ Location::CheckURL(nsIURI* aURI, nsDocShellLoadInfo** aLoadInfo)
}
}
}
}
else {
// No document; determine triggeringPrincipal by quering the
// subjectPrincipal, wich is the principal of the current JS
// compartment, or a null principal in case there is no
// compartment yet.
triggeringPrincipal = nsContentUtils::SubjectPrincipal();
}
} else {
// No document; just use our subject principal as the triggering principal.
triggeringPrincipal = &aSubjectPrincipal;
}
// Create load info
@ -159,9 +161,7 @@ Location::CheckURL(nsIURI* aURI, nsDocShellLoadInfo** aLoadInfo)
loadInfo->SetReferrerPolicy(referrerPolicy);
}
loadInfo.swap(*aLoadInfo);
return NS_OK;
return loadInfo.forget();
}
nsresult
@ -206,15 +206,17 @@ Location::GetURI(nsIURI** aURI, bool aGetInnermostURI)
return urifixup->CreateExposableURI(uri, aURI);
}
nsresult
Location::SetURI(nsIURI* aURI, bool aReplace)
void
Location::SetURI(nsIURI* aURI, nsIPrincipal& aSubjectPrincipal,
ErrorResult& aRv, bool aReplace)
{
nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mDocShell));
if (docShell) {
RefPtr<nsDocShellLoadInfo> loadInfo;
if(NS_FAILED(CheckURL(aURI, getter_AddRefs(loadInfo))))
return NS_ERROR_FAILURE;
RefPtr<nsDocShellLoadInfo> loadInfo =
CheckURL(aURI, aSubjectPrincipal, aRv);
if (aRv.Failed()) {
return;
}
if (aReplace) {
loadInfo->SetLoadType(LOAD_STOP_CONTENT_AND_REPLACE);
@ -229,11 +231,12 @@ Location::SetURI(nsIURI* aURI, bool aReplace)
loadInfo->SetSourceDocShell(sourceWindow->GetDocShell());
}
return docShell->LoadURI(aURI, loadInfo,
nsresult rv = docShell->LoadURI(aURI, loadInfo,
nsIWebNavigation::LOAD_FLAGS_NONE, true);
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
}
}
return NS_OK;
}
void
@ -305,7 +308,7 @@ Location::SetHash(const nsAString& aHash,
return;
}
aRv = SetURI(uri);
SetURI(uri, aSubjectPrincipal, aRv);
}
void
@ -359,7 +362,7 @@ Location::SetHost(const nsAString& aHost,
return;
}
aRv = SetURI(uri);
SetURI(uri, aSubjectPrincipal, aRv);
}
void
@ -404,7 +407,7 @@ Location::SetHostname(const nsAString& aHostname,
return;
}
aRv = SetURI(uri);
SetURI(uri, aSubjectPrincipal, aRv);
}
nsresult
@ -430,23 +433,25 @@ Location::GetHref(nsAString& aHref)
void
Location::SetHref(const nsAString& aHref,
nsIPrincipal& aSubjectPrincipal,
ErrorResult& aRv)
{
DoSetHref(aHref, false, aRv);
DoSetHref(aHref, aSubjectPrincipal, false, aRv);
}
void
Location::DoSetHref(const nsAString& aHref, bool aReplace, ErrorResult& aRv)
Location::DoSetHref(const nsAString& aHref, nsIPrincipal& aSubjectPrincipal,
bool aReplace, ErrorResult& aRv)
{
// Get the source of the caller
nsCOMPtr<nsIURI> base = GetSourceBaseURL();
aRv = SetHrefWithBase(aHref, base, aReplace);
SetHrefWithBase(aHref, base, aSubjectPrincipal, aReplace, aRv);
}
nsresult
void
Location::SetHrefWithBase(const nsAString& aHref, nsIURI* aBase,
bool aReplace)
nsIPrincipal& aSubjectPrincipal,
bool aReplace, ErrorResult& aRv)
{
nsresult result;
nsCOMPtr<nsIURI> newUri;
@ -488,9 +493,11 @@ Location::SetHrefWithBase(const nsAString& aHref, nsIURI* aBase,
}
}
return SetURI(newUri, aReplace || inScriptTag);
SetURI(newUri, aSubjectPrincipal, aRv, aReplace || inScriptTag);
return;
}
return result;
aRv.Throw(result);
}
void
@ -571,7 +578,7 @@ Location::SetPathname(const nsAString& aPathname,
return;
}
aRv = SetURI(uri);
SetURI(uri, aSubjectPrincipal, aRv);
}
void
@ -640,7 +647,7 @@ Location::SetPort(const nsAString& aPort,
return;
}
aRv = SetURI(uri);
SetURI(uri, aSubjectPrincipal, aRv);
}
void
@ -737,7 +744,7 @@ Location::SetProtocol(const nsAString& aProtocol,
return;
}
aRv = SetURI(uri);
SetURI(uri, aSubjectPrincipal, aRv);
}
void
@ -802,7 +809,7 @@ Location::SetSearch(const nsAString& aSearch,
return;
}
aRv = SetURI(uri);
SetURI(uri, aSubjectPrincipal, aRv);
}
nsresult
@ -858,7 +865,7 @@ Location::Replace(const nsAString& aUrl,
nsIPrincipal& aSubjectPrincipal,
ErrorResult& aRv)
{
DoSetHref(aUrl, true, aRv);
DoSetHref(aUrl, aSubjectPrincipal, true, aRv);
}
void
@ -871,7 +878,7 @@ Location::Assign(const nsAString& aUrl,
return;
}
DoSetHref(aUrl, false, aRv);
DoSetHref(aUrl, aSubjectPrincipal, false, aRv);
}
already_AddRefed<nsIURI>

View File

@ -68,6 +68,7 @@ public:
}
void SetHref(const nsAString& aHref,
nsIPrincipal& aSubjectPrincipal,
ErrorResult& aError);
void GetOrigin(nsAString& aOrigin,
@ -166,17 +167,27 @@ protected:
// Note, this method can return NS_OK with a null value for aURL. This happens
// if the docShell is null.
nsresult GetURI(nsIURI** aURL, bool aGetInnermostURI = false);
nsresult SetURI(nsIURI* aURL, bool aReplace = false);
nsresult SetHrefWithBase(const nsAString& aHref, nsIURI* aBase,
bool aReplace);
void SetURI(nsIURI* aURL, nsIPrincipal& aSubjectPrincipal,
ErrorResult& aRv, bool aReplace = false);
void SetHrefWithBase(const nsAString& aHref, nsIURI* aBase,
nsIPrincipal& aSubjectPrincipal,
bool aReplace, ErrorResult& aRv);
// Helper for Assign/SetHref/Replace
void DoSetHref(const nsAString& aHref, bool aReplace, ErrorResult& aRv);
void DoSetHref(const nsAString& aHref, nsIPrincipal& aSubjectPrincipal,
bool aReplace, ErrorResult& aRv);
// Get the base URL we should be using for our relative URL
// resolution for SetHref/Assign/Replace.
already_AddRefed<nsIURI> GetSourceBaseURL();
nsresult CheckURL(nsIURI *url, nsDocShellLoadInfo** aLoadInfo);
// Check whether it's OK to load the given url with the given subject
// principal, and if so construct the right nsDocShellLoadInfo for the load
// and return it.
already_AddRefed<nsDocShellLoadInfo> CheckURL(nsIURI *url,
nsIPrincipal& aSubjectPrincipal,
ErrorResult& aRv);
bool CallerSubsumes(nsIPrincipal* aSubjectPrincipal);
nsString mCachedHash;

View File

@ -116,3 +116,4 @@ MSG_DEF(MSG_INVALID_PANNERNODE_REFDISTANCE_ERROR, 0, JSEXN_RANGEERR, "The refDis
MSG_DEF(MSG_INVALID_PANNERNODE_MAXDISTANCE_ERROR, 0, JSEXN_RANGEERR, "The maxDistance value passed to PannerNode must be positive.")
MSG_DEF(MSG_INVALID_PANNERNODE_ROLLOFF_ERROR, 0, JSEXN_RANGEERR, "The rolloffFactor value passed to PannerNode must not be negative.")
MSG_DEF(MSG_NOT_ARRAY_NOR_UNDEFINED, 1, JSEXN_TYPEERR, "{0} is neither an array nor undefined.")
MSG_DEF(MSG_URL_NOT_LOADABLE, 1, JSEXN_TYPEERR, "Access to '{0}' from script denied.")

View File

@ -2412,7 +2412,7 @@ ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest)
if (srcBuf) {
if (recordreplay::IsRecordingOrReplaying()) {
recordreplay::NoteContentParse(this, options.filename(), "application/javascript",
recordreplay::NoteContentParse16(this, options.filename(), "application/javascript",
srcBuf->get(), srcBuf->length());
}
rv = exec.CompileAndExec(options, *srcBuf, &script);

View File

@ -20,7 +20,7 @@ interface Location {
[Throws, NeedsSubjectPrincipal]
stringifier;
[Throws, CrossOriginWritable, GetterNeedsSubjectPrincipal]
[Throws, CrossOriginWritable, NeedsSubjectPrincipal]
attribute USVString href;
[Throws, NeedsSubjectPrincipal]
readonly attribute USVString origin;

View File

@ -16,6 +16,7 @@
#include "builtin/Array.h"
#include "builtin/Reflect.h"
#include "frontend/ParseNode.h"
#include "frontend/Parser.h"
#include "frontend/TokenStream.h"
#include "js/CharacterEncoding.h"
@ -24,7 +25,6 @@
#include "vm/JSObject.h"
#include "vm/RegExpObject.h"
#include "frontend/ParseNode-inl.h"
#include "vm/JSObject-inl.h"
using namespace js;

View File

@ -20,13 +20,13 @@
#include "frontend/BinSource.h"
#include "frontend/BinTokenReaderTester.h"
#include "frontend/FullParseHandler.h"
#include "frontend/ParseNode.h"
#include "frontend/Parser.h"
#include "frontend/SharedContext.h"
#include "vm/RegExpObject.h"
#include "frontend/ParseContext-inl.h"
#include "frontend/ParseNode-inl.h"
namespace js {
namespace frontend {
@ -2843,8 +2843,9 @@ BinASTParser<Tok>::parseInterfaceCallExpression(const size_t start, const BinKin
// Check for direct calls to `eval`.
if (factory_.isEvalName(callee, cx_)) {
if (!parseContext_->varScope().lookupDeclaredNameForAdd(callee->name())
&& !parseContext_->innermostScope()->lookupDeclaredNameForAdd(callee->name())) {
if (!parseContext_->varScope().lookupDeclaredNameForAdd(cx_->names().eval)
&& !parseContext_->innermostScope()->lookupDeclaredNameForAdd(cx_->names().eval))
{
// This is a direct call to `eval`.
if (!parseContext_->sc()->hasDirectEval()) {
return raiseMissingDirectEvalInAssertedScope();
@ -4545,7 +4546,7 @@ BinASTParser<Tok>::parseInterfaceShorthandProperty(const size_t start, const Bin
MOZ_ASSERT(name->isKind(ParseNodeKind::Name));
MOZ_ASSERT(!factory_.isUsableAsObjectPropertyName(name));
BINJS_TRY_DECL(propName, factory_.newObjectLiteralPropertyName(name->name(), tokenizer_->pos(start)));
BINJS_TRY_DECL(propName, factory_.newObjectLiteralPropertyName(name->template as<NameNode>().name(), tokenizer_->pos(start)));
BINJS_TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(propName, name, AccessorType::None));
result->setKind(ParseNodeKind::Shorthand);

View File

@ -17,6 +17,7 @@
#include "frontend/BinSource-macros.h"
#include "frontend/BinTokenReaderTester.h"
#include "frontend/FullParseHandler.h"
#include "frontend/ParseNode.h"
#include "frontend/Parser.h"
#include "frontend/SharedContext.h"
@ -24,7 +25,6 @@
#include "vm/RegExpObject.h"
#include "frontend/ParseContext-inl.h"
#include "frontend/ParseNode-inl.h"
#include "vm/JSContext-inl.h"
// # About compliance with EcmaScript
@ -558,7 +558,7 @@ BinASTParser<Tok>::checkPositionalParameterIndices(Handle<GCVector<JSAtom*>> pos
}
// Step 2.a.i.
if (param->name() != name) {
if (param->as<NameNode>().name() != name) {
// Step 2.a.ii.1.
return raiseError("Name mismatch between AssertedPositionalParameterName in AssertedParameterScope.paramNames and actual parameter");
}

View File

@ -38,13 +38,13 @@ cpp:
#include "frontend/BinSource.h"
#include "frontend/BinTokenReaderTester.h"
#include "frontend/FullParseHandler.h"
#include "frontend/ParseNode.h"
#include "frontend/Parser.h"
#include "frontend/SharedContext.h"
#include "vm/RegExpObject.h"
#include "frontend/ParseContext-inl.h"
#include "frontend/ParseNode-inl.h"
namespace js {
namespace frontend {
@ -506,8 +506,9 @@ CallExpression:
// Check for direct calls to `eval`.
if (factory_.isEvalName(callee, cx_)) {
if (!parseContext_->varScope().lookupDeclaredNameForAdd(callee->name())
&& !parseContext_->innermostScope()->lookupDeclaredNameForAdd(callee->name())) {
if (!parseContext_->varScope().lookupDeclaredNameForAdd(cx_->names().eval)
&& !parseContext_->innermostScope()->lookupDeclaredNameForAdd(cx_->names().eval))
{
// This is a direct call to `eval`.
if (!parseContext_->sc()->hasDirectEval()) {
return raiseMissingDirectEvalInAssertedScope();
@ -1113,7 +1114,7 @@ ShorthandProperty:
build: |
MOZ_ASSERT(name->isKind(ParseNodeKind::Name));
MOZ_ASSERT(!factory_.isUsableAsObjectPropertyName(name));
BINJS_TRY_DECL(propName, factory_.newObjectLiteralPropertyName(name->name(), tokenizer_->pos(start)));
BINJS_TRY_DECL(propName, factory_.newObjectLiteralPropertyName(name->template as<NameNode>().name(), tokenizer_->pos(start)));
BINJS_TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(propName, name, AccessorType::None));
result->setKind(ParseNodeKind::Shorthand);

View File

@ -36,6 +36,7 @@
#include "frontend/ForOfLoopControl.h"
#include "frontend/IfEmitter.h"
#include "frontend/NameOpEmitter.h"
#include "frontend/ParseNode.h"
#include "frontend/Parser.h"
#include "frontend/PropOpEmitter.h"
#include "frontend/SwitchEmitter.h"
@ -53,7 +54,6 @@
#include "vm/Stack.h"
#include "wasm/AsmJS.h"
#include "frontend/ParseNode-inl.h"
#include "vm/JSObject-inl.h"
using namespace js;
@ -1716,9 +1716,9 @@ BytecodeEmitter::emitGetNameAtLocation(JSAtom* name, const NameLocation& loc)
}
bool
BytecodeEmitter::emitGetName(ParseNode* pn)
BytecodeEmitter::emitGetName(NameNode* name)
{
return emitGetName(pn->name());
return emitGetName(name->name());
}
bool
@ -2221,7 +2221,7 @@ BytecodeEmitter::emitSetThis(BinaryNode* setThisNode)
MOZ_ASSERT(setThisNode->isKind(ParseNodeKind::SetThis));
MOZ_ASSERT(setThisNode->left()->isKind(ParseNodeKind::Name));
RootedAtom name(cx, setThisNode->left()->name());
RootedAtom name(cx, setThisNode->left()->as<NameNode>().name());
// The 'this' binding is not lexical, but due to super() semantics this
// initialization needs to be treated as a lexical one.
@ -2558,7 +2558,7 @@ BytecodeEmitter::emitSetOrInitializeDestructuring(ParseNode* target, Destructuri
} else {
switch (target->getKind()) {
case ParseNodeKind::Name: {
RootedAtom name(cx, target->name());
RootedAtom name(cx, target->as<NameNode>().name());
NameLocation loc;
NameOpEmitter::Kind kind;
switch (flav) {
@ -3027,7 +3027,7 @@ BytecodeEmitter::emitInitializer(ParseNode* initializer, ParseNode* pattern)
if (initializer->isDirectRHSAnonFunction()) {
MOZ_ASSERT(!pattern->isInParens());
RootedAtom name(cx, pattern->name());
RootedAtom name(cx, pattern->as<NameNode>().name());
if (!setOrEmitSetFunName(initializer, name)) {
return false;
}
@ -3769,7 +3769,8 @@ BytecodeEmitter::emitDeclarationList(ListNode* declList)
return false;
}
} else {
if (!emitSingleDeclaration(declList, decl, decl->as<NameNode>().initializer())) {
NameNode* name = &decl->as<NameNode>();
if (!emitSingleDeclaration(declList, name, name->initializer())) {
return false;
}
}
@ -3778,7 +3779,7 @@ BytecodeEmitter::emitDeclarationList(ListNode* declList)
}
bool
BytecodeEmitter::emitSingleDeclaration(ParseNode* declList, ParseNode* decl,
BytecodeEmitter::emitSingleDeclaration(ListNode* declList, NameNode* decl,
ParseNode* initializer)
{
MOZ_ASSERT(decl->isKind(ParseNodeKind::Name));
@ -3865,8 +3866,10 @@ BytecodeEmitter::emitAssignment(ParseNode* lhs, JSOp compoundOp, ParseNode* rhs)
// Name assignments are handled separately because choosing ops and when
// to emit BINDNAME is involved and should avoid duplication.
if (lhs->isKind(ParseNodeKind::Name)) {
NameNode* nameNode = &lhs->as<NameNode>();
RootedAtom name(cx, nameNode->name());
NameOpEmitter noe(this,
lhs->name(),
name,
isCompound
? NameOpEmitter::Kind::CompoundAssignment
: NameOpEmitter::Kind::SimpleAssignment);
@ -3881,9 +3884,8 @@ BytecodeEmitter::emitAssignment(ParseNode* lhs, JSOp compoundOp, ParseNode* rhs)
return false;
}
if (rhs && rhs->isDirectRHSAnonFunction()) {
MOZ_ASSERT(!lhs->isInParens());
MOZ_ASSERT(!nameNode->isInParens());
MOZ_ASSERT(!isCompound);
RootedAtom name(cx, lhs->name());
if (!setOrEmitSetFunName(rhs, name)) { // ENV? VAL? RHS
return false;
}
@ -4236,13 +4238,13 @@ ParseNode::getConstantValue(JSContext* cx, AllowConstantObjects allowObjects,
}
bool
BytecodeEmitter::emitSingletonInitialiser(ParseNode* pn)
BytecodeEmitter::emitSingletonInitialiser(ListNode* objOrArray)
{
NewObjectKind newKind =
(pn->getKind() == ParseNodeKind::Object) ? SingletonObject : TenuredObject;
(objOrArray->getKind() == ParseNodeKind::Object) ? SingletonObject : TenuredObject;
RootedValue value(cx);
if (!pn->getConstantValue(cx, ParseNode::AllowObjects, &value, nullptr, 0, newKind)) {
if (!objOrArray->getConstantValue(cx, ParseNode::AllowObjects, &value, nullptr, 0, newKind)) {
return false;
}
@ -4327,7 +4329,7 @@ BytecodeEmitter::emitCatch(BinaryNode* catchClause)
break;
case ParseNodeKind::Name:
if (!emitLexicalInitialization(param)) {
if (!emitLexicalInitialization(&param->as<NameNode>())) {
return false;
}
if (!emit1(JSOP_POP)) {
@ -4909,7 +4911,8 @@ BytecodeEmitter::emitInitializeForInOrOfTarget(TernaryNode* forHead)
target = parser->astGenerator().singleBindingFromDeclaration(&target->as<ListNode>());
if (target->isKind(ParseNodeKind::Name)) {
NameOpEmitter noe(this, target->name(), NameOpEmitter::Kind::Initialize);
NameNode* nameNode = &target->as<NameNode>();
NameOpEmitter noe(this, nameNode->name(), NameOpEmitter::Kind::Initialize);
if (!noe.prepareForRhs()) {
return false;
}
@ -4965,8 +4968,7 @@ BytecodeEmitter::emitForOf(ForNode* forOfLoop, const EmitterScope* headLexicalEm
bool allowSelfHostedIter = false;
if (emitterMode == BytecodeEmitter::SelfHosting &&
forHeadExpr->isKind(ParseNodeKind::Call) &&
forHeadExpr->as<BinaryNode>().left()->name() == cx->names().allowContentIter)
{
forHeadExpr->as<BinaryNode>().left()->isName(cx->names().allowContentIter)) {
allowSelfHostedIter = true;
}
@ -5036,7 +5038,8 @@ BytecodeEmitter::emitForIn(ForNode* forInLoop, const EmitterScope* headLexicalEm
return false;
}
NameOpEmitter noe(this, decl->name(), NameOpEmitter::Kind::Initialize);
NameNode* nameNode = &decl->as<NameNode>();
NameOpEmitter noe(this, nameNode->name(), NameOpEmitter::Kind::Initialize);
if (!noe.prepareForRhs()) {
return false;
}
@ -5617,13 +5620,12 @@ BytecodeEmitter::emitContinue(PropertyName* label)
}
bool
BytecodeEmitter::emitGetFunctionThis(ParseNode* pn)
BytecodeEmitter::emitGetFunctionThis(NameNode* thisName)
{
MOZ_ASSERT(sc->thisBinding() == ThisBinding::Function);
MOZ_ASSERT(pn->isKind(ParseNodeKind::Name));
MOZ_ASSERT(pn->name() == cx->names().dotThis);
MOZ_ASSERT(thisName->isName(cx->names().dotThis));
return emitGetFunctionThis(Some(pn->pn_pos.begin));
return emitGetFunctionThis(Some(thisName->pn_pos.begin));
}
bool
@ -5651,13 +5653,15 @@ bool
BytecodeEmitter::emitGetThisForSuperBase(UnaryNode* superBase)
{
MOZ_ASSERT(superBase->isKind(ParseNodeKind::SuperBase));
return emitGetFunctionThis(superBase->kid()); // THIS
NameNode* nameNode = &superBase->kid()->as<NameNode>();
return emitGetFunctionThis(nameNode); // THIS
}
bool
BytecodeEmitter::emitThisLiteral(ThisLiteral* pn)
{
if (ParseNode* thisName = pn->kid()) {
if (ParseNode* kid = pn->kid()) {
NameNode* thisName = &kid->as<NameNode>();
return emitGetFunctionThis(thisName); // THIS
}
@ -6520,7 +6524,7 @@ BytecodeEmitter::emitSelfHostedCallFunction(BinaryNode* callNode)
//
// argc is set to the amount of actually emitted args and the
// emitting of args below is disabled by setting emitArgs to false.
ParseNode* calleeNode = callNode->left();
NameNode* calleeNode = &callNode->left()->as<NameNode>();
ListNode* argsList = &callNode->right()->as<ListNode>();
const char* errorName = SelfHostedCallFunctionName(calleeNode->name(), cx);
@ -6540,8 +6544,7 @@ BytecodeEmitter::emitSelfHostedCallFunction(BinaryNode* callNode)
ParseNode* funNode = argsList->head();
if (constructing) {
callOp = JSOP_NEW;
} else if (funNode->getKind() == ParseNodeKind::Name &&
funNode->name() == cx->names().std_Function_apply) {
} else if (funNode->isName(cx->names().std_Function_apply)) {
callOp = JSOP_FUNAPPLY;
}
@ -6735,7 +6738,7 @@ BytecodeEmitter::emitSelfHostedGetPropertySuper(BinaryNode* callNode)
}
bool
BytecodeEmitter::isRestParameter(ParseNode* pn)
BytecodeEmitter::isRestParameter(ParseNode* expr)
{
if (!sc->isFunctionBox()) {
return false;
@ -6747,20 +6750,20 @@ BytecodeEmitter::isRestParameter(ParseNode* pn)
return false;
}
if (!pn->isKind(ParseNodeKind::Name)) {
if (emitterMode == BytecodeEmitter::SelfHosting && pn->isKind(ParseNodeKind::Call)) {
BinaryNode* callNode = &pn->as<BinaryNode>();
ParseNode* calleeNode = callNode->left();
if (calleeNode->getKind() == ParseNodeKind::Name &&
calleeNode->name() == cx->names().allowContentIter)
if (!expr->isKind(ParseNodeKind::Name)) {
if (emitterMode == BytecodeEmitter::SelfHosting &&
expr->isKind(ParseNodeKind::Call))
{
BinaryNode* callNode = &expr->as<BinaryNode>();
ParseNode* calleeNode = callNode->left();
if (calleeNode->isName(cx->names().allowContentIter)) {
return isRestParameter(callNode->right()->as<ListNode>().head());
}
}
return false;
}
JSAtom* name = pn->name();
JSAtom* name = expr->as<NameNode>().name();
Maybe<NameLocation> paramLoc = locationOfNameBoundInFunctionScope(name);
if (paramLoc && lookupName(name) == *paramLoc) {
FunctionScope::Data* bindings = funbox->functionScopeBindings();
@ -6781,8 +6784,8 @@ BytecodeEmitter::emitCalleeAndThis(ParseNode* callee, ParseNode* call, CallOrNew
{
switch (callee->getKind()) {
case ParseNodeKind::Name:
if (!cone.emitNameCallee(callee->name())) { // CALLEE THIS
return false;
if (!cone.emitNameCallee(callee->as<NameNode>().name())) {
return false; // CALLEE THIS
}
break;
case ParseNodeKind::Dot: {
@ -6958,7 +6961,7 @@ BytecodeEmitter::emitCallOrNew(BinaryNode* callNode,
// Calls to "forceInterpreter", "callFunction",
// "callContentFunction", or "resumeGenerator" in self-hosted
// code generate inline bytecode.
PropertyName* calleeName = calleeNode->name();
PropertyName* calleeName = calleeNode->as<NameNode>().name();
if (calleeName == cx->names().callFunction ||
calleeName == cx->names().callContentFunction ||
calleeName == cx->names().constructContentFunction)
@ -7665,8 +7668,7 @@ BytecodeEmitter::emitArray(ParseNode* arrayHead, uint32_t count)
if (emitterMode == BytecodeEmitter::SelfHosting &&
expr->isKind(ParseNodeKind::Call) &&
expr->as<BinaryNode>().left()->name() == cx->names().allowContentIter)
{
expr->as<BinaryNode>().left()->isName(cx->names().allowContentIter)) {
allowSelfHostedIter = true;
}
} else {
@ -7955,7 +7957,7 @@ BytecodeEmitter::emitFunctionFormalParameters(ListNode* paramsBody)
return false;
}
} else if (hasParameterExprs || isRest) {
RootedAtom paramName(cx, bindingElement->name());
RootedAtom paramName(cx, bindingElement->as<NameNode>().name());
NameLocation paramLoc = *locationOfNameBoundInScope(paramName, funScope);
NameOpEmitter noe(this, paramName, paramLoc, NameOpEmitter::Kind::Initialize);
if (!noe.prepareForRhs()) {
@ -8102,9 +8104,9 @@ BytecodeEmitter::emitFunctionBody(ParseNode* funBody)
}
bool
BytecodeEmitter::emitLexicalInitialization(ParseNode* pn)
BytecodeEmitter::emitLexicalInitialization(NameNode* name)
{
NameOpEmitter noe(this, pn->name(), NameOpEmitter::Kind::Initialize);
NameOpEmitter noe(this, name->name(), NameOpEmitter::Kind::Initialize);
if (!noe.prepareForRhs()) {
return false;
}
@ -8326,7 +8328,7 @@ BytecodeEmitter::emitClass(ClassNode* classNode)
}
if (names) {
ParseNode* innerName = names->innerBinding();
NameNode* innerName = names->innerBinding();
if (!emitLexicalInitialization(innerName)) { // ... CONSTRUCTOR
return false;
}
@ -8337,8 +8339,7 @@ BytecodeEmitter::emitClass(ClassNode* classNode)
}
emitterScope.reset();
ParseNode* outerName = names->outerBinding();
if (outerName) {
if (NameNode* outerName = names->outerBinding()) {
if (!emitLexicalInitialization(outerName)) { // ... CONSTRUCTOR
return false;
}
@ -8368,7 +8369,7 @@ BytecodeEmitter::emitExportDefault(BinaryNode* exportNode)
}
if (ParseNode* binding = exportNode->right()) {
if (!emitLexicalInitialization(binding)) {
if (!emitLexicalInitialization(&binding->as<NameNode>())) {
return false;
}
@ -8804,7 +8805,7 @@ BytecodeEmitter::emitTree(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::
break;
case ParseNodeKind::Name:
if (!emitGetName(pn)) {
if (!emitGetName(&pn->as<NameNode>())) {
return false;
}
break;

View File

@ -17,6 +17,7 @@
#include "frontend/EitherParser.h"
#include "frontend/JumpList.h"
#include "frontend/NameFunctions.h"
#include "frontend/ParseNode.h"
#include "frontend/SharedContext.h"
#include "frontend/SourceNotes.h"
#include "frontend/ValueUsage.h"
@ -521,7 +522,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter
MOZ_MUST_USE bool emitNumberOp(double dval);
MOZ_MUST_USE bool emitThisLiteral(ThisLiteral* pn);
MOZ_MUST_USE bool emitGetFunctionThis(ParseNode* pn);
MOZ_MUST_USE bool emitGetFunctionThis(NameNode* thisName);
MOZ_MUST_USE bool emitGetFunctionThis(const mozilla::Maybe<uint32_t>& offset);
MOZ_MUST_USE bool emitGetThisForSuperBase(UnaryNode* superBase);
MOZ_MUST_USE bool emitSetThis(BinaryNode* setThisNode);
@ -585,18 +586,18 @@ struct MOZ_STACK_CLASS BytecodeEmitter
MOZ_MUST_USE bool emitGetName(JSAtom* name) {
return emitGetNameAtLocation(name, lookupName(name));
}
MOZ_MUST_USE bool emitGetName(ParseNode* pn);
MOZ_MUST_USE bool emitGetName(NameNode* name);
MOZ_MUST_USE bool emitTDZCheckIfNeeded(JSAtom* name, const NameLocation& loc);
MOZ_MUST_USE bool emitNameIncDec(UnaryNode* incDec);
MOZ_MUST_USE bool emitDeclarationList(ListNode* declList);
MOZ_MUST_USE bool emitSingleDeclaration(ParseNode* declList, ParseNode* decl,
MOZ_MUST_USE bool emitSingleDeclaration(ListNode* declList, NameNode* decl,
ParseNode* initializer);
MOZ_MUST_USE bool emitNewInit();
MOZ_MUST_USE bool emitSingletonInitialiser(ParseNode* pn);
MOZ_MUST_USE bool emitSingletonInitialiser(ListNode* objOrArray);
MOZ_MUST_USE bool emitPrepareIteratorResult();
MOZ_MUST_USE bool emitFinishIteratorResult(bool done);
@ -761,7 +762,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter
MOZ_MUST_USE bool emitConditionalExpression(ConditionalExpression& conditional,
ValueUsage valueUsage = ValueUsage::WantValue);
bool isRestParameter(ParseNode* pn);
bool isRestParameter(ParseNode* expr);
MOZ_MUST_USE bool emitArguments(ListNode* argsList, bool isCall, bool isSpread,
CallOrNewEmitter& cone);
@ -792,8 +793,8 @@ struct MOZ_STACK_CLASS BytecodeEmitter
MOZ_MUST_USE bool emitFunctionFormalParametersAndBody(ListNode* paramsBody);
MOZ_MUST_USE bool emitFunctionFormalParameters(ListNode* paramsBody);
MOZ_MUST_USE bool emitInitializeFunctionSpecialNames();
MOZ_MUST_USE bool emitFunctionBody(ParseNode* pn);
MOZ_MUST_USE bool emitLexicalInitialization(ParseNode* pn);
MOZ_MUST_USE bool emitFunctionBody(ParseNode* funBody);
MOZ_MUST_USE bool emitLexicalInitialization(NameNode* name);
// Emit bytecode for the spread operator.
//

View File

@ -42,8 +42,7 @@ class MOZ_RAII AutoEmittingRunOnceLambda
// CallOrNewEmitter cone(this, JSOP_CALL,
// CallOrNewEmitter::ArgumentsKind::Other,
// ValueUsage::WantValue);
// cone.emitNameCallee();
// emit(print);
// cone.emitNameCallee(print);
// cone.emitThis();
// cone.prepareForNonSpreadArguments();
// emit(arg);
@ -108,11 +107,11 @@ class MOZ_RAII AutoEmittingRunOnceLambda
// CallOrNewEmitter cone(this, JSOP_SPREADCALL,
// CallOrNewEmitter::ArgumentsKind::Other,
// ValueUsage::WantValue);
// cone.emitNameCallee();
// emit(print);
// cone.emitNameCallee(print);
// cone.emitThis();
// if (cone.wantSpreadOperand())
// if (cone.wantSpreadOperand()) {
// emit(arg)
// }
// cone.emitSpreadArgumentsTest();
// emit([...arg]);
// cone.emitEnd(1, Some(offset_of_callee));
@ -122,11 +121,11 @@ class MOZ_RAII AutoEmittingRunOnceLambda
// CallOrNewEmitter cone(this, JSOP_SPREADCALL,
// CallOrNewEmitter::ArgumentsKind::SingleSpreadRest,
// ValueUsage::WantValue);
// cone.emitNameCallee();
// emit(print);
// cone.emitNameCallee(print);
// cone.emitThis();
// if (cone.wantSpreadOperand())
// if (cone.wantSpreadOperand()) {
// emit(arg)
// }
// cone.emitSpreadArgumentsTest();
// emit([...arg]);
// cone.emitEnd(1, Some(offset_of_callee));
@ -135,8 +134,7 @@ class MOZ_RAII AutoEmittingRunOnceLambda
// CallOrNewEmitter cone(this, JSOP_NEW,
// CallOrNewEmitter::ArgumentsKind::Other,
// ValueUsage::WantValue);
// cone.emitNameCallee();
// emit(f);
// cone.emitNameCallee(f);
// cone.emitThis();
// cone.prepareForNonSpreadArguments();
// emit(arg);

View File

@ -1,30 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* 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_ParseNode_inl_h
#define frontend_ParseNode_inl_h
#include "frontend/ParseNode.h"
#include "frontend/SharedContext.h"
namespace js {
namespace frontend {
inline PropertyName*
ParseNode::name() const
{
MOZ_ASSERT(isKind(ParseNodeKind::Function) || isKind(ParseNodeKind::Name));
JSAtom* atom = isKind(ParseNodeKind::Function)
? as<CodeNode>().funbox()->function()->explicitName()
: as<NameNode>().atom();
return atom->asPropertyName();
}
} /* namespace frontend */
} /* namespace js */
#endif /* frontend_ParseNode_inl_h */

View File

@ -4,7 +4,7 @@
* 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/ParseNode-inl.h"
#include "frontend/ParseNode.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/FloatingPoint.h"

View File

@ -641,6 +641,7 @@ class ParseNode
ParseNodeKind kind = getKind();
return ParseNodeKind::BinOpFirst <= kind && kind <= ParseNodeKind::BinOpLast;
}
inline bool isName(PropertyName* name) const;
/* Boolean attributes. */
bool isInParens() const { return pn_parens; }
@ -740,9 +741,6 @@ class ParseNode
appendOrCreateList(ParseNodeKind kind, ParseNode* left, ParseNode* right,
FullParseHandler* handler, ParseContext* pc);
// include "ParseNode-inl.h" for these methods.
inline PropertyName* name() const;
/* True if pn is a parsenode representing a literal constant. */
bool isLiteral() const {
return isKind(ParseNodeKind::Number) ||
@ -841,6 +839,11 @@ class NameNode : public ParseNode
return pn_u.name.atom;
}
PropertyName* name() const {
MOZ_ASSERT(isKind(ParseNodeKind::Name));
return atom()->asPropertyName();
}
ParseNode* initializer() const {
return pn_u.name.initOrStmt;
}
@ -859,6 +862,12 @@ class NameNode : public ParseNode
}
};
inline bool
ParseNode::isName(PropertyName* name) const
{
return getKind() == ParseNodeKind::Name && as<NameNode>().name() == name;
}
class UnaryNode : public ParseNode
{
public:

View File

@ -35,6 +35,7 @@
#include "builtin/SelfHostingDefines.h"
#include "frontend/BytecodeCompiler.h"
#include "frontend/FoldConstants.h"
#include "frontend/ParseNode.h"
#include "frontend/TokenStream.h"
#include "irregexp/RegExpParser.h"
#include "vm/BytecodeUtil.h"
@ -47,7 +48,6 @@
#include "wasm/AsmJS.h"
#include "frontend/ParseContext-inl.h"
#include "frontend/ParseNode-inl.h"
#include "vm/EnvironmentObject-inl.h"
using namespace js;

View File

@ -848,6 +848,15 @@ class SourceCompressionTask
void work();
void complete();
private:
struct PerformTaskWork;
friend struct PerformTaskWork;
// The work algorithm, aware whether it's compressing one-byte UTF-8 source
// text or UTF-16, for CharT either Utf8Unit or char16_t. Invoked by
// work() after doing a type-test of the ScriptSource*.
template<typename CharT> void workEncodingSpecific();
};
// A PromiseHelperTask is an OffThreadPromiseTask that executes a single job on

View File

@ -14,6 +14,7 @@
#include "mozilla/CheckedInt.h"
#include "mozilla/Maybe.h"
#include "mozilla/Range.h"
#include "mozilla/Utf8.h"
#include <string.h>
@ -65,6 +66,7 @@ using mozilla::ArrayLength;
using mozilla::CheckedInt;
using mozilla::Maybe;
using mozilla::Some;
using mozilla::Utf8Unit;
using JS::AutoStableStringChars;
using JS::CompileOptions;
@ -1782,7 +1784,23 @@ JSFunction::createScriptForLazilyInterpretedFunction(JSContext* cx, HandleFuncti
// Parse and compile the script from source.
UncompressedSourceCache::AutoHoldEntry holder;
ScriptSource::PinnedChars chars(cx, lazy->scriptSource(), holder,
if (lazy->scriptSource()->hasSourceType<Utf8Unit>()) {
// UTF-8 source text.
ScriptSource::PinnedChars<Utf8Unit> chars(cx, lazy->scriptSource(), holder,
lazy->sourceStart(), lazyLength);
if (!chars.get()) {
return false;
}
// XXX There are no UTF-8 ScriptSources now, so just crash so this
// gets filled in later.
MOZ_CRASH("UTF-8 lazy function compilation not implemented yet");
} else {
MOZ_ASSERT(lazy->scriptSource()->hasSourceType<char16_t>());
// UTF-16 source text.
ScriptSource::PinnedChars<char16_t> chars(cx, lazy->scriptSource(), holder,
lazy->sourceStart(), lazyLength);
if (!chars.get()) {
return false;
@ -1797,6 +1815,7 @@ JSFunction::createScriptForLazilyInterpretedFunction(JSContext* cx, HandleFuncti
return false;
}
}
}
script = fun->nonLazyScript();

View File

@ -22,6 +22,7 @@
#include <algorithm>
#include <new>
#include <string.h>
#include <type_traits>
#include <utility>
#include "jsapi.h"
@ -73,6 +74,9 @@ using namespace js;
using mozilla::Maybe;
using mozilla::PodCopy;
using mozilla::PointerRangeSize;
using mozilla::Utf8AsUnsignedChars;
using mozilla::Utf8Unit;
using JS::CompileOptions;
using JS::ReadOnlyCompileOptions;
@ -1566,7 +1570,10 @@ JSScript::loadSource(JSContext* cx, ScriptSource* ss, bool* worked)
if (!src) {
return true;
}
if (!ss->setSource(cx, UniqueTwoByteChars(src), length)) {
// XXX On-demand source is currently only UTF-16. Perhaps it should be
// changed to UTF-8, or UTF-8 be allowed in addition to UTF-16?
if (!ss->setSource(cx, EntryChars<char16_t>(src), length)) {
return false;
}
@ -1588,48 +1595,6 @@ JSScript::appendSourceDataForToString(JSContext* cx, StringBuffer& buf)
return scriptSource()->appendSubstring(cx, buf, toStringStart(), toStringEnd());
}
UncompressedSourceCache::AutoHoldEntry::AutoHoldEntry()
: cache_(nullptr), sourceChunk_()
{
}
void
UncompressedSourceCache::AutoHoldEntry::holdEntry(UncompressedSourceCache* cache,
const ScriptSourceChunk& sourceChunk)
{
// Initialise the holder for a specific cache and script source. This will
// hold on to the cached source chars in the event that the cache is purged.
MOZ_ASSERT(!cache_ && !sourceChunk_.valid() && !charsToFree_);
cache_ = cache;
sourceChunk_ = sourceChunk;
}
void
UncompressedSourceCache::AutoHoldEntry::holdChars(UniqueTwoByteChars chars)
{
MOZ_ASSERT(!cache_ && !sourceChunk_.valid() && !charsToFree_);
charsToFree_ = std::move(chars);
}
void
UncompressedSourceCache::AutoHoldEntry::deferDelete(UniqueTwoByteChars chars)
{
// Take ownership of source chars now the cache is being purged. Remove our
// reference to the ScriptSource which might soon be destroyed.
MOZ_ASSERT(cache_ && sourceChunk_.valid() && !charsToFree_);
cache_ = nullptr;
sourceChunk_ = ScriptSourceChunk();
charsToFree_ = std::move(chars);
}
UncompressedSourceCache::AutoHoldEntry::~AutoHoldEntry()
{
if (cache_) {
MOZ_ASSERT(sourceChunk_.valid());
cache_->releaseEntry(*this);
}
}
void
UncompressedSourceCache::holdEntry(AutoHoldEntry& holder, const ScriptSourceChunk& ssc)
{
@ -1645,36 +1610,38 @@ UncompressedSourceCache::releaseEntry(AutoHoldEntry& holder)
holder_ = nullptr;
}
const char16_t*
template<typename CharT>
const CharT*
UncompressedSourceCache::lookup(const ScriptSourceChunk& ssc, AutoHoldEntry& holder)
{
MOZ_ASSERT(!holder_);
MOZ_ASSERT(ssc.ss->compressedSourceIs<CharT>());
if (!map_) {
return nullptr;
}
if (Map::Ptr p = map_->lookup(ssc)) {
holdEntry(holder, ssc);
return p->value().get();
return static_cast<const CharT*>(p->value().get());
}
return nullptr;
}
bool
UncompressedSourceCache::put(const ScriptSourceChunk& ssc, UniqueTwoByteChars str,
AutoHoldEntry& holder)
UncompressedSourceCache::put(const ScriptSourceChunk& ssc, SourceData data, AutoHoldEntry& holder)
{
MOZ_ASSERT(!holder_);
if (!map_) {
UniquePtr<Map> map = MakeUnique<Map>();
if (!map) {
map_ = MakeUnique<Map>();
if (!map_) {
return false;
}
map_ = std::move(map);
}
if (!map_->put(ssc, std::move(str))) {
if (!map_->put(ssc, std::move(data))) {
return false;
}
@ -1696,7 +1663,7 @@ UncompressedSourceCache::purge()
}
}
map_.reset();
map_ = nullptr;
}
size_t
@ -1712,29 +1679,32 @@ UncompressedSourceCache::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
return n;
}
const char16_t*
template<typename CharT>
const CharT*
ScriptSource::chunkChars(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& holder,
size_t chunk)
{
const Compressed& c = data.as<Compressed>();
const Compressed<CharT>& c = data.as<Compressed<CharT>>();
ScriptSourceChunk ssc(this, chunk);
if (const char16_t* decompressed = cx->caches().uncompressedSourceCache.lookup(ssc, holder)) {
if (const CharT* decompressed = cx->caches().uncompressedSourceCache.lookup<CharT>(ssc, holder)) {
return decompressed;
}
size_t totalLengthInBytes = length() * sizeof(char16_t);
size_t totalLengthInBytes = length() * sizeof(CharT);
size_t chunkBytes = Compressor::chunkSize(totalLengthInBytes, chunk);
MOZ_ASSERT((chunkBytes % sizeof(char16_t)) == 0);
const size_t lengthWithNull = (chunkBytes / sizeof(char16_t)) + 1;
UniqueTwoByteChars decompressed(js_pod_malloc<char16_t>(lengthWithNull));
MOZ_ASSERT((chunkBytes % sizeof(CharT)) == 0);
const size_t lengthWithNull = (chunkBytes / sizeof(CharT)) + 1;
EntryChars<CharT> decompressed(js_pod_malloc<CharT>(lengthWithNull));
if (!decompressed) {
JS_ReportOutOfMemory(cx);
return nullptr;
}
if (!DecompressStringChunk((const unsigned char*) c.raw.chars(),
// Compression treats input and output memory as plain ol' bytes. These
// reinterpret_cast<>s accord exactly with that.
if (!DecompressStringChunk(reinterpret_cast<const unsigned char*>(c.raw.chars()),
chunk,
reinterpret_cast<unsigned char*>(decompressed.get()),
chunkBytes))
@ -1743,67 +1713,58 @@ ScriptSource::chunkChars(JSContext* cx, UncompressedSourceCache::AutoHoldEntry&
return nullptr;
}
decompressed[lengthWithNull - 1] = '\0';
decompressed[lengthWithNull - 1] = CharT('\0');
const char16_t* ret = decompressed.get();
if (!cx->caches().uncompressedSourceCache.put(ssc, std::move(decompressed), holder)) {
const CharT* ret = decompressed.get();
if (!cx->caches().uncompressedSourceCache.put(ssc, ToSourceData(std::move(decompressed)),
holder))
{
JS_ReportOutOfMemory(cx);
return nullptr;
}
return ret;
}
ScriptSource::PinnedChars::PinnedChars(JSContext* cx, ScriptSource* source,
UncompressedSourceCache::AutoHoldEntry& holder,
size_t begin, size_t len)
: stack_(nullptr),
prev_(nullptr),
source_(source)
template<typename CharT>
void
ScriptSource::movePendingCompressedSource()
{
chars_ = source->chars(cx, holder, begin, len);
if (chars_) {
stack_ = &source->pinnedCharsStack_;
prev_ = *stack_;
*stack_ = this;
if (pendingCompressed_.empty()) {
return;
}
Compressed<CharT>& pending = pendingCompressed_.ref<Compressed<CharT>>();
MOZ_ASSERT(!hasCompressedSource());
MOZ_ASSERT_IF(hasUncompressedSource(),
pending.uncompressedLength == length());
data = SourceType(std::move(pending));
pendingCompressed_.destroy();
}
ScriptSource::PinnedChars::~PinnedChars()
template<typename CharT>
ScriptSource::PinnedChars<CharT>::~PinnedChars()
{
if (chars_) {
MOZ_ASSERT(*stack_ == this);
*stack_ = prev_;
if (!prev_) {
source_->movePendingCompressedSource();
source_->movePendingCompressedSource<CharT>();
}
}
}
void
ScriptSource::movePendingCompressedSource()
{
if (!pendingCompressed_) {
return;
}
MOZ_ASSERT(data.is<Missing>() || data.is<Uncompressed>());
MOZ_ASSERT_IF(data.is<Uncompressed>(),
data.as<Uncompressed>().string.length() ==
pendingCompressed_->uncompressedLength);
data = SourceType(Compressed(std::move(pendingCompressed_->raw),
pendingCompressed_->uncompressedLength));
pendingCompressed_ = mozilla::Nothing();
}
const char16_t*
template<typename CharT>
const CharT*
ScriptSource::chars(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& holder,
size_t begin, size_t len)
{
MOZ_ASSERT(begin <= length());
MOZ_ASSERT(begin + len <= length());
if (data.is<Uncompressed>()) {
const char16_t* chars = data.as<Uncompressed>().string.chars();
if (data.is<Uncompressed<CharT>>()) {
const CharT* chars = data.as<Uncompressed<CharT>>().chars();
if (!chars) {
return nullptr;
}
@ -1814,24 +1775,25 @@ ScriptSource::chars(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& holde
MOZ_CRASH("ScriptSource::chars() on ScriptSource with SourceType = Missing");
}
MOZ_ASSERT(data.is<Compressed>());
MOZ_ASSERT(data.is<Compressed<CharT>>());
// Determine which chunk(s) we are interested in, and the offsets within
// these chunks.
size_t firstChunk, lastChunk;
size_t firstChunkOffset, lastChunkOffset;
MOZ_ASSERT(len > 0);
Compressor::toChunkOffset(begin * sizeof(char16_t), &firstChunk, &firstChunkOffset);
Compressor::toChunkOffset((begin + len - 1) * sizeof(char16_t), &lastChunk, &lastChunkOffset);
Compressor::toChunkOffset(begin * sizeof(CharT), &firstChunk, &firstChunkOffset);
Compressor::toChunkOffset((begin + len - 1) * sizeof(CharT), &lastChunk, &lastChunkOffset);
MOZ_ASSERT(firstChunkOffset % sizeof(char16_t) == 0);
size_t firstChar = firstChunkOffset / sizeof(char16_t);
MOZ_ASSERT(firstChunkOffset % sizeof(CharT) == 0);
size_t firstChar = firstChunkOffset / sizeof(CharT);
if (firstChunk == lastChunk) {
const char16_t* chars = chunkChars(cx, holder, firstChunk);
const CharT* chars = chunkChars<CharT>(cx, holder, firstChunk);
if (!chars) {
return nullptr;
}
return chars + firstChar;
}
@ -1842,29 +1804,29 @@ ScriptSource::chars(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& holde
MOZ_ASSERT(firstChunk < lastChunk);
size_t lengthWithNull = len + 1;
UniqueTwoByteChars decompressed(js_pod_malloc<char16_t>(lengthWithNull));
EntryChars<CharT> decompressed(js_pod_malloc<CharT>(lengthWithNull));
if (!decompressed) {
JS_ReportOutOfMemory(cx);
return nullptr;
}
size_t totalLengthInBytes = length() * sizeof(char16_t);
char16_t* cursor = decompressed.get();
size_t totalLengthInBytes = length() * sizeof(CharT);
CharT* cursor = decompressed.get();
for (size_t i = firstChunk; i <= lastChunk; i++) {
UncompressedSourceCache::AutoHoldEntry chunkHolder;
const char16_t* chars = chunkChars(cx, chunkHolder, i);
const CharT* chars = chunkChars<CharT>(cx, chunkHolder, i);
if (!chars) {
return nullptr;
}
size_t numChars = Compressor::chunkSize(totalLengthInBytes, i) / sizeof(char16_t);
size_t numChars = Compressor::chunkSize(totalLengthInBytes, i) / sizeof(CharT);
if (i == firstChunk) {
MOZ_ASSERT(firstChar < numChars);
chars += firstChar;
numChars -= firstChar;
} else if (i == lastChunk) {
size_t numCharsNew = lastChunkOffset / sizeof(char16_t) + 1;
size_t numCharsNew = lastChunkOffset / sizeof(CharT) + 1;
MOZ_ASSERT(numCharsNew <= numChars);
numChars = numCharsNew;
}
@ -1872,25 +1834,63 @@ ScriptSource::chars(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& holde
cursor += numChars;
}
*cursor++ = '\0';
MOZ_ASSERT(size_t(cursor - decompressed.get()) == lengthWithNull);
// XXX Bug 1499192: can we remove the null-termination? It's unclear if
// anyone uses chunk implicit null-termination, chunks can contain
// nulls anyway, and the extra character risks size-class goofs.
*cursor++ = CharT('\0');
MOZ_ASSERT(PointerRangeSize(decompressed.get(), cursor) == lengthWithNull);
// Transfer ownership to |holder|.
const char16_t* ret = decompressed.get();
const CharT* ret = decompressed.get();
holder.holdChars(std::move(decompressed));
return ret;
}
template<typename CharT>
ScriptSource::PinnedChars<CharT>::PinnedChars(JSContext* cx, ScriptSource* source,
UncompressedSourceCache::AutoHoldEntry& holder,
size_t begin, size_t len)
: PinnedCharsBase(source)
{
MOZ_ASSERT(source->hasSourceType<CharT>(),
"must pin chars of source's type");
chars_ = source->chars<CharT>(cx, holder, begin, len);
if (chars_) {
stack_ = &source->pinnedCharsStack_;
prev_ = *stack_;
*stack_ = this;
}
}
template class ScriptSource::PinnedChars<Utf8Unit>;
template class ScriptSource::PinnedChars<char16_t>;
JSFlatString*
ScriptSource::substring(JSContext* cx, size_t start, size_t stop)
{
MOZ_ASSERT(start <= stop);
size_t len = stop - start;
UncompressedSourceCache::AutoHoldEntry holder;
PinnedChars chars(cx, this, holder, start, len);
// UTF-8 source text.
if (hasSourceType<Utf8Unit>()) {
PinnedChars<Utf8Unit> chars(cx, this, holder, start, len);
if (!chars.get()) {
return nullptr;
}
char* str = SourceTypeTraits<Utf8Unit>::toString(chars.get());
return NewStringCopyUTF8N<CanGC>(cx, JS::UTF8Chars(str, len));
}
// UTF-16 source text.
PinnedChars<char16_t> chars(cx, this, holder, start, len);
if (!chars.get()) {
return nullptr;
}
return NewStringCopyN<CanGC>(cx, chars.get(), len);
}
@ -1898,12 +1898,31 @@ JSFlatString*
ScriptSource::substringDontDeflate(JSContext* cx, size_t start, size_t stop)
{
MOZ_ASSERT(start <= stop);
size_t len = stop - start;
UncompressedSourceCache::AutoHoldEntry holder;
PinnedChars chars(cx, this, holder, start, len);
// UTF-8 source text.
if (hasSourceType<Utf8Unit>()) {
PinnedChars<Utf8Unit> chars(cx, this, holder, start, len);
if (!chars.get()) {
return nullptr;
}
char* str = SourceTypeTraits<Utf8Unit>::toString(chars.get());
// There doesn't appear to be a non-deflating UTF-8 string creation
// function -- but then again, it's not entirely clear how current
// callers benefit from non-deflation.
return NewStringCopyUTF8N<CanGC>(cx, JS::UTF8Chars(str, len));
}
// UTF-16 source text.
PinnedChars<char16_t> chars(cx, this, holder, start, len);
if (!chars.get()) {
return nullptr;
}
return NewStringCopyNDontDeflate<CanGC>(cx, chars.get(), len);
}
@ -1911,9 +1930,15 @@ bool
ScriptSource::appendSubstring(JSContext* cx, StringBuffer& buf, size_t start, size_t stop)
{
MOZ_ASSERT(start <= stop);
size_t len = stop - start;
UncompressedSourceCache::AutoHoldEntry holder;
PinnedChars chars(cx, this, holder, start, len);
if (hasSourceType<Utf8Unit>()) {
MOZ_CRASH("for now");
return false;
} else {
PinnedChars<char16_t> chars(cx, this, holder, start, len);
if (!chars.get()) {
return false;
}
@ -1921,6 +1946,7 @@ ScriptSource::appendSubstring(JSContext* cx, StringBuffer& buf, size_t start, si
return false;
}
return buf.append(chars.get(), len);
}
}
JSFlatString*
@ -1933,16 +1959,28 @@ ScriptSource::functionBodyString(JSContext* cx)
return substring(cx, start, stop);
}
template<typename CharT>
void
ScriptSource::setSource(typename SourceTypeTraits<CharT>::SharedImmutableString uncompressed)
{
MOZ_ASSERT(data.is<Missing>());
data = SourceType(Uncompressed<CharT>(std::move(uncompressed)));
}
template<typename CharT>
MOZ_MUST_USE bool
ScriptSource::setSource(JSContext* cx, UniqueTwoByteChars&& source, size_t length)
ScriptSource::setSource(JSContext* cx, EntryChars<CharT>&& source, size_t length)
{
auto& cache = cx->zone()->runtimeFromAnyThread()->sharedImmutableStrings();
auto deduped = cache.getOrCreate(std::move(source), length);
auto uniqueChars = SourceTypeTraits<CharT>::toCacheable(std::move(source));
auto deduped = cache.getOrCreate(std::move(uniqueChars), length);
if (!deduped) {
ReportOutOfMemory(cx);
return false;
}
setSource(std::move(*deduped));
setSource<CharT>(std::move(*deduped));
return true;
}
@ -1985,17 +2023,11 @@ ScriptSource::binASTSource()
#endif /* JS_BUILD_BINAST */
void
ScriptSource::setSource(SharedImmutableTwoByteString&& string)
{
MOZ_ASSERT(data.is<Missing>());
data = SourceType(Uncompressed(std::move(string)));
}
bool
ScriptSource::tryCompressOffThread(JSContext* cx)
{
if (!data.is<Uncompressed>()) {
if (!hasUncompressedSource()) {
// This excludes already-compressed, missing, and BinAST source.
return true;
}
@ -2039,32 +2071,37 @@ ScriptSource::tryCompressOffThread(JSContext* cx)
return EnqueueOffThreadCompression(cx, std::move(task));
}
template<typename CharT>
void
ScriptSource::setCompressedSource(SharedImmutableString raw, size_t uncompressedLength)
{
MOZ_ASSERT(data.is<Missing>() || hasUncompressedSource());
MOZ_ASSERT_IF(hasUncompressedSource(), length() == uncompressedLength);
if (pinnedCharsStack_) {
MOZ_ASSERT(pendingCompressed_.empty());
pendingCompressed_.construct<Compressed<CharT>>(std::move(raw), uncompressedLength);
} else {
data = SourceType(Compressed<CharT>(std::move(raw), uncompressedLength));
}
}
template<typename CharT>
MOZ_MUST_USE bool
ScriptSource::setCompressedSource(JSContext* cx, UniqueChars&& raw, size_t rawLength,
ScriptSource::setCompressedSource(JSContext* cx, UniqueChars&& compressed, size_t rawLength,
size_t sourceLength)
{
MOZ_ASSERT(raw);
MOZ_ASSERT(compressed);
auto& cache = cx->zone()->runtimeFromAnyThread()->sharedImmutableStrings();
auto deduped = cache.getOrCreate(std::move(raw), rawLength);
auto deduped = cache.getOrCreate(std::move(compressed), rawLength);
if (!deduped) {
ReportOutOfMemory(cx);
return false;
}
setCompressedSource(std::move(*deduped), sourceLength);
return true;
}
void
ScriptSource::setCompressedSource(SharedImmutableString&& raw, size_t uncompressedLength)
{
MOZ_ASSERT(data.is<Missing>() || data.is<Uncompressed>());
MOZ_ASSERT_IF(data.is<Uncompressed>(),
data.as<Uncompressed>().string.length() == uncompressedLength);
if (pinnedCharsStack_) {
pendingCompressed_ = mozilla::Some(Compressed(std::move(raw), uncompressedLength));
} else {
data = SourceType(Compressed(std::move(raw), uncompressedLength));
}
setCompressedSource<CharT>(std::move(*deduped), sourceLength);
return true;
}
bool
@ -2074,7 +2111,7 @@ ScriptSource::setSourceCopy(JSContext* cx, SourceBufferHolder& srcBuf)
JSRuntime* runtime = cx->zone()->runtimeFromAnyThread();
auto& cache = runtime->sharedImmutableStrings();
auto deduped = cache.getOrCreate(srcBuf.get(), srcBuf.length(), [&]() {
auto deduped = cache.getOrCreate(srcBuf.get(), srcBuf.length(), [&srcBuf]() {
return srcBuf.ownsChars()
? UniqueTwoByteChars(srcBuf.take())
: DuplicateString(srcBuf.get(), srcBuf.length());
@ -2083,8 +2120,8 @@ ScriptSource::setSourceCopy(JSContext* cx, SourceBufferHolder& srcBuf)
ReportOutOfMemory(cx);
return false;
}
setSource(std::move(*deduped));
setSource<char16_t>(std::move(*deduped));
return true;
}
@ -2114,28 +2151,24 @@ reallocUniquePtr(UniqueChars& unique, size_t size)
return true;
}
template<typename CharT>
void
SourceCompressionTask::work()
SourceCompressionTask::workEncodingSpecific()
{
if (shouldCancel()) {
return;
}
ScriptSource* source = sourceHolder_.get();
MOZ_ASSERT(source->data.is<ScriptSource::Uncompressed>());
MOZ_ASSERT(source->data.is<ScriptSource::Uncompressed<CharT>>());
// Try to keep the maximum memory usage down by only allocating half the
// size of the string, first.
size_t inputBytes = source->length() * sizeof(char16_t);
size_t inputBytes = source->length() * sizeof(CharT);
size_t firstSize = inputBytes / 2;
UniqueChars compressed(js_pod_malloc<char>(firstSize));
if (!compressed) {
return;
}
const char16_t* chars = source->data.as<ScriptSource::Uncompressed>().string.chars();
Compressor comp(reinterpret_cast<const unsigned char*>(chars),
inputBytes);
const CharT* chars = source->data.as<ScriptSource::Uncompressed<CharT>>().chars();
Compressor comp(reinterpret_cast<const unsigned char*>(chars), inputBytes);
if (!comp.init()) {
return;
}
@ -2192,12 +2225,58 @@ SourceCompressionTask::work()
resultString_ = strings.getOrCreate(std::move(compressed), totalBytes);
}
struct SourceCompressionTask::PerformTaskWork
{
SourceCompressionTask* const task_;
explicit PerformTaskWork(SourceCompressionTask* task)
: task_(task)
{}
template<typename CharT>
void match(const ScriptSource::Uncompressed<CharT>&) {
task_->workEncodingSpecific<CharT>();
}
template<typename T>
void match (const T&) {
MOZ_CRASH("why are we compressing missing, already-compressed, or "
"BinAST source?");
}
};
void
ScriptSource::performTaskWork(SourceCompressionTask* task)
{
MOZ_ASSERT(hasUncompressedSource());
data.match(SourceCompressionTask::PerformTaskWork(task));
}
void
SourceCompressionTask::work()
{
if (shouldCancel()) {
return;
}
ScriptSource* source = sourceHolder_.get();
MOZ_ASSERT(source->hasUncompressedSource());
source->performTaskWork(this);
}
void
ScriptSource::setCompressedSourceFromTask(SharedImmutableString compressed)
{
data.match(SetCompressedSourceFromTask(this, compressed));
}
void
SourceCompressionTask::complete()
{
if (!shouldCancel() && resultString_) {
if (!shouldCancel() && resultString_.isSome()) {
ScriptSource* source = sourceHolder_.get();
source->setCompressedSource(std::move(*resultString_), source->length());
source->setCompressedSourceFromTask(std::move(*resultString_));
}
}
@ -2286,52 +2365,106 @@ ScriptSource::xdrFinalizeEncoder(JS::TranscodeBuffer& buffer)
return res.isOk();
}
template<typename CharT>
struct SourceDecoder
{
XDRState<XDR_DECODE>* const xdr_;
ScriptSource* const scriptSource_;
const uint32_t uncompressedLength_;
public:
SourceDecoder(XDRState<XDR_DECODE>* xdr, ScriptSource* scriptSource,
uint32_t uncompressedLength)
: xdr_(xdr),
scriptSource_(scriptSource),
uncompressedLength_(uncompressedLength)
{}
XDRResult decode() {
auto sourceChars =
xdr_->cx()->make_pod_array<CharT>(Max<size_t>(uncompressedLength_, 1));
if (!sourceChars) {
return xdr_->fail(JS::TranscodeResult_Throw);
}
MOZ_TRY(xdr_->codeChars(sourceChars.get(), uncompressedLength_));
if (!scriptSource_->setSource(xdr_->cx(), std::move(sourceChars),
uncompressedLength_))
{
return xdr_->fail(JS::TranscodeResult_Throw);
}
return Ok();
}
};
namespace js {
template<>
XDRResult
ScriptSource::xdrUncompressedSource<XDR_DECODE>(XDRState<XDR_DECODE>* xdr,
uint8_t sourceCharSize,
uint32_t uncompressedLength)
{
MOZ_ASSERT(sourceCharSize == 1 || sourceCharSize == 2);
if (sourceCharSize == 1) {
SourceDecoder<Utf8Unit> decoder(xdr, this, uncompressedLength);
return decoder.decode();
}
SourceDecoder<char16_t> decoder(xdr, this, uncompressedLength);
return decoder.decode();
}
} // namespace js
template<typename CharT>
struct SourceEncoder
{
XDRState<XDR_ENCODE>* const xdr_;
ScriptSource* const source_;
const uint32_t uncompressedLength_;
SourceEncoder(XDRState<XDR_ENCODE>* xdr, ScriptSource* source, uint32_t uncompressedLength)
: xdr_(xdr),
source_(source),
uncompressedLength_(uncompressedLength)
{}
XDRResult encode() {
CharT* sourceChars = const_cast<CharT*>(source_->uncompressedData<CharT>());
return xdr_->codeChars(sourceChars, uncompressedLength_);
}
};
namespace js {
template<>
XDRResult
ScriptSource::xdrUncompressedSource<XDR_ENCODE>(XDRState<XDR_ENCODE>* xdr,
uint8_t sourceCharSize,
uint32_t uncompressedLength)
{
MOZ_ASSERT(sourceCharSize == 1 || sourceCharSize == 2);
if (sourceCharSize == 1) {
SourceEncoder<Utf8Unit> encoder(xdr, this, uncompressedLength);
return encoder.encode();
}
SourceEncoder<char16_t> encoder(xdr, this, uncompressedLength);
return encoder.encode();
}
} // namespace js
template<XDRMode mode>
XDRResult
ScriptSource::performXDR(XDRState<mode>* xdr)
{
struct CompressedLengthMatcher
{
size_t match(Uncompressed&) {
// Return 0 for uncompressed source so that |if (compressedLength)|
// can be used to distinguish compressed and uncompressed source.
return 0;
}
size_t match(Compressed& c) {
return c.raw.length();
}
size_t match(BinAST&) {
return 0;
}
size_t match(Missing&) {
MOZ_CRASH("Missing source data in ScriptSource::performXDR");
return 0;
}
};
struct RawDataMatcher
{
void* match(Uncompressed& u) {
return (void*) u.string.chars();
}
void* match(Compressed& c) {
return (void*) c.raw.chars();
}
void* match(BinAST& b) {
return (void*) b.string.chars();
}
void* match(Missing&) {
MOZ_CRASH("Missing source data in ScriptSource::performXDR");
return nullptr;
}
};
uint8_t hasSource = hasSourceText();
MOZ_TRY(xdr->codeUint8(&hasSource));
@ -2349,16 +2482,29 @@ ScriptSource::performXDR(XDRState<mode>* xdr)
}
MOZ_TRY(xdr->codeUint32(&uncompressedLength));
// A compressed length of 0 indicates source is uncompressed.
// A compressed length of 0 indicates source is uncompressed (or is
// BinAST if |hasBinSource|).
uint32_t compressedLength;
if (mode == XDR_ENCODE) {
CompressedLengthMatcher m;
compressedLength = data.match(m);
compressedLength = compressedLengthOrZero();
}
MOZ_TRY(xdr->codeUint32(&compressedLength));
if (mode == XDR_DECODE) {
uint8_t srcCharSize;
if (mode == XDR_ENCODE) {
srcCharSize = sourceCharSize();
}
MOZ_TRY(xdr->codeUint8(&srcCharSize));
if (srcCharSize != 1 && srcCharSize != 2) {
// Fail in debug, but only soft-fail in release, if the source-char
// size is invalid.
MOZ_ASSERT_UNREACHABLE("bad XDR source chars size");
return xdr->fail(JS::TranscodeResult_Failure_BadDecode);
}
if (hasBinSource) {
if (mode == XDR_DECODE) {
#if defined(JS_BUILD_BINAST)
auto bytes =
xdr->cx()->template make_pod_array<char>(Max<size_t>(uncompressedLength, 1));
@ -2374,43 +2520,35 @@ ScriptSource::performXDR(XDRState<mode>* xdr)
MOZ_ASSERT(mode != XDR_ENCODE);
return xdr->fail(JS::TranscodeResult_Throw);
#endif /* JS_BUILD_BINAST */
} else {
void* bytes = binASTData();
MOZ_TRY(xdr->codeBytes(bytes, uncompressedLength));
}
} else if (compressedLength) {
auto bytes =
xdr->cx()->template make_pod_array<char>(Max<size_t>(compressedLength, 1));
if (mode == XDR_DECODE) {
// Compressed data is always single-byte chars.
auto bytes = xdr->cx()->template make_pod_array<char>(compressedLength);
if (!bytes) {
return xdr->fail(JS::TranscodeResult_Throw);
}
MOZ_TRY(xdr->codeBytes(bytes.get(), compressedLength));
if (!setCompressedSource(xdr->cx(), std::move(bytes), compressedLength,
uncompressedLength))
if (!(srcCharSize == 1
? setCompressedSource<Utf8Unit>(xdr->cx(), std::move(bytes),
compressedLength, uncompressedLength)
: setCompressedSource<char16_t>(xdr->cx(), std::move(bytes),
compressedLength, uncompressedLength)))
{
return xdr->fail(JS::TranscodeResult_Throw);
}
} else {
auto sourceChars =
xdr->cx()->template make_pod_array<char16_t>(Max<size_t>(uncompressedLength,
1));
if (!sourceChars) {
return xdr->fail(JS::TranscodeResult_Throw);
}
MOZ_TRY(xdr->codeChars(sourceChars.get(), uncompressedLength));
if (!setSource(xdr->cx(), std::move(sourceChars), uncompressedLength)) {
return xdr->fail(JS::TranscodeResult_Throw);
}
}
} else {
if (hasBinSource) {
void* bytes = data.match(RawDataMatcher());
MOZ_TRY(xdr->codeBytes(bytes, uncompressedLength));
} else if (compressedLength) {
void* bytes = data.match(RawDataMatcher());
void* bytes = srcCharSize == 1
? compressedData<Utf8Unit>()
: compressedData<char16_t>();
MOZ_TRY(xdr->codeBytes(bytes, compressedLength));
} else {
char16_t* sourceChars = static_cast<char16_t*>(data.match(RawDataMatcher()));
MOZ_TRY(xdr->codeChars(sourceChars, uncompressedLength));
}
} else {
MOZ_TRY(xdrUncompressedSource(xdr, srcCharSize, uncompressedLength));
}
uint8_t hasMetadata = !!binASTMetadata_;
@ -2551,12 +2689,24 @@ ScriptSource::performXDR(XDRState<mode>* xdr)
mozilla::recordreplay::IsRecordingOrReplaying())
{
UncompressedSourceCache::AutoHoldEntry holder;
ScriptSource::PinnedChars chars(xdr->cx(), this, holder, 0, length());
if (hasSourceType<Utf8Unit>()) {
// UTF-8 source text.
ScriptSource::PinnedChars<Utf8Unit> chars(xdr->cx(), this, holder, 0, length());
if (!chars.get()) {
return xdr->fail(JS::TranscodeResult_Throw);
}
mozilla::recordreplay::NoteContentParse(this, filename(), "application/javascript",
mozilla::recordreplay::NoteContentParse8(this, filename(), "application/javascript",
chars.get(), length());
} else {
// UTF-16 source text.
ScriptSource::PinnedChars<char16_t> chars(xdr->cx(), this, holder, 0, length());
if (!chars.get()) {
return xdr->fail(JS::TranscodeResult_Throw);
}
mozilla::recordreplay::NoteContentParse16(this, filename(), "application/javascript",
chars.get(), length());
}
}
}

View File

@ -12,10 +12,16 @@
#include "mozilla/ArrayUtils.h"
#include "mozilla/Atomics.h"
#include "mozilla/Maybe.h"
#include "mozilla/MaybeOneOf.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Span.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/Utf8.h"
#include "mozilla/Variant.h"
#include <type_traits> // std::is_same
#include <utility> // std::move
#include "jstypes.h"
#include "frontend/BinSourceRuntimeSupport.h"
@ -26,6 +32,7 @@
#include "js/CompileOptions.h"
#include "js/UbiNode.h"
#include "js/UniquePtr.h"
#include "js/Utility.h"
#include "vm/BytecodeUtil.h"
#include "vm/JSAtom.h"
#include "vm/NativeObject.h"
@ -295,17 +302,17 @@ class ScriptSource;
struct ScriptSourceChunk
{
ScriptSource* ss;
uint32_t chunk;
ScriptSource* ss = nullptr;
uint32_t chunk = 0;
ScriptSourceChunk() = default;
ScriptSourceChunk()
: ss(nullptr), chunk(0)
{}
ScriptSourceChunk(ScriptSource* ss, uint32_t chunk)
: ss(ss), chunk(chunk)
{
MOZ_ASSERT(valid());;
MOZ_ASSERT(valid());
}
bool valid() const { return ss != nullptr; }
bool operator==(const ScriptSourceChunk& other) const {
@ -325,40 +332,103 @@ struct ScriptSourceChunkHasher
}
};
template<typename CharT>
using EntryChars = mozilla::UniquePtr<CharT[], JS::FreePolicy>;
// The uncompressed source cache contains *either* UTF-8 source data *or*
// UTF-16 source data. ScriptSourceChunk implies a ScriptSource that
// contains either UTF-8 data or UTF-16 data, so the nature of the key to
// Map below indicates how each SourceData ought to be interpreted.
using SourceData = mozilla::UniquePtr<void, JS::FreePolicy>;
template<typename CharT>
inline SourceData
ToSourceData(EntryChars<CharT> chars)
{
static_assert(std::is_same<SourceData::DeleterType,
typename EntryChars<CharT>::DeleterType>::value,
"EntryChars and SourceData must share the same deleter "
"type, that need not know the type of the data being freed, "
"for the upcast below to be safe");
return SourceData(chars.release());
}
class UncompressedSourceCache
{
typedef HashMap<ScriptSourceChunk,
UniqueTwoByteChars,
using Map = HashMap<ScriptSourceChunk,
SourceData,
ScriptSourceChunkHasher,
SystemAllocPolicy> Map;
SystemAllocPolicy>;
public:
// Hold an entry in the source data cache and prevent it from being purged on GC.
class AutoHoldEntry
{
UncompressedSourceCache* cache_;
ScriptSourceChunk sourceChunk_;
UniqueTwoByteChars charsToFree_;
UncompressedSourceCache* cache_ = nullptr;
ScriptSourceChunk sourceChunk_ = {};
SourceData data_ = nullptr;
public:
explicit AutoHoldEntry();
~AutoHoldEntry();
void holdChars(UniqueTwoByteChars chars);
explicit AutoHoldEntry() = default;
~AutoHoldEntry() {
if (cache_) {
MOZ_ASSERT(sourceChunk_.valid());
cache_->releaseEntry(*this);
}
}
template<typename CharT>
void holdChars(EntryChars<CharT> chars) {
MOZ_ASSERT(!cache_);
MOZ_ASSERT(!sourceChunk_.valid());
MOZ_ASSERT(!data_);
data_ = ToSourceData(std::move(chars));
}
private:
void holdEntry(UncompressedSourceCache* cache, const ScriptSourceChunk& sourceChunk);
void deferDelete(UniqueTwoByteChars chars);
void holdEntry(UncompressedSourceCache* cache, const ScriptSourceChunk& sourceChunk) {
// Initialise the holder for a specific cache and script source.
// This will hold on to the cached source chars in the event that
// the cache is purged.
MOZ_ASSERT(!cache_);
MOZ_ASSERT(!sourceChunk_.valid());
MOZ_ASSERT(!data_);
cache_ = cache;
sourceChunk_ = sourceChunk;
}
void deferDelete(SourceData data) {
// Take ownership of source chars now the cache is being purged. Remove our
// reference to the ScriptSource which might soon be destroyed.
MOZ_ASSERT(cache_);
MOZ_ASSERT(sourceChunk_.valid());
MOZ_ASSERT(!data_);
cache_ = nullptr;
sourceChunk_ = ScriptSourceChunk();
data_ = std::move(data);
}
const ScriptSourceChunk& sourceChunk() const { return sourceChunk_; }
friend class UncompressedSourceCache;
};
private:
UniquePtr<Map> map_;
AutoHoldEntry* holder_;
UniquePtr<Map> map_ = nullptr;
AutoHoldEntry* holder_ = nullptr;
public:
UncompressedSourceCache() : holder_(nullptr) {}
UncompressedSourceCache() = default;
const char16_t* lookup(const ScriptSourceChunk& ssc, AutoHoldEntry& asp);
bool put(const ScriptSourceChunk& ssc, UniqueTwoByteChars chars, AutoHoldEntry& asp);
template<typename CharT>
const CharT* lookup(const ScriptSourceChunk& ssc, AutoHoldEntry& asp);
bool put(const ScriptSourceChunk& ssc, SourceData data, AutoHoldEntry& asp);
void purge();
@ -369,23 +439,79 @@ class UncompressedSourceCache
void releaseEntry(AutoHoldEntry& holder);
};
template<typename CharT>
struct SourceTypeTraits;
template<>
struct SourceTypeTraits<mozilla::Utf8Unit>
{
using SharedImmutableString = js::SharedImmutableString;
static const mozilla::Utf8Unit* chars(const SharedImmutableString& string) {
// Casting |char| data to |Utf8Unit| is safe because |Utf8Unit|
// contains a |char|. See the long comment in |Utf8Unit|'s definition.
return reinterpret_cast<const mozilla::Utf8Unit*>(string.chars());
}
static char* toString(const mozilla::Utf8Unit* units) {
auto asUnsigned = const_cast<unsigned char*>(mozilla::Utf8AsUnsignedChars(units));
return reinterpret_cast<char*>(asUnsigned);
}
static UniqueChars toCacheable(EntryChars<mozilla::Utf8Unit> str) {
// The cache only stores strings of |char| or |char16_t|, and right now
// it seems best not to gunk up the cache with |Utf8Unit| too. So
// cache |Utf8Unit| strings by interpreting them as |char| strings.
char* chars = toString(str.release());
return UniqueChars(chars);
}
};
template<>
struct SourceTypeTraits<char16_t>
{
using SharedImmutableString = js::SharedImmutableTwoByteString;
static const char16_t* chars(const SharedImmutableString& string) {
return string.chars();
}
static char16_t* toString(char16_t* units) {
return units;
}
static UniqueTwoByteChars toCacheable(EntryChars<char16_t> str) {
return UniqueTwoByteChars(std::move(str));
}
};
class ScriptSource
{
friend class SourceCompressionTask;
class PinnedCharsBase
{
protected:
PinnedCharsBase** stack_ = nullptr;
PinnedCharsBase* prev_ = nullptr;
ScriptSource* source_;
explicit PinnedCharsBase(ScriptSource* source)
: source_(source)
{}
};
public:
// Any users that wish to manipulate the char buffer of the ScriptSource
// needs to do so via PinnedChars for GC safety. A GC may compress
// ScriptSources. If the source were initially uncompressed, then any raw
// pointers to the char buffer would now point to the freed, uncompressed
// chars. This is analogous to Rooted.
class PinnedChars
template<typename CharT>
class PinnedChars : public PinnedCharsBase
{
PinnedChars** stack_;
PinnedChars* prev_;
ScriptSource* source_;
const char16_t* chars_;
const CharT* chars_;
public:
PinnedChars(JSContext* cx, ScriptSource* source,
@ -394,7 +520,7 @@ class ScriptSource
~PinnedChars();
const char16_t* get() const {
const CharT* get() const {
return chars_;
}
};
@ -408,26 +534,39 @@ class ScriptSource
// on the main thread.
// Indicate which field in the |data| union is active.
struct Missing { };
struct Uncompressed
template<typename CharT>
class Uncompressed
{
SharedImmutableTwoByteString string;
typename SourceTypeTraits<CharT>::SharedImmutableString string_;
explicit Uncompressed(SharedImmutableTwoByteString&& str)
: string(std::move(str))
{ }
public:
explicit Uncompressed(typename SourceTypeTraits<CharT>::SharedImmutableString str)
: string_(std::move(str))
{}
const CharT* chars() const {
return SourceTypeTraits<CharT>::chars(string_);
}
size_t length() const {
return string_.length();
}
};
template<typename CharT>
struct Compressed
{
// Single-byte compressed text, regardless whether the original text
// was single-byte or two-byte.
SharedImmutableString raw;
size_t uncompressedLength;
Compressed(SharedImmutableString&& raw, size_t uncompressedLength)
: raw(std::move(raw))
, uncompressedLength(uncompressedLength)
Compressed(SharedImmutableString raw, size_t uncompressedLength)
: raw(std::move(raw)),
uncompressedLength(uncompressedLength)
{ }
};
@ -439,14 +578,18 @@ class ScriptSource
{ }
};
using SourceType = mozilla::Variant<Missing, Uncompressed, Compressed, BinAST>;
using SourceType =
mozilla::Variant<Compressed<mozilla::Utf8Unit>, Uncompressed<mozilla::Utf8Unit>,
Compressed<char16_t>, Uncompressed<char16_t>,
Missing,
BinAST>;
SourceType data;
// If the GC attempts to call setCompressedSource with PinnedChars
// present, the first PinnedChars (that is, bottom of the stack) will set
// the compressed chars upon destruction.
PinnedChars* pinnedCharsStack_;
mozilla::Maybe<Compressed> pendingCompressed_;
PinnedCharsBase* pinnedCharsStack_;
mozilla::MaybeOneOf<Compressed<mozilla::Utf8Unit>, Compressed<char16_t>> pendingCompressed_;
// The filename of this script.
UniqueChars filename_;
@ -512,7 +655,8 @@ class ScriptSource
UniquePtr<frontend::BinASTSourceMetadata> binASTMetadata_;
const char16_t* chunkChars(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& holder,
template<typename CharT>
const CharT* chunkChars(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& holder,
size_t chunk);
// Return a string containing the chars starting at |begin| and ending at
@ -520,9 +664,11 @@ class ScriptSource
//
// Warning: this is *not* GC-safe! Any chars to be handed out should use
// PinnedChars. See comment below.
const char16_t* chars(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& asp,
template<typename CharT>
const CharT* chars(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& asp,
size_t begin, size_t len);
template<typename CharT>
void movePendingCompressedSource();
public:
@ -568,8 +714,6 @@ class ScriptSource
bool sourceRetrievable() const { return sourceRetrievable_; }
bool hasSourceText() const { return hasUncompressedSource() || hasCompressedSource(); }
bool hasBinASTSource() const { return data.is<BinAST>(); }
bool hasUncompressedSource() const { return data.is<Uncompressed>(); }
bool hasCompressedSource() const { return data.is<Compressed>(); }
void setBinASTSourceMetadata(frontend::BinASTSourceMetadata* metadata) {
MOZ_ASSERT(hasBinASTSource());
@ -580,15 +724,192 @@ class ScriptSource
return binASTMetadata_.get();
}
size_t length() const {
struct LengthMatcher
private:
struct UncompressedDataMatcher
{
size_t match(const Uncompressed& u) {
return u.string.length();
template<typename CharT>
const void* match(const Uncompressed<CharT>& u) {
return u.chars();
}
size_t match(const Compressed& c) {
return c.uncompressedLength;
template<typename T>
const void* match(const T&) {
MOZ_CRASH("attempting to access uncompressed data in a "
"ScriptSource not containing it");
return nullptr;
}
};
public:
template<typename CharT>
const CharT* uncompressedData() {
return static_cast<const CharT*>(data.match(UncompressedDataMatcher()));
}
private:
struct CompressedDataMatcher
{
template<typename CharT>
char* match(const Compressed<CharT>& c) {
return const_cast<char*>(c.raw.chars());
}
template<typename T>
char* match(const T&) {
MOZ_CRASH("attempting to access compressed data in a ScriptSource "
"not containing it");
return nullptr;
}
};
public:
template<typename CharT>
char* compressedData() {
return data.match(CompressedDataMatcher());
}
private:
struct BinASTDataMatcher
{
void* match(const BinAST& b) {
return const_cast<char*>(b.string.chars());
}
void notBinAST() {
MOZ_CRASH("ScriptSource isn't backed by BinAST data");
}
template<typename T>
void* match(const T&) {
notBinAST();
return nullptr;
}
};
public:
void* binASTData() {
return data.match(BinASTDataMatcher());
}
private:
struct HasUncompressedSource
{
template<typename CharT>
bool match(const Uncompressed<CharT>&) { return true; }
template<typename CharT>
bool match(const Compressed<CharT>&) { return false; }
bool match(const BinAST&) { return false; }
bool match(const Missing&) { return false; }
};
public:
bool hasUncompressedSource() const {
return data.match(HasUncompressedSource());
}
template<typename CharT>
bool uncompressedSourceIs() const {
MOZ_ASSERT(hasUncompressedSource());
return data.is<Uncompressed<CharT>>();
}
private:
struct HasCompressedSource
{
template<typename CharT>
bool match(const Compressed<CharT>&) { return true; }
template<typename CharT>
bool match(const Uncompressed<CharT>&) { return false; }
bool match(const BinAST&) { return false; }
bool match(const Missing&) { return false; }
};
public:
bool hasCompressedSource() const {
return data.match(HasCompressedSource());
}
template<typename CharT>
bool compressedSourceIs() const {
MOZ_ASSERT(hasCompressedSource());
return data.is<Compressed<CharT>>();
}
private:
template<typename CharT>
struct SourceTypeMatcher
{
template<template<typename C> class Data>
bool match(const Data<CharT>&) {
return true;
}
template<template<typename C> class Data, typename NotCharT>
bool match(const Data<NotCharT>&) {
return false;
}
bool match(const BinAST&) {
MOZ_CRASH("doesn't make sense to ask source type of BinAST data");
return false;
}
bool match(const Missing&) {
MOZ_CRASH("doesn't make sense to ask source type when missing");
return false;
}
};
public:
template<typename CharT>
bool hasSourceType() const {
return data.match(SourceTypeMatcher<CharT>());
}
private:
struct SourceCharSizeMatcher
{
template<template<typename C> class Data, typename CharT>
uint8_t match(const Data<CharT>& data) {
static_assert(std::is_same<CharT, mozilla::Utf8Unit>::value ||
std::is_same<CharT, char16_t>::value,
"should only have UTF-8 or UTF-16 source char");
return sizeof(CharT);
}
uint8_t match(const BinAST&) {
MOZ_CRASH("BinAST source has no source-char size");
return 0;
}
uint8_t match(const Missing&) {
MOZ_CRASH("missing source has no source-char size");
return 0;
}
};
public:
uint8_t sourceCharSize() const {
return data.match(SourceCharSizeMatcher());
}
private:
struct UncompressedLengthMatcher
{
template<typename CharT>
size_t match(const Uncompressed<CharT>& u) {
return u.length();
}
template<typename CharT>
size_t match(const Compressed<CharT>& u) {
return u.uncompressedLength;
}
size_t match(const BinAST& b) {
@ -601,8 +922,39 @@ class ScriptSource
}
};
public:
size_t length() const {
MOZ_ASSERT(hasSourceText() || hasBinASTSource());
return data.match(LengthMatcher());
return data.match(UncompressedLengthMatcher());
}
private:
struct CompressedLengthOrZeroMatcher
{
template<typename CharT>
size_t match(const Uncompressed<CharT>&) {
return 0;
}
template<typename CharT>
size_t match(const Compressed<CharT>& c) {
return c.raw.length();
}
size_t match(const BinAST&) {
MOZ_CRASH("trying to get compressed length for BinAST data");
return 0;
}
size_t match(const Missing&) {
MOZ_CRASH("missing source data");
return 0;
}
};
public:
size_t compressedLengthOrZero() const {
return data.match(CompressedLengthOrZeroMatcher());
}
JSFlatString* substring(JSContext* cx, size_t start, size_t stop);
@ -618,18 +970,24 @@ class ScriptSource
void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
JS::ScriptSourceInfo* info) const;
template<typename CharT>
MOZ_MUST_USE bool setSource(JSContext* cx,
UniqueTwoByteChars&& source,
EntryChars<CharT>&& source,
size_t length);
void setSource(SharedImmutableTwoByteString&& string);
template<typename CharT>
void setSource(typename SourceTypeTraits<CharT>::SharedImmutableString uncompressed);
MOZ_MUST_USE bool tryCompressOffThread(JSContext* cx);
MOZ_MUST_USE bool setCompressedSource(JSContext* cx,
UniqueChars&& raw,
size_t rawLength,
// The CharT parameter determines which type of compressed source is
// recorded, but raw compressed source is always single-byte.
template<typename CharT>
void setCompressedSource(SharedImmutableString compressed, size_t sourceLength);
template<typename CharT>
MOZ_MUST_USE bool setCompressedSource(JSContext* cx, UniqueChars&& raw, size_t rawLength,
size_t sourceLength);
void setCompressedSource(SharedImmutableString&& raw, size_t sourceLength);
#if defined(JS_BUILD_BINAST)
@ -650,10 +1008,61 @@ class ScriptSource
#endif /* JS_BUILD_BINAST */
private:
void performTaskWork(SourceCompressionTask* task);
struct SetCompressedSourceFromTask
{
ScriptSource* const source_;
SharedImmutableString& compressed_;
SetCompressedSourceFromTask(ScriptSource* source, SharedImmutableString& compressed)
: source_(source),
compressed_(compressed)
{}
template<typename CharT>
void match(const Uncompressed<CharT>&) {
source_->setCompressedSource<CharT>(std::move(compressed_), source_->length());
}
template<typename CharT>
void match(const Compressed<CharT>&) {
MOZ_CRASH("can't set compressed source when source is already "
"compressed -- ScriptSource::tryCompressOffThread "
"shouldn't have queued up this task?");
}
void match(const BinAST&) {
MOZ_CRASH("doesn't make sense to set compressed source for BinAST "
"data");
}
void match(const Missing&) {
MOZ_CRASH("doesn't make sense to set compressed source for "
"missing source -- ScriptSource::tryCompressOffThread "
"shouldn't have queued up this task?");
}
};
void setCompressedSourceFromTask(SharedImmutableString compressed);
public:
// XDR handling
template <XDRMode mode>
MOZ_MUST_USE XDRResult performXDR(XDRState<mode>* xdr);
private:
// It'd be better to make this function take <XDRMode, CharT>, as both
// specializations of this function contain nested CharT-parametrized
// helper classes that do everything the function needs to do. But then
// we'd need template function partial specialization to hold XDRMode
// constant while varying CharT, so that idea's no dice.
template <XDRMode mode>
MOZ_MUST_USE XDRResult xdrUncompressedSource(XDRState<mode>* xdr, uint8_t sourceCharSize,
uint32_t uncompressedLength);
public:
MOZ_MUST_USE bool setFilename(JSContext* cx, const char* filename);
const char* introducerFilename() const {
return introducerFilename_ ? introducerFilename_.get() : filename_.get();

View File

@ -12,6 +12,7 @@
#include <cstring>
#include <new> // for placement new
#include <utility> // std::move
#include "builtin/String.h"
@ -29,9 +30,9 @@ class SharedImmutableString;
class SharedImmutableTwoByteString;
/**
* The `SharedImmutableStringsCache` allows for safely sharing and deduplicating
* immutable strings (either `const char*` or `const char16_t*`) between
* threads.
* The `SharedImmutableStringsCache` allows safely sharing and deduplicating
* immutable strings (either `const char*` [any encoding, not restricted to
* only Latin-1 or only UTF-8] or `const char16_t*`) between threads.
*
* The locking mechanism is dead-simple and coarse grained: a single lock guards
* all of the internal table itself, the table's entries, and the entries'

View File

@ -31,6 +31,7 @@
#include "jsutil.h"
#include "builtin/String.h"
#include "frontend/ParseNode.h"
#include "frontend/Parser.h"
#include "gc/Policy.h"
#include "js/MemoryMetrics.h"
@ -52,7 +53,6 @@
#include "wasm/WasmSerialize.h"
#include "wasm/WasmValidate.h"
#include "frontend/ParseNode-inl.h"
#include "vm/ArrayBufferObject-inl.h"
#include "vm/JSObject-inl.h"
@ -703,7 +703,7 @@ MaybeInitializer(ParseNode* pn)
static inline bool
IsUseOfName(ParseNode* pn, PropertyName* name)
{
return pn->isKind(ParseNodeKind::Name) && pn->name() == name;
return pn->isName(name);
}
static inline bool
@ -2250,7 +2250,7 @@ IsCallToGlobal(ModuleValidator& m, ParseNode* pn, const ModuleValidator::Global*
return false;
}
*global = m.lookupGlobal(callee->name());
*global = m.lookupGlobal(callee->as<NameNode>().name());
return !!*global;
}
@ -2771,11 +2771,12 @@ CheckArgument(ModuleValidator& m, ParseNode* arg, PropertyName** name)
return m.fail(arg, "argument is not a plain name");
}
if (!CheckIdentifier(m, arg, arg->name())) {
PropertyName* argName = arg->as<NameNode>().name();;
if (!CheckIdentifier(m, arg, argName)) {
return false;
}
*name = arg->name();
*name = argName;
return true;
}
@ -3008,7 +3009,7 @@ CheckNewArrayView(ModuleValidator& m, PropertyName* varName, ParseNode* newExpr)
return m.fail(ctorExpr, "expecting name of imported array view constructor");
}
PropertyName* globalName = ctorExpr->name();
PropertyName* globalName = ctorExpr->as<NameNode>().name();
const ModuleValidator::Global* global = m.lookupGlobal(globalName);
if (!global) {
return m.failName(ctorExpr, "%s not found in module global scope", globalName);
@ -3083,7 +3084,8 @@ CheckGlobalDotImport(ModuleValidator& m, PropertyName* varName, ParseNode* initN
return m.fail(base, "expected name of variable or parameter");
}
if (base->name() == m.globalArgumentName()) {
auto baseName = base->as<NameNode>().name();
if (baseName == m.globalArgumentName()) {
if (field == m.cx()->names().NaN) {
return m.addGlobalConstant(varName, GenericNaN(), field);
}
@ -3099,7 +3101,7 @@ CheckGlobalDotImport(ModuleValidator& m, PropertyName* varName, ParseNode* initN
return m.failName(initNode, "'%s' is not a standard constant or typed array name", field);
}
if (base->name() != m.importArgumentName()) {
if (baseName != m.importArgumentName()) {
return m.fail(base, "expected global or import name");
}
@ -3113,7 +3115,8 @@ CheckModuleGlobal(ModuleValidator& m, ParseNode* var, bool isConst)
return m.fail(var, "import variable is not a plain name");
}
if (!CheckModuleLevelName(m, var, var->name())) {
PropertyName* varName = var->as<NameNode>().name();
if (!CheckModuleLevelName(m, var, varName)) {
return false;
}
@ -3123,22 +3126,22 @@ CheckModuleGlobal(ModuleValidator& m, ParseNode* var, bool isConst)
}
if (IsNumericLiteral(m, initNode)) {
return CheckGlobalVariableInitConstant(m, var->name(), initNode, isConst);
return CheckGlobalVariableInitConstant(m, varName, initNode, isConst);
}
if (initNode->isKind(ParseNodeKind::BitOr) ||
initNode->isKind(ParseNodeKind::Pos) ||
initNode->isKind(ParseNodeKind::Call))
{
return CheckGlobalVariableInitImport(m, var->name(), initNode, isConst);
return CheckGlobalVariableInitImport(m, varName, initNode, isConst);
}
if (initNode->isKind(ParseNodeKind::New)) {
return CheckNewArrayView(m, var->name(), initNode);
return CheckNewArrayView(m, varName, initNode);
}
if (initNode->isKind(ParseNodeKind::Dot)) {
return CheckGlobalDotImport(m, var->name(), initNode);
return CheckGlobalDotImport(m, varName, initNode);
}
return m.fail(initNode, "unsupported import expression");
@ -3283,7 +3286,7 @@ static bool
IsLiteralOrConst(FunctionValidator& f, ParseNode* pn, NumLit* lit)
{
if (pn->isKind(ParseNodeKind::Name)) {
const ModuleValidator::Global* global = f.lookupGlobal(pn->name());
const ModuleValidator::Global* global = f.lookupGlobal(pn->as<NameNode>().name());
if (!global || global->which() != ModuleValidator::Global::ConstantLiteral) {
return false;
}
@ -3326,7 +3329,7 @@ CheckVariable(FunctionValidator& f, ParseNode* var, ValTypeVector* types, Vector
return f.fail(var, "local variable is not a plain name");
}
PropertyName* name = var->name();
PropertyName* name = var->as<NameNode>().name();
if (!CheckIdentifier(f.m(), var, name)) {
return false;
@ -3414,7 +3417,7 @@ CheckNumericLiteral(FunctionValidator& f, ParseNode* num, Type* type)
static bool
CheckVarRef(FunctionValidator& f, ParseNode* varRef, Type* type)
{
PropertyName* name = varRef->name();
PropertyName* name = varRef->as<NameNode>().name();
if (const FunctionValidator::Local* local = f.lookupLocal(name)) {
if (!f.encoder().writeOp(Op::GetLocal)) {
@ -3473,7 +3476,7 @@ CheckArrayAccess(FunctionValidator& f, ParseNode* viewName, ParseNode* indexExpr
return f.fail(viewName, "base of array access must be a typed array view name");
}
const ModuleValidator::Global* global = f.lookupGlobal(viewName->name());
const ModuleValidator::Global* global = f.lookupGlobal(viewName->as<NameNode>().name());
if (!global || !global->isAnyArrayView()) {
return f.fail(viewName, "base of array access must be a typed array view name");
}
@ -3706,7 +3709,7 @@ CheckStoreArray(FunctionValidator& f, ParseNode* lhs, ParseNode* rhs, Type* type
static bool
CheckAssignName(FunctionValidator& f, ParseNode* lhs, ParseNode* rhs, Type* type)
{
RootedPropertyName name(f.cx(), lhs->name());
RootedPropertyName name(f.cx(), lhs->as<NameNode>().name());
if (const FunctionValidator::Local* lhsVar = f.lookupLocal(name)) {
Type rhsType;
@ -4106,7 +4109,7 @@ CheckFuncPtrCall(FunctionValidator& f, ParseNode* callNode, Type ret, Type* type
return f.fail(tableNode, "expecting name of function-pointer array");
}
PropertyName* name = tableNode->name();
PropertyName* name = tableNode->as<NameNode>().name();
if (const ModuleValidator::Global* existing = f.lookupGlobal(name)) {
if (existing->which() != ModuleValidator::Global::Table) {
return f.failName(tableNode, "'%s' is not the name of a function-pointer array", name);
@ -4173,7 +4176,7 @@ CheckFFICall(FunctionValidator& f, ParseNode* callNode, unsigned ffiIndex, Type
{
MOZ_ASSERT(ret.isCanonical());
PropertyName* calleeName = CallCallee(callNode)->name();
PropertyName* calleeName = CallCallee(callNode)->as<NameNode>().name();
if (ret.isFloat()) {
return f.fail(callNode, "FFI calls can't return float");
@ -4466,7 +4469,7 @@ CheckCoercedCall(FunctionValidator& f, ParseNode* call, Type ret, Type* type)
return f.fail(callee, "unexpected callee expression type");
}
PropertyName* calleeName = callee->name();
PropertyName* calleeName = callee->as<NameNode>().name();
if (const ModuleValidator::Global* global = f.lookupGlobal(calleeName)) {
switch (global->which()) {
@ -4480,7 +4483,7 @@ CheckCoercedCall(FunctionValidator& f, ParseNode* call, Type ret, Type* type)
case ModuleValidator::Global::Table:
case ModuleValidator::Global::ArrayView:
case ModuleValidator::Global::ArrayViewCtor:
return f.failName(callee, "'%s' is not callable function", callee->name());
return f.failName(callee, "'%s' is not callable function", calleeName);
case ModuleValidator::Global::Function:
break;
}
@ -6014,7 +6017,7 @@ CheckFuncPtrTable(ModuleValidator& m, ParseNode* var)
return m.fail(elem, "function-pointer table's elements must be names of functions");
}
PropertyName* funcName = elem->name();
PropertyName* funcName = elem->as<NameNode>().name();
const ModuleValidator::Func* func = m.lookupFuncDef(funcName);
if (!func) {
return m.fail(elem, "function-pointer table's elements must be names of functions");
@ -6040,7 +6043,7 @@ CheckFuncPtrTable(ModuleValidator& m, ParseNode* var)
}
uint32_t tableIndex;
if (!CheckFuncPtrTableAgainstExisting(m, var, var->name(), std::move(copy), mask, &tableIndex)) {
if (!CheckFuncPtrTableAgainstExisting(m, var, var->as<NameNode>().name(), std::move(copy), mask, &tableIndex)) {
return false;
}
@ -6088,7 +6091,7 @@ CheckModuleExportFunction(ModuleValidator& m, ParseNode* pn, PropertyName* maybe
return m.fail(pn, "expected name of exported function");
}
PropertyName* funcName = pn->name();
PropertyName* funcName = pn->as<NameNode>().name();
const ModuleValidator::Func* func = m.lookupFuncDef(funcName);
if (!func) {
return m.failName(pn, "function '%s' not found", funcName);
@ -7002,7 +7005,7 @@ class ModuleCharsForStore : ModuleChars
CodeNode* functionNode = parser.pc->functionBox()->functionNode;
ParseNode* arg = FunctionFormalParametersList(functionNode, &numArgs);
for (unsigned i = 0; i < numArgs; i++, arg = arg->pn_next) {
UniqueChars name = StringToNewUTF8CharsZ(nullptr, *arg->name());
UniqueChars name = StringToNewUTF8CharsZ(nullptr, *arg->as<NameNode>().name());
if (!name || !funCtorArgs_.append(std::move(name))) {
return false;
}
@ -7095,7 +7098,7 @@ class ModuleCharsForLookup : ModuleChars
return false;
}
for (unsigned i = 0; i < funCtorArgs_.length(); i++, arg = arg->pn_next) {
UniqueChars name = StringToNewUTF8CharsZ(nullptr, *arg->name());
UniqueChars name = StringToNewUTF8CharsZ(nullptr, *arg->as<NameNode>().name());
if (!name || strcmp(funCtorArgs_[i].get(), name.get())) {
return false;
}

View File

@ -9,6 +9,7 @@
#include "js/GCAnnotations.h"
#include "mozilla/Atomics.h"
#include "mozilla/Casting.h"
#include "mozilla/Utf8.h"
#include <stdlib.h>
@ -76,7 +77,10 @@ namespace recordreplay {
Macro(BeginContentParse, \
(const void* aToken, const char* aURL, const char* aContentType), \
(aToken, aURL, aContentType)) \
Macro(AddContentParseData, \
Macro(AddContentParseData8, \
(const void* aToken, const mozilla::Utf8Unit* aUtf8Buffer, size_t aLength), \
(aToken, aUtf8Buffer, aLength)) \
Macro(AddContentParseData16, \
(const void* aToken, const char16_t* aBuffer, size_t aLength), \
(aToken, aBuffer, aLength)) \
Macro(EndContentParse, (const void* aToken), (aToken))

View File

@ -13,6 +13,7 @@
#include "mozilla/GuardObjects.h"
#include "mozilla/TemplateLib.h"
#include "mozilla/Types.h"
#include "mozilla/Utf8.h"
#include <functional>
#include <stdarg.h>
@ -353,8 +354,12 @@ MFBT_API bool DefineRecordReplayControlObject(JSContext* aCx, JSObject* aObj);
MFBT_API void BeginContentParse(const void* aToken,
const char* aURL, const char* aContentType);
// Add some parse data to an existing content parse.
MFBT_API void AddContentParseData(const void* aToken,
// Add some UTF-8 parse data to an existing content parse.
MFBT_API void AddContentParseData8(const void* aToken,
const Utf8Unit* aUtf8Buffer, size_t aLength);
// Add some UTF-16 parse data to an existing content parse.
MFBT_API void AddContentParseData16(const void* aToken,
const char16_t* aBuffer, size_t aLength);
// Mark a content parse as having completed.
@ -362,12 +367,23 @@ MFBT_API void EndContentParse(const void* aToken);
// Perform an entire content parse, when the entire URL is available at once.
static inline void
NoteContentParse(const void* aToken,
NoteContentParse8(const void* aToken,
const char* aURL, const char* aContentType,
const mozilla::Utf8Unit* aUtf8Buffer, size_t aLength)
{
BeginContentParse(aToken, aURL, aContentType);
AddContentParseData8(aToken, aUtf8Buffer, aLength);
EndContentParse(aToken);
}
// Perform an entire content parse, when the entire URL is available at once.
static inline void
NoteContentParse16(const void* aToken,
const char* aURL, const char* aContentType,
const char16_t* aBuffer, size_t aLength)
{
BeginContentParse(aToken, aURL, aContentType);
AddContentParseData(aToken, aBuffer, aLength);
AddContentParseData16(aToken, aBuffer, aLength);
EndContentParse(aToken);
}

View File

@ -6,15 +6,6 @@ ac_add_options --target=aarch64-linux-android
ac_add_options --with-branding=mobile/android/branding/nightly
export AR="$topsrcdir/clang/bin/llvm-ar"
export NM="$topsrcdir/clang/bin/llvm-nm"
export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
# Enable LTO if the NDK is available.
if [ -z "$NO_NDK" ]; then
ac_add_options --enable-lto
fi
export MOZILLA_OFFICIAL=1
export MOZ_TELEMETRY_REPORTING=1
export MOZ_ANDROID_POCKET=1

View File

@ -16,13 +16,4 @@ export MOZ_TELEMETRY_REPORTING=1
export MOZ_ANDROID_MMA=1
export MOZ_ANDROID_POCKET=1
export AR="$topsrcdir/clang/bin/llvm-ar"
export NM="$topsrcdir/clang/bin/llvm-nm"
export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
# Enable LTO if the NDK is available.
if [ -z "$NO_NDK" ]; then
ac_add_options --enable-lto
fi
. "$topsrcdir/mobile/android/config/mozconfigs/common.override"

View File

@ -14,13 +14,4 @@ export MOZILLA_OFFICIAL=1
export MOZ_TELEMETRY_REPORTING=1
export MOZ_ANDROID_POCKET=1
export AR="$topsrcdir/clang/bin/llvm-ar"
export NM="$topsrcdir/clang/bin/llvm-nm"
export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
# Enable LTO if the NDK is available.
if [ -z "$NO_NDK" ]; then
ac_add_options --enable-lto
fi
. "$topsrcdir/mobile/android/config/mozconfigs/common.override"

View File

@ -1745,11 +1745,17 @@ VARCACHE_PREF(
// Feature-Policy prefs
//---------------------------------------------------------------------------
#ifdef NIGHTLY_BUILD
# define PREF_VALUE true
#else
# define PREF_VALUE false
#endif
VARCACHE_PREF(
"dom.security.featurePolicy.enabled",
dom_security_featurePolicy_enabled,
bool, false
bool, PREF_VALUE
)
#undef PREF_VALUE
//---------------------------------------------------------------------------
// End of prefs

View File

@ -854,7 +854,7 @@ nsHtml5StreamParser::WriteStreamBytes(const uint8_t* aFromSegment,
Tie(result, read, written, hadErrors) =
mUnicodeDecoder->DecodeToUTF16(src, dst, false);
if (recordreplay::IsRecordingOrReplaying()) {
recordreplay::AddContentParseData(this, dst.data(), written);
recordreplay::AddContentParseData16(this, dst.data(), written);
}
if (hadErrors && !mHasHadErrors) {
mHasHadErrors = true;
@ -1113,7 +1113,7 @@ nsHtml5StreamParser::DoStopRequest()
Tie(result, read, written, hadErrors) =
mUnicodeDecoder->DecodeToUTF16(src, dst, true);
if (recordreplay::IsRecordingOrReplaying()) {
recordreplay::AddContentParseData(this, dst.data(), written);
recordreplay::AddContentParseData16(this, dst.data(), written);
}
if (hadErrors && !mHasHadErrors) {
mHasHadErrors = true;

View File

@ -762,41 +762,6 @@ win64-devedition-nightly/opt:
- win64-sccache
- win64-node
win32-mingw32/opt:
description: "Win32 MinGW Opt"
index:
product: firefox
job-name: win32-mingw32-opt
treeherder:
platform: windows-mingw32/all
symbol: WM32(Bo)
tier: 2
worker-type: aws-provisioner-v1/gecko-{level}-b-linux
worker:
docker-image: {in-tree: mingw32-build}
max-run-time: 7200
env:
PERFHERDER_EXTRA_OPTIONS: "opt"
run:
using: mozharness
actions: [build]
script: mozharness/scripts/fx_desktop_build.py
config:
- builds/releng_base_firefox.py
- builds/releng_base_windows_32_mingw_builds.py
need-xvfb: false
run-on-projects: []
toolchains:
- mingw32-rust
- linux64-upx
- linux64-wine
- win64-cbindgen
- linux64-sccache
- linux64-mingw32-gcc
- linux64-mingw32-nsis
- linux64-mingw32-fxc2
- linux64-node
win32-msvc/debug:
description: "Win32 MSVC Debug"
index:
@ -935,42 +900,6 @@ win64-msvc/opt:
- win64-sccache
- win64-node
win32-mingw32/debug:
description: "Win32 MinGW Debug"
index:
product: firefox
job-name: win32-mingw32-debug
treeherder:
platform: windows-mingw32/all
symbol: WM32(Bd)
tier: 2
worker-type: aws-provisioner-v1/gecko-{level}-b-linux
worker:
docker-image: {in-tree: mingw32-build}
max-run-time: 7200
env:
PERFHERDER_EXTRA_OPTIONS: "debug"
run:
using: mozharness
actions: [build]
script: mozharness/scripts/fx_desktop_build.py
config:
- builds/releng_base_firefox.py
- builds/releng_base_windows_32_mingw_builds.py
- builds/releng_sub_windows_configs/32_mingw_debug.py
need-xvfb: false
run-on-projects: []
toolchains:
- mingw32-rust
- linux64-upx
- linux64-wine
- linux64-cbindgen
- linux64-sccache
- linux64-mingw32-gcc
- linux64-mingw32-nsis
- linux64-mingw32-fxc2
- linux64-node
win32-mingwclang/opt:
description: "Win32 MinGW-Clang Opt"
index:

View File

@ -66,8 +66,6 @@ treeherder:
'TMW': 'Toolchain builds for Windows MinGW'
'TW32': 'Toolchain builds for Windows 32-bits'
'TW64': 'Toolchain builds for Windows 64-bits'
'WM32': 'MinGW builds for Windows 32-bits'
'WM64': 'MinGW builds for Windows 64-bits'
'WMC32': 'MinGW-Clang builds for Windows 32-bits'
'WMC64': 'MinGW-Clang builds for Windows 64-bits'
'Searchfox': 'Searchfox builds'

View File

@ -713,8 +713,13 @@ class ADBDevice(ADBCommand):
return
device = devices[0]
# Allow : in device serial if it matches a tcpip device serial.
re_device_serial_tcpip = re.compile(r'[^:]+:[0-9]+$')
def is_valid_serial(serial):
return ":" not in serial or serial.startswith("usb:")
return serial.startswith("usb:") or \
re_device_serial_tcpip.match(serial) is not None or \
":" not in serial
if isinstance(device, (str, unicode)):
# Treat this as a device serial

View File

@ -8,7 +8,7 @@ from __future__ import absolute_import
from setuptools import setup
PACKAGE_NAME = 'mozdevice'
PACKAGE_VERSION = '1.1.2'
PACKAGE_VERSION = '1.1.3'
deps = ['mozfile >= 1.0',
'mozlog >= 3.0',

View File

@ -1,3 +0,0 @@
config = {
'mozconfig_variant': 'mingw32-debug',
}

View File

@ -5,36 +5,24 @@
[XHR: file://example:1/ should throw]
expected: FAIL
[Location's href: file://example:1/ should throw]
expected: FAIL
[URL's href: file://example:test/ should throw]
expected: FAIL
[XHR: file://example:test/ should throw]
expected: FAIL
[Location's href: file://example:test/ should throw]
expected: FAIL
[URL's href: file://example%/ should throw]
expected: FAIL
[XHR: file://example%/ should throw]
expected: FAIL
[Location's href: file://example%/ should throw]
expected: FAIL
[URL's href: file://[example\]/ should throw]
expected: FAIL
[XHR: file://[example\]/ should throw]
expected: FAIL
[Location's href: file://[example\]/ should throw]
expected: FAIL
[Location's href: http://user:pass@/ should throw]
expected: FAIL

View File

@ -2,6 +2,3 @@
[Feature-Policy allow="wake-lock" allows same-origin relocation]
expected: FAIL
[Feature-Policy allow="wake-lock" disallows cross-origin relocation]
expected: FAIL

View File

@ -1,13 +1,11 @@
[usb-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html]
expected: TIMEOUT
[Feature-Policy allow="usb" allows same-origin relocation.]
expected: FAIL
[Feature-Policy allow="usb" disallows cross-origin relocation.]
expected: FAIL
[Feature-Policy allow="usb" allows workers in same-origin relocation.]
expected: FAIL
expected: TIMEOUT
[Feature-Policy allow="usb" disallows workers in cross-origin relocation.]
expected: FAIL
expected: TIMEOUT

View File

@ -1,4 +1,5 @@
[usb-allowed-by-feature-policy-attribute.https.sub.html]
expected: TIMEOUT
[Feature policy "usb" can be enabled in cross-origin iframes using "allowed" attribute.]
expected: FAIL
@ -9,10 +10,10 @@
expected: FAIL
[Feature policy "usb" can be enabled in a worker in same-origin iframe using allow="usb" attribute]
expected: FAIL
expected: TIMEOUT
[Feature policy "usb" can be enabled in a worker in cross-origin iframe using allow="usb" attribute]
expected: FAIL
expected: TIMEOUT
[Inherited header feature policy allows dedicated workers.]
expected: FAIL

View File

@ -576,7 +576,8 @@ struct ContentInfo
const void* mToken;
char* mURL;
char* mContentType;
InfallibleVector<char16_t> mContent;
InfallibleVector<char> mContent8;
InfallibleVector<char16_t> mContent16;
ContentInfo(const void* aToken, const char* aURL, const char* aContentType)
: mToken(aToken),
@ -588,7 +589,8 @@ struct ContentInfo
: mToken(aOther.mToken),
mURL(aOther.mURL),
mContentType(aOther.mContentType),
mContent(std::move(aOther.mContent))
mContent8(std::move(aOther.mContent8)),
mContent16(std::move(aOther.mContent16))
{
aOther.mURL = nullptr;
aOther.mContentType = nullptr;
@ -623,18 +625,37 @@ RecordReplayInterface_BeginContentParse(const void* aToken,
}
MOZ_EXPORT void
RecordReplayInterface_AddContentParseData(const void* aToken,
RecordReplayInterface_AddContentParseData8(const void* aToken,
const Utf8Unit* aUtf8Buffer, size_t aLength)
{
MOZ_RELEASE_ASSERT(IsRecordingOrReplaying());
MOZ_RELEASE_ASSERT(aToken);
RecordReplayAssert("AddContentParseData8ForRecordReplay %d", (int) aLength);
MonitorAutoLock lock(*child::gMonitor);
for (ContentInfo& info : gContent) {
if (info.mToken == aToken) {
info.mContent8.append(reinterpret_cast<const char*>(aUtf8Buffer), aLength);
return;
}
}
MOZ_CRASH("Unknown content parse token");
}
MOZ_EXPORT void
RecordReplayInterface_AddContentParseData16(const void* aToken,
const char16_t* aBuffer, size_t aLength)
{
MOZ_RELEASE_ASSERT(IsRecordingOrReplaying());
MOZ_RELEASE_ASSERT(aToken);
RecordReplayAssert("AddContentParseDataForRecordReplay %d", (int) aLength);
RecordReplayAssert("AddContentParseData16ForRecordReplay %d", (int) aLength);
MonitorAutoLock lock(*child::gMonitor);
for (ContentInfo& info : gContent) {
if (info.mToken == aToken) {
info.mContent.append(aBuffer, aLength);
info.mContent16.append(aBuffer, aLength);
return;
}
}
@ -667,9 +688,19 @@ FetchContent(JSContext* aCx, HandleString aURL,
for (ContentInfo& info : gContent) {
if (JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(aURL), info.mURL)) {
aContentType.set(JS_NewStringCopyZ(aCx, info.mContentType));
aContent.set(JS_NewUCStringCopyN(aCx, (const char16_t*) info.mContent.begin(),
info.mContent.length()));
return aContentType && aContent;
if (!aContentType) {
return false;
}
MOZ_ASSERT(info.mContent8.length() == 0 ||
info.mContent16.length() == 0,
"should have content data of only one type");
aContent.set(info.mContent8.length() > 0
? JS_NewStringCopyUTF8N(aCx, JS::UTF8Chars(info.mContent8.begin(),
info.mContent8.length()))
: JS_NewUCStringCopyN(aCx, info.mContent16.begin(), info.mContent16.length()));
return aContent != nullptr;
}
}
aContentType.set(JS_NewStringCopyZ(aCx, "text/plain"));

View File

@ -1099,7 +1099,10 @@ ProfileBuffer::StreamSamplesToJSON(SpliceableJSONWriter& aWriter, int aThreadId,
}
if (numFrames == 0) {
ERROR_AND_CONTINUE("expected one or more frame entries");
// It is possible to have empty stacks if native stackwalking is
// disabled. Skip samples with empty stacks. (See Bug 1497985).
// Thus, don't use ERROR_AND_CONTINUE, but just continue.
continue;
}
sample.mStack = aUniqueStacks.GetOrAddStackIndex(stack);