mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-17 23:35:34 +00:00
Merge inbound to mozilla-central. a=merge
This commit is contained in:
commit
3d989d097f
@ -61,92 +61,94 @@ 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();
|
||||
if (NS_WARN_IF(!ssm)) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Get security manager.
|
||||
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
|
||||
NS_ENSURE_STATE(ssm);
|
||||
// Check to see if URI is allowed.
|
||||
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;
|
||||
}
|
||||
|
||||
// Check to see if URI is allowed.
|
||||
nsresult rv = ssm->CheckLoadURIFromScript(cx, aURI);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
// Make the load's referrer reflect changes to the document's URI caused by
|
||||
// push/replaceState, if possible. First, get the document corresponding to
|
||||
// fp. If the document's original URI (i.e. its URI before
|
||||
// push/replaceState) matches the principal's URI, use the document's
|
||||
// current URI as the referrer. If they don't match, use the principal's
|
||||
// URI.
|
||||
//
|
||||
// The triggering principal for this load should be the principal of the
|
||||
// incumbent document (which matches where the referrer information is
|
||||
// coming from) when there is an incumbent document, and the subject
|
||||
// principal otherwise. Note that the URI in the triggering principal
|
||||
// may not match the referrer URI in various cases, notably including
|
||||
// the cases when the incumbent document's document URI was modified
|
||||
// after the document was loaded.
|
||||
|
||||
// Make the load's referrer reflect changes to the document's URI caused by
|
||||
// push/replaceState, if possible. First, get the document corresponding to
|
||||
// fp. If the document's original URI (i.e. its URI before
|
||||
// push/replaceState) matches the principal's URI, use the document's
|
||||
// current URI as the referrer. If they don't match, use the principal's
|
||||
// URI.
|
||||
//
|
||||
// The triggering principal for this load should be the principal of the
|
||||
// incumbent document (which matches where the referrer information is
|
||||
// coming from) when there is an incumbent document, and the subject
|
||||
// principal otherwise. Note that the URI in the triggering principal
|
||||
// may not match the referrer URI in various cases, notably including
|
||||
// the cases when the incumbent document's document URI was modified
|
||||
// after the document was loaded.
|
||||
nsCOMPtr<nsPIDOMWindowInner> incumbent =
|
||||
do_QueryInterface(mozilla::dom::GetIncumbentGlobal());
|
||||
nsCOMPtr<nsIDocument> doc = incumbent ? incumbent->GetDoc() : nullptr;
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> incumbent =
|
||||
do_QueryInterface(mozilla::dom::GetIncumbentGlobal());
|
||||
nsCOMPtr<nsIDocument> doc = incumbent ? incumbent->GetDoc() : nullptr;
|
||||
if (doc) {
|
||||
nsCOMPtr<nsIURI> docOriginalURI, docCurrentURI, principalURI;
|
||||
docOriginalURI = doc->GetOriginalURI();
|
||||
docCurrentURI = doc->GetDocumentURI();
|
||||
rv = doc->NodePrincipal()->GetURI(getter_AddRefs(principalURI));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aRv.Throw(rv);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (doc) {
|
||||
nsCOMPtr<nsIURI> docOriginalURI, docCurrentURI, principalURI;
|
||||
docOriginalURI = doc->GetOriginalURI();
|
||||
docCurrentURI = doc->GetDocumentURI();
|
||||
rv = doc->NodePrincipal()->GetURI(getter_AddRefs(principalURI));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
triggeringPrincipal = doc->NodePrincipal();
|
||||
referrerPolicy = doc->GetReferrerPolicy();
|
||||
|
||||
triggeringPrincipal = doc->NodePrincipal();
|
||||
referrerPolicy = doc->GetReferrerPolicy();
|
||||
|
||||
bool urisEqual = false;
|
||||
if (docOriginalURI && docCurrentURI && principalURI) {
|
||||
principalURI->Equals(docOriginalURI, &urisEqual);
|
||||
}
|
||||
if (urisEqual) {
|
||||
sourceURI = docCurrentURI;
|
||||
}
|
||||
else {
|
||||
// Use principalURI as long as it is not an NullPrincipalURI. We
|
||||
// could add a method such as GetReferrerURI to principals to make this
|
||||
// cleaner, but given that we need to start using Source Browsing
|
||||
// Context for referrer (see Bug 960639) this may be wasted effort at
|
||||
// this stage.
|
||||
if (principalURI) {
|
||||
bool isNullPrincipalScheme;
|
||||
rv = principalURI->SchemeIs(NS_NULLPRINCIPAL_SCHEME,
|
||||
&isNullPrincipalScheme);
|
||||
if (NS_SUCCEEDED(rv) && !isNullPrincipalScheme) {
|
||||
sourceURI = principalURI;
|
||||
}
|
||||
bool urisEqual = false;
|
||||
if (docOriginalURI && docCurrentURI && principalURI) {
|
||||
principalURI->Equals(docOriginalURI, &urisEqual);
|
||||
}
|
||||
if (urisEqual) {
|
||||
sourceURI = docCurrentURI;
|
||||
}
|
||||
else {
|
||||
// Use principalURI as long as it is not an NullPrincipalURI. We
|
||||
// could add a method such as GetReferrerURI to principals to make this
|
||||
// cleaner, but given that we need to start using Source Browsing
|
||||
// Context for referrer (see Bug 960639) this may be wasted effort at
|
||||
// this stage.
|
||||
if (principalURI) {
|
||||
bool isNullPrincipalScheme;
|
||||
rv = principalURI->SchemeIs(NS_NULLPRINCIPAL_SCHEME,
|
||||
&isNullPrincipalScheme);
|
||||
if (NS_SUCCEEDED(rv) && !isNullPrincipalScheme) {
|
||||
sourceURI = principalURI;
|
||||
}
|
||||
}
|
||||
}
|
||||
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,
|
||||
nsIWebNavigation::LOAD_FLAGS_NONE, true);
|
||||
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>
|
||||
|
@ -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;
|
||||
|
@ -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.")
|
||||
|
@ -2412,8 +2412,8 @@ ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest)
|
||||
|
||||
if (srcBuf) {
|
||||
if (recordreplay::IsRecordingOrReplaying()) {
|
||||
recordreplay::NoteContentParse(this, options.filename(), "application/javascript",
|
||||
srcBuf->get(), srcBuf->length());
|
||||
recordreplay::NoteContentParse16(this, options.filename(), "application/javascript",
|
||||
srcBuf->get(), srcBuf->length());
|
||||
}
|
||||
rv = exec.CompileAndExec(options, *srcBuf, &script);
|
||||
} else {
|
||||
|
@ -20,7 +20,7 @@ interface Location {
|
||||
[Throws, NeedsSubjectPrincipal]
|
||||
stringifier;
|
||||
|
||||
[Throws, CrossOriginWritable, GetterNeedsSubjectPrincipal]
|
||||
[Throws, CrossOriginWritable, NeedsSubjectPrincipal]
|
||||
attribute USVString href;
|
||||
[Throws, NeedsSubjectPrincipal]
|
||||
readonly attribute USVString origin;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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(¶m->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>();
|
||||
if (!expr->isKind(ParseNodeKind::Name)) {
|
||||
if (emitterMode == BytecodeEmitter::SelfHosting &&
|
||||
expr->isKind(ParseNodeKind::Call))
|
||||
{
|
||||
BinaryNode* callNode = &expr->as<BinaryNode>();
|
||||
ParseNode* calleeNode = callNode->left();
|
||||
if (calleeNode->getKind() == ParseNodeKind::Name &&
|
||||
calleeNode->name() == cx->names().allowContentIter)
|
||||
{
|
||||
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;
|
||||
|
@ -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.
|
||||
//
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
@ -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"
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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,19 +1784,36 @@ JSFunction::createScriptForLazilyInterpretedFunction(JSContext* cx, HandleFuncti
|
||||
|
||||
// Parse and compile the script from source.
|
||||
UncompressedSourceCache::AutoHoldEntry holder;
|
||||
ScriptSource::PinnedChars chars(cx, lazy->scriptSource(), holder,
|
||||
lazy->sourceStart(), lazyLength);
|
||||
if (!chars.get()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!frontend::CompileLazyFunction(cx, lazy, chars.get(), lazyLength)) {
|
||||
// The frontend shouldn't fail after linking the function and the
|
||||
// non-lazy script together.
|
||||
MOZ_ASSERT(fun->isInterpretedLazy());
|
||||
MOZ_ASSERT(fun->lazyScript() == lazy);
|
||||
MOZ_ASSERT(!lazy->hasScript());
|
||||
return false;
|
||||
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;
|
||||
}
|
||||
|
||||
if (!frontend::CompileLazyFunction(cx, lazy, chars.get(), lazyLength)) {
|
||||
// The frontend shouldn't fail after linking the function and the
|
||||
// non-lazy script together.
|
||||
MOZ_ASSERT(fun->isInterpretedLazy());
|
||||
MOZ_ASSERT(fun->lazyScript() == lazy);
|
||||
MOZ_ASSERT(!lazy->hasScript());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,16 +1930,23 @@ 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 (!chars.get()) {
|
||||
|
||||
if (hasSourceType<Utf8Unit>()) {
|
||||
MOZ_CRASH("for now");
|
||||
return false;
|
||||
} else {
|
||||
PinnedChars<char16_t> chars(cx, this, holder, start, len);
|
||||
if (!chars.get()) {
|
||||
return false;
|
||||
}
|
||||
if (len > SourceDeflateLimit && !buf.ensureTwoByteChars()) {
|
||||
return false;
|
||||
}
|
||||
return buf.append(chars.get(), len);
|
||||
}
|
||||
if (len > SourceDeflateLimit && !buf.ensureTwoByteChars()) {
|
||||
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) {
|
||||
if (hasBinSource) {
|
||||
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 if (compressedLength) {
|
||||
auto bytes =
|
||||
xdr->cx()->template make_pod_array<char>(Max<size_t>(compressedLength, 1));
|
||||
} else {
|
||||
void* bytes = binASTData();
|
||||
MOZ_TRY(xdr->codeBytes(bytes, uncompressedLength));
|
||||
}
|
||||
} else if (compressedLength) {
|
||||
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);
|
||||
}
|
||||
void* bytes = srcCharSize == 1
|
||||
? compressedData<Utf8Unit>()
|
||||
: compressedData<char16_t>();
|
||||
MOZ_TRY(xdr->codeBytes(bytes, compressedLength));
|
||||
}
|
||||
} else {
|
||||
if (hasBinSource) {
|
||||
void* bytes = data.match(RawDataMatcher());
|
||||
MOZ_TRY(xdr->codeBytes(bytes, uncompressedLength));
|
||||
} else if (compressedLength) {
|
||||
void* bytes = data.match(RawDataMatcher());
|
||||
MOZ_TRY(xdr->codeBytes(bytes, compressedLength));
|
||||
} else {
|
||||
char16_t* sourceChars = static_cast<char16_t*>(data.match(RawDataMatcher()));
|
||||
MOZ_TRY(xdr->codeChars(sourceChars, uncompressedLength));
|
||||
}
|
||||
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 (!chars.get()) {
|
||||
return xdr->fail(JS::TranscodeResult_Throw);
|
||||
|
||||
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::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());
|
||||
}
|
||||
mozilla::recordreplay::NoteContentParse(this, filename(), "application/javascript",
|
||||
chars.get(), length());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
ScriptSourceChunkHasher,
|
||||
SystemAllocPolicy> Map;
|
||||
using Map = HashMap<ScriptSourceChunk,
|
||||
SourceData,
|
||||
ScriptSourceChunkHasher,
|
||||
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,17 +655,20 @@ class ScriptSource
|
||||
|
||||
UniquePtr<frontend::BinASTSourceMetadata> binASTMetadata_;
|
||||
|
||||
const char16_t* chunkChars(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& holder,
|
||||
size_t chunk);
|
||||
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
|
||||
// |begin + len|.
|
||||
//
|
||||
// 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,
|
||||
size_t begin, size_t len);
|
||||
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,29 +724,237 @@ class ScriptSource
|
||||
return binASTMetadata_.get();
|
||||
}
|
||||
|
||||
private:
|
||||
struct UncompressedDataMatcher
|
||||
{
|
||||
template<typename CharT>
|
||||
const void* match(const Uncompressed<CharT>& u) {
|
||||
return u.chars();
|
||||
}
|
||||
|
||||
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) {
|
||||
return b.string.length();
|
||||
}
|
||||
|
||||
size_t match(const Missing& m) {
|
||||
MOZ_CRASH("ScriptSource::length on a missing source");
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
size_t length() const {
|
||||
struct LengthMatcher
|
||||
{
|
||||
size_t match(const Uncompressed& u) {
|
||||
return u.string.length();
|
||||
}
|
||||
|
||||
size_t match(const Compressed& c) {
|
||||
return c.uncompressedLength;
|
||||
}
|
||||
|
||||
size_t match(const BinAST& b) {
|
||||
return b.string.length();
|
||||
}
|
||||
|
||||
size_t match(const Missing& m) {
|
||||
MOZ_CRASH("ScriptSource::length on a missing source");
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
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();
|
||||
|
@ -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'
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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))
|
||||
|
@ -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,21 +354,36 @@ 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,
|
||||
const char16_t* aBuffer, size_t aLength);
|
||||
// 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.
|
||||
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,
|
||||
const char* aURL, const char* aContentType,
|
||||
const char16_t* aBuffer, size_t aLength)
|
||||
NoteContentParse8(const void* aToken,
|
||||
const char* aURL, const char* aContentType,
|
||||
const mozilla::Utf8Unit* aUtf8Buffer, size_t aLength)
|
||||
{
|
||||
BeginContentParse(aToken, aURL, aContentType);
|
||||
AddContentParseData(aToken, aBuffer, aLength);
|
||||
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);
|
||||
AddContentParseData16(aToken, aBuffer, aLength);
|
||||
EndContentParse(aToken);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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'
|
||||
|
@ -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
|
||||
|
@ -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',
|
||||
|
@ -1,3 +0,0 @@
|
||||
config = {
|
||||
'mozconfig_variant': 'mingw32-debug',
|
||||
}
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
const char16_t* aBuffer, size_t aLength)
|
||||
RecordReplayInterface_AddContentParseData8(const void* aToken,
|
||||
const Utf8Unit* aUtf8Buffer, size_t aLength)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(IsRecordingOrReplaying());
|
||||
MOZ_RELEASE_ASSERT(aToken);
|
||||
|
||||
RecordReplayAssert("AddContentParseDataForRecordReplay %d", (int) aLength);
|
||||
RecordReplayAssert("AddContentParseData8ForRecordReplay %d", (int) aLength);
|
||||
|
||||
MonitorAutoLock lock(*child::gMonitor);
|
||||
for (ContentInfo& info : gContent) {
|
||||
if (info.mToken == aToken) {
|
||||
info.mContent.append(aBuffer, aLength);
|
||||
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("AddContentParseData16ForRecordReplay %d", (int) aLength);
|
||||
|
||||
MonitorAutoLock lock(*child::gMonitor);
|
||||
for (ContentInfo& info : gContent) {
|
||||
if (info.mToken == aToken) {
|
||||
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"));
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user