Closed
Bug 728250
Opened 13 years ago
Closed 13 years ago
slimmer and faster JSPrincipals
Categories
(Core :: JavaScript Engine, defect)
Tracking
()
RESOLVED
FIXED
mozilla13
People
(Reporter: igor, Assigned: igor)
References
Details
Attachments
(2 files, 9 obsolete files)
135.13 KB,
patch
|
Details | Diff | Splinter Review | |
14.68 KB,
patch
|
bzbarsky
:
review+
|
Details | Diff | Splinter Review |
With a fresh profile during startup even with optimization from the bug 725576 we still read from the XDR cache about 900 principals. In all cases the principal is the system one, yet just to support that we do at least 5 indirect calls and one malloc/free pair per top-level scripts with a storage requirement of at least 20 bytes using a complex interaction between XDR and object input stream.
All this can be avoided if JS_XDRScript avoid any principal transcoding. Rather during decoding the caller should supply the necessary principals to XDR.
While looking at this I noticed that this pattern of too much indirection is rather widespread while working with JSPrincipals.
For example, the code assumes that any JSPrincipal that is stored in the script is an instance of nsJSPrincipals that contains a pointer to nsIPrincipal and uses simple static_cast<nsJSprincipal *>(jsPrincipal)->nsIPrincipalPtr. Yet to do the reverse action of translating the nsIPrincipal pointer into JSPrincipals* we use the virtual call that returns a strong reference to JSPrincipals* that must be dropped. This indirection and JSPRICIPALS_DROP can be avoided if nsJSprincipal simply subclasses both nsIPrincipal and JSPrincipals and any principal implementation then should subclass nsJSprincipal rather than storing its instance in one of its fields. Then one can use static_cast for any nsIPrincipal <-> JSPrincipal conversion.
Another unnecessary indirection is the implementation of JSSecurityCallbacks::findObjectPrincipals. It is defined in nsJSEnvironment which then calls the security manager via a virtual function. This can be avoided if the security manager implements JSSecurityCallbacks::findObjectPrincipals directly.
Yet another inefficiency is the JSPrincipals::codebase string. It is not used in the JS engine. Moreover, its uses outside the engine are not on performance-critical paths so the string can be removed completely and reconstructed as necessary from the fields stored in the principals.
It would be nice to address these inefficiencies.
Comment 1•13 years ago
|
||
> Yet another inefficiency is the JSPrincipals::codebase string. It is not used in the JS
> engine.
Really? Then I agree; we should just nuke it.
Assignee | ||
Comment 2•13 years ago
|
||
(In reply to Boris Zbarsky (:bz) from comment #1)
> > Yet another inefficiency is the JSPrincipals::codebase string. It is not used in the JS
> > engine.
>
> Really? Then I agree; we should just nuke it.
Yes - if one simply moves the codebase field from JSPrincipals to nsJSPrincipals, then everything compiles and runs.
Comment 3•13 years ago
|
||
(1) findObjectPrincipals can go away with c-p-g (because we can assert findObjectPrincipals(obj) == obj->compartment()->principals).
(2) If you remove codebase, can you provide a simple helper so that it is easy to get a human-readable string out of a principals? This is most useful when debugging compartment assertions.
Assignee | ||
Comment 4•13 years ago
|
||
(In reply to Luke Wagner [:luke] from comment #3)
> (2) If you remove codebase, can you provide a simple helper so that it is
> easy to get a human-readable string out of a principals? This is most
> useful when debugging compartment assertions.
Is it OK to have a debug-only method like JS_PrintPrincipal(JSRuntime *rt, JSPrincpals *) or do you prefer to avoid any function calls during debugging?
Comment 5•13 years ago
|
||
Compartment assertions are only in debug builds, so that sounds fine to me. One request, could you instead make an #ifdef __cplusplus dump() member function of JSPrincipals (analogous to the newly added JSAtom::dump/JSObject::dump functions)?
Assignee | ||
Comment 6•13 years ago
|
||
This patch removes the codebase field from JSPrincipals and makes nsJSPrincipals to subclass both nsIPrincipals and JSPrincipals so a conversion between these types can be done via a simple static_cast without the need for any virtual calls. To trimmer JSPrincipals farther I moved the Destroy and Subsume function pointers to the security callbacks structure.
The patch also removed the JSContext:: version of the security callbacks as they are not used in FF since the web workers gained own runtime.
Attachment #598811 -
Flags: review?(luke)
Attachment #598811 -
Flags: review?(bzbarsky)
Assignee | ||
Comment 7•13 years ago
|
||
The patch here removes the transcoding support for principals delegating that to the callers. That allowed not only to remove the complex XDR - ObjectStream play but also the need to create ObjectStream in the first place when reading/writing scripts to the startup cache.
Attachment #598813 -
Flags: review?(mrbkap)
Comment 8•13 years ago
|
||
Comment on attachment 598811 [details] [diff] [review]
part 1
Looks great from a JS perspective. Great to see the runtime/context-security-callbacks split removed.
Attachment #598811 -
Flags: review?(luke) → review+
Comment 9•13 years ago
|
||
Comment on attachment 598811 [details] [diff] [review]
part 1
>+JS_EXPORT_API(void)
>+JSPrincipals::dump()
> {
>+ } else if (debugToken == 0x7e2df9d2) {
This should really be a define/const/enum/
>--- a/caps/src/nsPrincipal.cpp
>+++ b/caps/src/nsPrincipal.cpp
>@@ -141,53 +141,39 @@ nsPrincipal::Init(const nsACString& aCer
>- nsresult rv;
>+ nsresult rv = NS_OK;
> if (!aCertFingerprint.IsEmpty()) {
> rv = SetCertificate(aCertFingerprint, aSubjectName, aPrettyName, aCert);
> }
> return rv;
This could be
if (aCertFingerprint.IsEmpty()) {
return NS_OK;
}
return SetCertificate(aCertFingerprint, aSubjectName, aPrettyName, aCert);
>+nsSystemPrincipal::GetSpec(nsACString &aStr)
>+{
>+ aStr.Assign(SYSTEM_PRINCIPAL_SPEC);
>+ if (!aStr.EqualsLiteral(SYSTEM_PRINCIPAL_SPEC)) {
>+ return NS_ERROR_OUT_OF_MEMORY;
>+ }
>+ return NS_OK;
Can't you do the Length() trick here? (Maybe == ArrayLength(SYSTEM_PRINCIPAL_SPEC) - 1.)
>--- a/content/base/src/nsFrameMessageManager.cpp
>+++ b/content/base/src/nsFrameMessageManager.cpp
>@@ -804,27 +805,25 @@ nsFrameScriptExecutor::LoadFrameScriptIn
> JSScript* script =
>- JS_CompileUCScriptForPrincipals(mCx, nsnull, jsprin,
>- (jschar*)dataString.get(),
>+ JS_CompileUCScriptForPrincipals(mCx, nsnull,
>+ nsJSPrincipals::get(mPrincipal),
>+ (jschar*)dataString.get(),
While you're here, could you please make this static_cast<const jschar*>(dataString.get())?
>--- a/dom/base/nsJSEnvironment.cpp
>+++ b/dom/base/nsJSEnvironment.cpp
> // Safety first: get an object representing the script's principals, i.e.,
> // the entities who signed this script, or the fully-qualified-domain-name
> // or "codebase" from which it was loaded.
Duplicated code... I'm afraid I don't really see how to avoid it :/
>@@ -1575,58 +1536,51 @@ nsJSContext::CompileScript(const PRUnich
> // SecurityManager said "ok", but don't compile if aVersion is unknown.
> // Since the caller is responsible for parsing the version strings, we just
> // check it isn't JSVERSION_UNKNOWN.
> if (ok && ((JSVersion)aVersion) != JSVERSION_UNKNOWN) {
> JSAutoRequest ar(mContext);
>
> JSScript* script =
> ::JS_CompileUCScriptForPrincipalsVersion(mContext,
> scopeObject,
>- jsprin,
>+ nsJSPrincipals::get(aPrincipal),
> static_cast<const jschar*>(aText),
> aTextLength,
> aURL,
> aLineNo,
> JSVersion(aVersion));
> if (script) {
> NS_ASSERTION(aScriptObject.getScriptTypeID()==JAVASCRIPT,
> "Expecting JS script object holder");
> rv = aScriptObject.set(script);
> } else {
> rv = NS_ERROR_OUT_OF_MEMORY;
> }
> }
>
>- // Whew! Finally done.
>- JSPRINCIPALS_DROP(mContext, jsprin);
> return rv;
This can now use early returns... Feel like doing that?
Assignee | ||
Comment 10•13 years ago
|
||
(In reply to Ms2ger from comment #9)
> >+JS_EXPORT_API(void)
> >+JSPrincipals::dump()
> > {
> >+ } else if (debugToken == 0x7e2df9d2) {
>
> This should really be a define/const/enum/
Do you have any suggestion in which header it should be defined so it can be used both in caps/src and dom/workers ? Is it OK to to put it into caps/include/nsJSPrincipals.h and in include that under dom/workers?
Comment 11•13 years ago
|
||
(In reply to Igor Bukanov from comment #10)
> (In reply to Ms2ger from comment #9)
> > >+JS_EXPORT_API(void)
> > >+JSPrincipals::dump()
> > > {
> > >+ } else if (debugToken == 0x7e2df9d2) {
> >
> > This should really be a define/const/enum/
>
> Do you have any suggestion in which header it should be defined so it can be
> used both in caps/src and dom/workers ? Is it OK to to put it into
> caps/include/nsJSPrincipals.h and in include that under dom/workers?
bent might be able to answer that, I've CC'd him.
Since it's worker-specific we could put it in dom/workers/Workers.h
Comment 13•13 years ago
|
||
Comment on attachment 598811 [details] [diff] [review]
part 1
It'll take me a day or two to get to this.
Assignee | ||
Comment 14•13 years ago
|
||
The new patch incorporates nice suggestions from the comment 9.
Attachment #598811 -
Attachment is obsolete: true
Attachment #598813 -
Attachment is obsolete: true
Attachment #599732 -
Flags: review?(bzbarsky)
Attachment #598811 -
Flags: review?(bzbarsky)
Attachment #598813 -
Flags: review?(mrbkap)
Assignee | ||
Comment 15•13 years ago
|
||
The new patch reflects changes from the bug 725576.
Attachment #599736 -
Flags: review?(mrbkap)
Assignee | ||
Comment 16•13 years ago
|
||
here is a rebased patch - the previous no longer applies to MC tip.
Attachment #599732 -
Attachment is obsolete: true
Attachment #599736 -
Attachment is obsolete: true
Attachment #600329 -
Flags: review?(bzbarsky)
Attachment #599732 -
Flags: review?(bzbarsky)
Attachment #599736 -
Flags: review?(mrbkap)
Assignee | ||
Comment 17•13 years ago
|
||
The patch required a rebase.
Attachment #600329 -
Attachment is obsolete: true
Attachment #601429 -
Flags: review?(bzbarsky)
Attachment #600329 -
Flags: review?(bzbarsky)
Assignee | ||
Comment 18•13 years ago
|
||
here is a rebased patch.
Boris, do you have time to review this?
Attachment #601429 -
Attachment is obsolete: true
Attachment #603075 -
Flags: review?(bzbarsky)
Attachment #601429 -
Flags: review?(bzbarsky)
Comment 19•13 years ago
|
||
Yes, I've been working on it as we go. It keep changing with no indication of what changed, which is complicating things a bit.... :(
Comment 20•13 years ago
|
||
Comment on attachment 603075 [details] [diff] [review]
v5
>bug 728250 - remove JSPrincipals::codebase
>
>try: -b do -p all -u all -t none
This needs a real checkin comment, please..... Probably one that describes the actual changes being made at a bit more length.
>+++ b/caps/idl/nsIPrincipal.idl
>+ [noscript] readonly attribute AUTF8String spec;
Perhaps scriptLocation?
The are two callers, one of which can handle failure in this method fine and the other of which ignores the return value.... I think just always returning NS_OK from this function, even on OOM, would be fine.
>+++ b/caps/include/nsJSPrincipals.h
>+++ b/caps/include/nsNullPrincipal.h
>+ // Our refcount is managed by nsJSPrincipal. Use this macro to avoid an
nsJSPrincipals
> // FIXME: bug 327245 -- I sorta wish there were a clean way to share the
>- // mJSPrincipals munging code between the various principal classes without
>+ // nsJSPrincipal munging code between the various principal classes without
nsJSPrincipals, but once this patch lands this looks easy: just move the refcounting to nsJSPrincipals, right?
>+++ b/caps/include/nsPrincipal.h
>+ // Our refcount is managed by nsJSPrincipal. Use this macro to avoid
nsJSPrincipals
>+++ b/caps/include/nsSystemPrincipal.h
>+ // Our refcount is managed by nsJSPrincipal. Use this macro to avoid
nsJSPrincipals
>+++ b/caps/src/nsJSPrincipals.cpp
>+nsJSPrincipals::Destroy(JSPrincipals *jsprin)
> // Note that we don't want to use NS_IF_RELEASE because it will try
> // to set nsjsprin->nsIPrincipalPtr to nsnull *after* nsjsprin has
> // already been destroyed.
This comment no longer seems relevant. Take it out?
>+++ b/caps/src/nsPrincipal.cpp
>@@ -141,53 +141,37 @@ nsPrincipal::Init(const nsACString& aCer
>+ return aCertFingerprint.IsEmpty()
>+ ? NS_OK
>+ : SetCertificate(aCertFingerprint, aSubjectName, aPrettyName, aCert);
I would prefer using an actual if statement here.
>+++ b/caps/src/nsScriptSecurityManager.cpp
> nsScriptSecurityManager::Shutdown()
>- JS_SetRuntimeSecurityCallbacks(sRuntime, NULL);
Why is this no longer needed?
>+++ b/dom/base/nsJSEnvironment.cpp
>@@ -1205,54 +1204,49 @@ nsJSContext::EvaluateStringWithValue(con
> nsIPrincipal *principal = aPrincipal;
>+ nsCOMPtr<nsIPrincipal> globalPrincipalStrongRef;
Why not just make |principal| an nsCOMPtr? That's assuming we really need the strong ref....
>@@ -1414,65 +1404,45 @@ nsJSContext::EvaluateString(const nsAStr
> nsIPrincipal *principal = aPrincipal;
...
>+ nsCOMPtr<nsIPrincipal> globalPrincipalStrongRef;
Likewise.
You may want to have bent look over the worker changes; they seem fine to me.
I assume someone else is covering review for js/src.
>+++ b/js/xpconnect/src/XPCComponents.cpp
>@@ -1755,30 +1732,27 @@ class JSCompartmentsMultiReporter : publ
>+ path.Insert(js::IsSystemCompartment(c)
>+ ? NS_LITERAL_CSTRING("compartments/system/")
>+ : NS_LITERAL_CSTRING("compartments/user/"),
I seem to recall at least some compilers miscompiling this.... Maybe it's ok now, though.
I really like these changes in general!
I'd also really appreciate an interdiff for whatever changes you make to the patch if you want a follow-up reviews.
r=me with the nits fixed.
Attachment #603075 -
Flags: review?(bzbarsky) → review+
Assignee | ||
Comment 21•13 years ago
|
||
(In reply to Boris Zbarsky (:bz) from comment #20)
> The are two callers, one of which can handle failure in this method fine and
> the other of which ignores the return value.... I think just always
> returning NS_OK from this function, even on OOM, would be fine.
Those two callers expects an instance of nsJSPrincipals, so in the updated patch I moved the function out from IDL to nsJSPrincipals.
> >+++ b/caps/src/nsScriptSecurityManager.cpp
> > nsScriptSecurityManager::Shutdown()
> >- JS_SetRuntimeSecurityCallbacks(sRuntime, NULL);
>
> Why is this no longer needed?
The patch moves the destroy callback from the JSPrincipals to the security manager structure. But at the point when nsScriptSecurityManager::Shutdown() is called the JS may still have not run the last GC and some principals may yet to be destroyed. To allow proper finalization of them the patch keeps the security callbacks intact. This should be fine as the callbacks are a static structure that outlives the JSRuntime.
Should I comment about that in the code?
EvaluateStringWithValue(con
> > nsIPrincipal *principal = aPrincipal;
> >+ nsCOMPtr<nsIPrincipal> globalPrincipalStrongRef;
>
> Why not just make |principal| an nsCOMPtr? That's assuming we really need
> the strong ref....
I kept the strong ref at the code previously did. But I wanted to avoid useless AddRef/Release on aPrincipal for the common case when aPrincipal is not null. This is the reason for the extra variable.
> I assume someone else is covering review for js/src.
Luke already did that.
Assignee | ||
Comment 22•13 years ago
|
||
Bent, can you have a look at web-workers related small changes that the patch makes?
Attachment #603075 -
Attachment is obsolete: true
Attachment #603682 -
Flags: review?(bent.mozilla)
Assignee | ||
Comment 23•13 years ago
|
||
Here are the changes since v5 to fix the comments. Most of the changes are related to moving GetScriptLocation from the idl to nsJSprincipals.
Attachment #603684 -
Flags: review?(bzbarsky)
Comment 24•13 years ago
|
||
> This should be fine as the callbacks are a static structure that outlives the JSRuntime.
It's only fine if the methods in that structure don't use gScriptSecMan directly or indirectly.... There should probably be an audit to that effect and comments added to all relevant methods.
Alternately, maybe swap out the callbacks on security manager shutdown for a structure that only defines destroy and document that _that_ must not depend on the security manager?
> I kept the strong ref at the code previously did.
The code previously held a strong ref even if aPrincipal is not null. If you're actually trying to preserve behavior, you should do that too; nothing guarantees that the caller has any more of a ref to aPrincipal than it would have via the nsIScriptObjectPrincipal in the !aPrincipal codepath...
Comment 25•13 years ago
|
||
Comment on attachment 603684 [details] [diff] [review]
v5 - v6 interdiff
r=me modulo the comments
Attachment #603684 -
Flags: review?(bzbarsky) → review+
Comment on attachment 603682 [details] [diff] [review]
v6
Review of attachment 603682 [details] [diff] [review]:
-----------------------------------------------------------------
Looks good, thanks.
::: dom/workers/WorkerPrivate.cpp
@@ +156,5 @@
> }
>
> NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(JsWorkerMallocSizeOf, "js-worker")
>
> +struct WorkerJSRuntimeStats : public JS::RuntimeStats {
Nit: { on next line
@@ +161,5 @@
> + WorkerJSRuntimeStats()
> + : JS::RuntimeStats(JsWorkerMallocSizeOf) { }
> +
> + virtual void initExtraCompartmentStats(JSCompartment *c,
> + JS::CompartmentStats *cstats) MOZ_OVERRIDE {
Nit: { on next line
::: dom/workers/Workers.h
@@ +123,5 @@
> WorkerCrossThreadDispatcher*
> GetWorkerCrossThreadDispatcher(JSContext* aCx, jsval aWorker);
>
> +// Random unique constant to facilitate JSPrincipal debugging
> +const uint32_t JSPRINCIPALS_DEBUG_TOKEN = 0x7e2df9d2;
Nit: Since this is not a #define I think I'd prefer 'kJSPrincipalsDebugToken'.
Attachment #603682 -
Flags: review?(bent.mozilla) → review+
Assignee | ||
Comment 27•13 years ago
|
||
Attachment #603682 -
Attachment is obsolete: true
Attachment #603684 -
Attachment is obsolete: true
Assignee | ||
Comment 28•13 years ago
|
||
The new patch addresses the comments for the v6. In particular, it always keeps a strong reference to the passed JSPrincipal pointer in EvaluateStringWithValue/EvaluateString and moves the destroy principals callback outside of JSSecurityCallbacks into own JSRuntime field. This way the security manager can continue to clear the security callbacks on shutdown while the principals can be safely destroyed after the security manager is destroyed.
Attachment #604062 -
Flags: review?(bzbarsky)
Comment 29•13 years ago
|
||
Comment on attachment 604062 [details] [diff] [review]
v6 - v7 interdiff
> + // The JS runtime can calls this method during the last GC when
s/calls/call/ and r=me
Attachment #604062 -
Flags: review?(bzbarsky) → review+
Assignee | ||
Comment 30•13 years ago
|
||
Comment 31•13 years ago
|
||
Status: NEW → RESOLVED
Closed: 13 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla13
You need to log in
before you can comment on or make changes to this bug.
Description
•