Merge m-c to fx-team

This commit is contained in:
Wes Kocher 2013-12-20 22:34:56 -08:00
commit 5e9b06cdb1
31 changed files with 361 additions and 271 deletions

View File

@ -525,8 +525,7 @@ nsEventListenerManager::ListenerCanHandle(nsListenerStruct* aLs,
}
return aLs->mTypeString.Equals(aEvent->typeString);
}
MOZ_ASSERT_IF(aEvent->eventStructType != NS_SCRIPT_ERROR_EVENT,
mIsMainThreadELM);
MOZ_ASSERT(mIsMainThreadELM);
return aLs->mEventType == aEvent->message;
}
@ -595,11 +594,6 @@ nsEventListenerManager::SetEventHandlerInternal(JS::Handle<JSObject*> aScopeObje
nsCOMPtr<nsIJSEventListener> scriptListener;
NS_NewJSEventListener(aScopeObject, mTarget, aName,
aHandler, getter_AddRefs(scriptListener));
if (!aName && aTypeString.EqualsLiteral("error")) {
eventType = NS_LOAD_ERROR;
}
EventListenerHolder holder(scriptListener);
AddEventListenerInternal(holder, eventType, aName, aTypeString, flags,
true);

View File

@ -482,7 +482,7 @@ public:
if (mIsMainThreadELM) {
handler = GetEventHandlerInternal(nsGkAtoms::onerror, EmptyString());
} else {
handler = GetEventHandlerInternal(nullptr, NS_LITERAL_STRING("onerror"));
handler = GetEventHandlerInternal(nullptr, NS_LITERAL_STRING("error"));
}
return handler ? handler->OnErrorEventHandler() : nullptr;
}

View File

@ -148,27 +148,28 @@ TextTrack::GetActiveCues()
// the active cue list from scratch.
if (mDirty) {
mCuePos = 0;
mDirty = false;
mDirty = true;
mActiveCueList->RemoveAll();
}
double playbackTime = mMediaElement->CurrentTime();
// Remove all the cues from the active cue list whose end times now occur
// earlier then the current playback time.
for (uint32_t i = mActiveCueList->Length(); i > 0; i--) {
if ((*mActiveCueList)[i - 1]->EndTime() < playbackTime) {
mActiveCueList->RemoveCueAt(i - 1);
}
// earlier then the current playback time. When we reach a cue whose end time
// is valid we can safely stop iterating as the list is sorted.
for (uint32_t i = 0; i < mActiveCueList->Length() &&
(*mActiveCueList)[i]->EndTime() < playbackTime; i++) {
mActiveCueList->RemoveCueAt(i);
}
// Add all the cues, starting from the position of the last cue that was
// added, that have valid start and end times for the current playback time.
// We can stop iterating safely once we encounter a cue that does not have
// a valid start time as the cue list is sorted.
for (; mCuePos < mCueList->Length() &&
(*mCueList)[mCuePos]->StartTime() <= playbackTime; mCuePos++) {
if ((*mCueList)[mCuePos]->EndTime() >= playbackTime) {
mActiveCueList->AddCue(*(*mCueList)[mCuePos]);
// valid times for the current playback time as the cue list is sorted.
for (; mCuePos < mCueList->Length(); mCuePos++) {
TextTrackCue* cue = (*mCueList)[mCuePos];
if (cue->StartTime() > playbackTime || cue->EndTime() < playbackTime) {
break;
}
mActiveCueList->AddCue(*cue);
}
return mActiveCueList;
}

View File

@ -10,6 +10,9 @@
#include "nsComponentManagerUtils.h"
#include "mozilla/ClearOnShutdown.h"
// Alternate value for the 'auto' keyword.
#define WEBVTT_AUTO -1
namespace mozilla {
namespace dom {
@ -36,9 +39,8 @@ TextTrackCue::SetDefaultCueSettings()
mSize = 100;
mPauseOnExit = false;
mSnapToLines = true;
mLine.SetAsAutoKeyword() = AutoKeyword::Auto;
mLine = WEBVTT_AUTO;
mAlign = AlignSetting::Middle;
mLineAlign = AlignSetting::Start;
mVertical = DirectionSetting::_empty;
}

View File

@ -14,7 +14,6 @@
#include "nsIWebVTTParserWrapper.h"
#include "mozilla/StaticPtr.h"
#include "nsIDocument.h"
#include "mozilla/dom/UnionTypes.h"
namespace mozilla {
namespace dom {
@ -83,9 +82,8 @@ public:
void SetStartTime(double aStartTime)
{
if (mStartTime == aStartTime) {
if (mStartTime == aStartTime)
return;
}
mStartTime = aStartTime;
CueChanged();
@ -98,9 +96,8 @@ public:
void SetEndTime(double aEndTime)
{
if (mEndTime == aEndTime) {
if (mEndTime == aEndTime)
return;
}
mEndTime = aEndTime;
CueChanged();
@ -113,9 +110,8 @@ public:
void SetPauseOnExit(bool aPauseOnExit)
{
if (mPauseOnExit == aPauseOnExit) {
if (mPauseOnExit == aPauseOnExit)
return;
}
mPauseOnExit = aPauseOnExit;
CueChanged();
@ -143,9 +139,8 @@ public:
void SetVertical(const DirectionSetting& aVertical)
{
if (mVertical == aVertical) {
if (mVertical == aVertical)
return;
}
mReset = true;
mVertical = aVertical;
@ -159,58 +154,24 @@ public:
void SetSnapToLines(bool aSnapToLines)
{
if (mSnapToLines == aSnapToLines) {
if (mSnapToLines == aSnapToLines)
return;
}
mReset = true;
mSnapToLines = aSnapToLines;
CueChanged();
}
void GetLine(OwningLongOrAutoKeyword& aLine) const
double Line() const
{
if (mLine.IsLong()) {
aLine.SetAsLong() = mLine.GetAsLong();
return;
}
aLine.SetAsAutoKeyword() = mLine.GetAsAutoKeyword();
return mLine;
}
void SetLine(const LongOrAutoKeyword& aLine)
void SetLine(double aLine)
{
if (aLine.IsLong() &&
(mLine.IsAutoKeyword() || (aLine.GetAsLong() != mLine.GetAsLong()))) {
mLine.SetAsLong() = aLine.GetAsLong();
CueChanged();
mReset = true;
return;
}
if (mLine.IsLong()) {
mLine.SetAsAutoKeyword() = aLine.GetAsAutoKeyword();
CueChanged();
mReset = true;
}
}
AlignSetting LineAlign() const
{
return mLineAlign;
}
void SetLineAlign(AlignSetting& aLineAlign, ErrorResult& aRv)
{
if (mLineAlign == aLineAlign)
return;
if (aLineAlign == AlignSetting::Left ||
aLineAlign == AlignSetting::Right) {
return aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
}
//XXX: TODO Line position can be a keyword auto. bug882299
mReset = true;
mLineAlign = aLineAlign;
CueChanged();
mLine = aLine;
}
int32_t Position() const
@ -220,9 +181,9 @@ public:
void SetPosition(int32_t aPosition, ErrorResult& aRv)
{
if (mPosition == aPosition) {
// XXXhumph: validate? bug 868519.
if (mPosition == aPosition)
return;
}
if (aPosition > 100 || aPosition < 0){
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
@ -262,9 +223,8 @@ public:
void SetAlign(AlignSetting& aAlign)
{
if (mAlign == aAlign) {
if (mAlign == aAlign)
return;
}
mReset = true;
mAlign = aAlign;
@ -278,9 +238,8 @@ public:
void SetText(const nsAString& aText)
{
if (mText == aText) {
if (mText == aText)
return;
}
mReset = true;
mText = aText;
@ -363,9 +322,8 @@ private:
bool mSnapToLines;
nsString mRegionId;
DirectionSetting mVertical;
LongOrAutoKeyword mLine;
int mLine;
AlignSetting mAlign;
AlignSetting mLineAlign;
// Holds the computed DOM elements that represent the parsed cue text.
// http://www.whatwg.org/specs/web-apps/current-work/#text-track-cue-display-state

View File

@ -8,18 +8,10 @@ This
00:01.200 --> 00:02.400
Is
2.5
00:02.000 --> 00:03.500
(Over here?!)
3
00:02.710 --> 00:02.910
A
4
3
00:03.217 --> 00:03.989
Test
5
00:03.217 --> 00:03.989
And more!
Test

View File

@ -37,7 +37,7 @@ SpecialPowers.pushPrefEnv({"set": [["media.webvtt.enabled", true]]},
is(trackElement.readyState, 2, "Track::ReadyState should be set to LOADED.");
var cueList = trackElement.track.cues;
is(cueList.length, 6, "Cue list length should be 6.");
is(cueList.length, 4, "Cue list length should be 4.");
// Check that the typedef of TextTrackCue works in Gecko.
is(window.TextTrackCue, undefined, "TextTrackCue should be undefined.");
@ -57,80 +57,41 @@ SpecialPowers.pushPrefEnv({"set": [["media.webvtt.enabled", true]]},
// Check that all cue times were not rounded
is(cueList[1].startTime, 1.2, "Second cue's start time should be 1.2.");
is(cueList[1].endTime, 2.4, "Second cue's end time should be 2.4.");
is(cueList[2].startTime, 2, "Third cue's start time should be 2.");
is(cueList[2].endTime, 3.5, "Third cue's end time should be 3.5.");
is(cueList[3].startTime, 2.71, "Fourth cue's start time should be 2.71.");
is(cueList[3].endTime, 2.91, "Fourth cue's end time should be 2.91.");
is(cueList[4].startTime, 3.217, "Fifth cue's start time should be 3.217.");
is(cueList[4].endTime, 3.989, "Fifth cue's end time should be 3.989.");
is(cueList[5].startTime, 3.217, "Sixth cue's start time should be 3.217.");
is(cueList[5].endTime, 3.989, "Sixth cue's end time should be 3.989.");
is(cueList[2].startTime, 2.71, "Third cue's start time should be 2.71.");
is(cueList[2].endTime, 2.91, "Third cue's end time should be 2.91.");
is(cueList[3].startTime, 3.217, "Fourth cue's start time should be 3.217.");
is(cueList[3].endTime, 3.989, "Fourth cue's end time should be 3.989.");
// Check that Cue setters are working correctly.
cue.id = "Cue 01";
is(cue.id, "Cue 01", "Cue's ID should be 'Cue 01'.");
cue.startTime = 0.51;
is(cue.startTime, 0.51, "Cue's start time should be 0.51.");
cue.endTime = 0.71;
is(cue.endTime, 0.71, "Cue's end time should be 0.71.");
cue.startTime = 1.5;
is(cue.startTime, 1.5, "Cue's start time should be 1.5.");
cue.endTime = 2.5;
is(cue.endTime, 2.5, "Cue's end time should be 2.5.");
cue.pauseOnExit = true;
is(cue.pauseOnExit, true, "Cue's pause on exit flag should be true.");
// Check that cue line align works properly
is(cue.lineAlign, "start", "Cue's default line alignment should be start.");
var exceptionHappened = false;
try {
cue.lineAlign = "left";
} catch(e) {
exceptionHappened = true;
is(e.name, "SyntaxError", "Should have thrown SyntaxError.");
}
ok(exceptionHappened, "Exception should have happened.");
exceptionHappened = false;
try {
cue.lineAlign = "right";
} catch(e) {
exceptionHappened = true;
is(e.name, "SyntaxError", "Should have thrown SyntaxError.");
}
ok(exceptionHappened, "Exception should have happened.");
cue.lineAlign = "middle";
is(cue.lineAlign, "middle", "Cue's line align should be middle.");
cue.lineAlign = "START";
is(cue.lineAlign, "middle", "Cue's line align should be middle.");
cue.lineAlign = "end";
is(cue.lineAlign, "end", "Cue's line align should be end.");
// Check cue.line
is(cue.line, "auto", "Cue's line value should initially be auto.");
cue.line = 12410
is(cue.line, 12410, "Cue's line value should now be 12410.");
cue.line = "auto";
is(cue.line, "auto", "Cue's line value should now be auto.");
// Check that we can create and add new VTTCues
var vttCue = new VTTCue(3.999, 4, "foo");
trackElement.track.addCue(vttCue);
is(cueList.length, 7, "Cue list length should now be 7.");
is(cueList.length, 5, "Cue list length should now be 5.");
// Check that new VTTCue was added correctly
cue = cueList[6];
cue = cueList[4];
is(cue.startTime, 3.999, "Cue's start time should be 3.999.");
is(cue.endTime, 4, "Cue's end time should be 4.");
is(cue.text, "foo", "Cue's text should be foo.");
// Adding the same cue again should not increase the cue count.
trackElement.track.addCue(vttCue);
is(cueList.length, 7, "Cue list length should be 7.");
is(cueList.length, 5, "Cue list length should be 5.");
// Check that we are able to remove cues.
trackElement.track.removeCue(cue);
is(cueList.length, 6, "Cue list length should be 6.");
is(cueList.length, 4, "Cue list length should be 4.");
exceptionHappened = false;
var exceptionHappened = false;
try {
// We should not be able to remove a cue that is not in the list.
cue = new VTTCue(1, 2, "foo");
@ -145,45 +106,9 @@ SpecialPowers.pushPrefEnv({"set": [["media.webvtt.enabled", true]]},
// when we shouln't have.
ok(exceptionHappened, "Exception should have happened.");
is(cueList.length, 6, "Cue list length should be 6.");
is(cueList.length, 4, "Cue list length should be 4.");
// Test TextTrack::ActiveCues.
var cueInfo = [
{ startTime: 0.51, endTime: 0.71, ids: ["Cue 01"] },
{ startTime: 1.2, endTime: 2, ids: [2] },
{ startTime: 2, endTime: 2.4, ids: [2, 2.5] },
{ startTime: 2.4, endTime: 2.71, ids: [2.5] },
{ startTime: 2.71, endTime: 2.91, ids: [2.5, 3] },
{ startTime: 2.91, endTime: 3.217, ids: [2.5] },
{ startTime: 3.217, endTime: 3.5, ids: [2.5, 4, 5] },
{ startTime: 3.50, endTime: 3.989, ids: [4, 5] }
];
video.addEventListener("timeupdate", function() {
var activeCues = trackElement.track.activeCues,
found = false,
playbackTime = video.currentTime;
for (var i = 0; i < cueInfo.length && !found; i++) {
var cue = cueInfo[i];
if (playbackTime >= cue.startTime && playbackTime <= cue.endTime) {
is(activeCues.length, cue.ids.length, "There should be " + cue.ids.length + " currently active cue(s).");
for (var j = 0; j < cue.ids.length; j++) {
isnot(activeCues.getCueById(cue.ids[j]), undefined, "The cue with ID " + cue.ids[j] + " should be active.");
}
found = true;
}
}
if (!found) {
is(activeCues.length, 0, "There should be 0 currently active cues.");
}
});
video.addEventListener("ended", function(){
SimpleTest.finish();
});
video.play();
SimpleTest.finish();
});
}
);

View File

@ -840,9 +840,7 @@ def UnionTypes(descriptors, dictionaries, callbacks, config):
if typeNeedsRooting(f):
headers.add("mozilla/dom/RootedDictionary.h")
elif f.isEnum():
# Need to see the actual definition of the enum,
# unfortunately.
headers.add(CGHeaders.getDeclarationFilename(f.inner))
headers.add(CGHeaders.getDeclarationFilename(f))
map(addInfoForType, getAllTypes(descriptors, dictionaries, callbacks))

View File

@ -976,8 +976,20 @@ function PeerConnectionWrapper(label, configuration) {
var self = this;
// This enables tests to validate that the next ice state is the one they expect to happen
this.next_ice_state = ""; // in most cases, the next state will be "checking", but in some tests "closed"
// This allows test to register their own callbacks for ICE connection state changes
this.ice_connection_callbacks = [ ];
this._pc.oniceconnectionstatechange = function() {
ok(self._pc.iceConnectionState != undefined, "iceConnectionState should not be undefined");
info(self + ": oniceconnectionstatechange fired, new state is: " + self._pc.iceConnectionState);
if (Object.keys(self.ice_connection_callbacks).length >= 1) {
var it = Iterator(self.ice_connection_callbacks);
var name = "";
var callback = "";
for ([name, callback] in it) {
callback();
}
}
if (self.next_ice_state != "") {
is(self._pc.iceConnectionState, self.next_ice_state, "iceConnectionState changed to '" +
self.next_ice_state + "'");
@ -1081,13 +1093,21 @@ PeerConnectionWrapper.prototype = {
},
/**
* Returns the remote signaling state.
* Returns the signaling state.
*
* @returns {object} The local description
*/
get signalingState() {
return this._pc.signalingState;
},
/**
* Returns the ICE connection state.
*
* @returns {object} The local description
*/
get iceConnectionState() {
return this._pc.iceConnectionState;
},
/**
* Callback when we get media from either side. Also an appropriate
@ -1316,6 +1336,63 @@ PeerConnectionWrapper.prototype = {
}) ;
},
/**
* Returns if the ICE the connection state is "connected".
*
* @returns {boolean} True is the connection state is "connected", otherwise false.
*/
isIceConnected : function PCW_isIceConnected() {
info("iceConnectionState: " + this.iceConnectionState);
return this.iceConnectionState === "connected";
},
/**
* Returns if the ICE the connection state is "checking".
*
* @returns {boolean} True is the connection state is "checking", otherwise false.
*/
isIceChecking : function PCW_isIceChecking() {
return this.iceConnectionState === "checking";
},
/**
* Returns if the ICE the connection state is "new".
*
* @returns {boolean} True is the connection state is "new", otherwise false.
*/
isIceNew : function PCW_isIceNew() {
return this.iceConnectionState === "new";
},
/**
* Registers a callback for the ICE connection state change and
* reports success (=connected) or failure via the callbacks.
* States "new" and "checking" are ignored.
*
* @param {function} onSuccess
* Callback if ICE connection status is "connected".
* @param {function} onFailure
* Callback if ICE connection reaches a different state than
* "new", "checking" or "connected".
*/
waitForIceConnected : function PCW_waitForIceConnected(onSuccess, onFailure) {
var self = this;
var mySuccess = onSuccess;
var myFailure = onFailure;
function iceConnectedChanged () {
if (self.isIceConnected()) {
delete self.ice_connection_callbacks["waitForIceConnected"];
mySuccess();
} else if (! (self.isIceChecking() || self.isIceNew())) {
delete self.ice_connection_callbacks["waitForIceConnected"];
myFailure();
}
};
self.ice_connection_callbacks["waitForIceConnected"] = (function() {iceConnectedChanged()});
},
/**
* Checks that we are getting the media streams we expect.
*

View File

@ -136,6 +136,52 @@ var commandsPeerConnection = [
});
}
],
[
'PC_LOCAL_WAIT_FOR_ICE_CONNECTED',
function (test) {
var myTest = test;
var myPc = myTest.pcLocal;
function onIceConnectedSuccess () {
ok(true, "pc_local: ICE switched to connected state");
myTest.next();
};
function onIceConnectedFailed () {
ok(false, "pc_local: ICE failed to switch to connected");
myTest.next();
};
if (myPc.isIceConnected()) {
ok(true, "pc_local: ICE is in connected state");
myTest.next();
} else {
myPc.waitForIceConnected(onIceConnectedSuccess, onIceConnectedFailed);
}
}
],
[
'PC_REMOTE_WAIT_FOR_ICE_CONNECTED',
function (test) {
var myTest = test;
var myPc = myTest.pcRemote;
function onIceConnectedSuccess () {
ok(true, "pc_remote: ICE switched to connected state");
myTest.next();
};
function onIceConnectedFailed () {
ok(false, "pc_remote: ICE failed to switch to connected");
myTest.next();
};
if (myPc.isIceConnected()) {
ok(true, "pc_remote: ICE is in connected state");
myTest.next();
} else {
myPc.waitForIceConnected(onIceConnectedSuccess, onIceConnectedFailed);
}
}
],
[
'PC_LOCAL_CHECK_MEDIA_STREAMS',
function (test) {

View File

@ -169,7 +169,9 @@ nsJSEventListener::HandleEvent(nsIDOMEvent* aEvent)
NS_ENSURE_TRUE(aEvent, NS_ERROR_UNEXPECTED);
InternalScriptErrorEvent* scriptEvent =
aEvent->GetInternalNSEvent()->AsScriptErrorEvent();
if (scriptEvent && scriptEvent->message == NS_LOAD_ERROR) {
if (scriptEvent &&
(scriptEvent->message == NS_LOAD_ERROR ||
scriptEvent->typeString.EqualsLiteral("error"))) {
errorMsg = scriptEvent->errorMsg;
msgOrEvent.SetAsString() = static_cast<nsAString*>(&errorMsg);

View File

@ -35,9 +35,8 @@ interface VTTCue : EventTarget {
attribute DOMString regionId;
attribute DirectionSetting vertical;
attribute boolean snapToLines;
attribute (long or AutoKeyword) line;
[SetterThrows]
attribute AlignSetting lineAlign;
// XXXhumph: https://www.w3.org/Bugs/Public/show_bug.cgi?id=20651
// attribute (long or AutoKeyword) line;
[SetterThrows]
attribute long position;
[SetterThrows]

View File

@ -1223,7 +1223,8 @@ public:
MOZ_ASSERT(target == globalTarget->GetWrapperPreserveColor());
// Icky, we have to fire an InternalScriptErrorEvent...
InternalScriptErrorEvent event(true, NS_LOAD_ERROR);
MOZ_ASSERT(!NS_IsMainThread());
InternalScriptErrorEvent event(true, NS_USER_DEFINED_EVENT);
event.lineNr = aLineNumber;
event.errorMsg = aMessage.get();
event.fileName = aFilename.get();
@ -1238,6 +1239,7 @@ public:
}
else if ((sgo = nsJSUtils::GetStaticScriptGlobal(target))) {
// Icky, we have to fire an InternalScriptErrorEvent...
MOZ_ASSERT(NS_IsMainThread());
InternalScriptErrorEvent event(true, NS_LOAD_ERROR);
event.lineNr = aLineNumber;
event.errorMsg = aMessage.get();
@ -3213,6 +3215,7 @@ WorkerPrivateParent<Derived>::BroadcastErrorToSharedWorkers(
do_QueryInterface(windowAction.mWindow);
MOZ_ASSERT(sgo);
MOZ_ASSERT(NS_IsMainThread());
InternalScriptErrorEvent event(true, NS_LOAD_ERROR);
event.lineNr = aLineNumber;
event.errorMsg = aMessage.BeginReading();

View File

@ -36,3 +36,7 @@ onmessage = function(event) {
}
onerror = errorHandler;
onerror = onerror;
if (!onerror || onerror != onerror) {
throw "onerror wasn't set properly";
}

View File

@ -29,14 +29,18 @@ if (!xhr.onload) {
postMessage(message);
}
xhr.addEventListener("error", function(event) {
xhr.onerror = function(event) {
if (event.target != xhr) {
throw "onerror event.target != xhr";
}
var message = { type: "error",
error: event.target.status };
postMessage(message);
}, false);
};
xhr.onerror = xhr.onerror;
if (!xhr.onerror || xhr.onerror != xhr.onerror) {
throw "onerror wasn't set properly";
}
function onprogress(event) {
if (event.target != xhr) {

View File

@ -90,8 +90,8 @@ function isMatchingConstructor(destructor, edge)
var variable = callee.Variable;
if (variable.Kind != "Func")
return false;
var name = variable.Name[0];
var destructorName = destructor.Exp[0].Variable.Name[0];
var name = readable(variable.Name[0]);
var destructorName = readable(destructor.Exp[0].Variable.Name[0]);
var match = destructorName.match(/^(.*?::)~(\w+)\(/);
if (!match) {
printErr("Unhandled destructor syntax: " + destructorName);

View File

@ -220,6 +220,11 @@ for k,v in vars(args).items():
if args.tag and not args.buildcommand:
args.buildcommand="build.%s" % args.tag
if args.jobs is not None:
data['jobs'] = args.jobs
if not data.get('jobs'):
data['jobs'] = subprocess.check_output(['nproc', '--ignore=1'])
if args.buildcommand:
data['buildcommand'] = args.buildcommand
elif 'BUILD' in os.environ:
@ -232,6 +237,9 @@ if 'ANALYZED_OBJDIR' in os.environ:
if 'SOURCE' in os.environ:
data['source'] = os.environ['SOURCE']
if not data.get('source') and data.get('sixgill_bin'):
path = subprocess.check_output(['sh', '-c', data['sixgill_bin'] + '/xdbkeys file_source.xdb | grep jsapi.cpp'])
data['source'] = path.replace("/js/src/jsapi.cpp", "")
steps = [ 'dbs',
'callgraph',

View File

@ -24,9 +24,8 @@ var tmpfile = scriptArgs[6] || "tmp.txt";
var gcFunctions = {};
var text = snarf("gcFunctions.lst").split("\n");
assert(text.pop().length == 0);
for (var line of text) {
gcFunctions[line] = true;
}
for (var line of text)
gcFunctions[mangled(line)] = true;
var suppressedFunctions = {};
var text = snarf(suppressedFunctionsFile).split("\n");
@ -190,7 +189,7 @@ function edgeKillsVariable(edge, variable)
break;
assert(callee.Variable.Kind == "Func");
var calleeName = callee.Variable.Name[0];
var calleeName = readable(callee.Variable.Name[0]);
// Constructor calls include the text 'Name::Name(' or 'Name<...>::Name('.
var openParen = calleeName.indexOf('(');
@ -224,11 +223,9 @@ function edgeCanGC(edge)
if (callee.Kind == "Var") {
var variable = callee.Variable;
assert(variable.Kind == "Func");
if (variable.Name[0] in gcFunctions)
var callee = mangled(variable.Name[0]);
if (callee in gcFunctions)
return "'" + variable.Name[0] + "'";
var otherName = otherDestructorName(variable.Name[0]);
if (otherName in gcFunctions)
return "'" + otherName + "'";
return null;
}
assert(callee.Kind == "Drf");
@ -241,8 +238,8 @@ function edgeCanGC(edge)
return (fullFieldName in suppressedFunctions) ? null : fullFieldName;
}
assert(callee.Exp[0].Kind == "Var");
var calleeName = callee.Exp[0].Variable.Name[0];
return indirectCallCannotGC(functionName, calleeName) ? null : "*" + calleeName;
var varName = callee.Exp[0].Variable.Name[0];
return indirectCallCannotGC(functionName, varName) ? null : "*" + varName;
}
function variableUseFollowsGC(suppressed, variable, worklist)
@ -364,6 +361,12 @@ function variableLiveAcrossGC(suppressed, variable)
return null;
}
// An unrooted variable has its address stored in another variable via
// assignment, or passed into a function that can GC. If the address is
// assigned into some other variable, we can't track it to see if it is held
// live across a GC. If it is passed into a function that can GC, then it's
// sort of like a Handle to an unrooted location, and the callee could GC
// before overwriting it or rooting it.
function unsafeVariableAddressTaken(suppressed, variable)
{
for (var body of functionBodies) {
@ -494,7 +497,7 @@ function processBodies(functionName)
{
if (!("DefineVariable" in functionBodies[0]))
return;
var suppressed = (functionName in suppressedFunctions);
var suppressed = (mangled(functionName) in suppressedFunctions);
for (var variable of functionBodies[0].DefineVariable) {
if (variable.Variable.Kind == "Return")
continue;

View File

@ -19,8 +19,16 @@ var ignoreIndirectCalls = {
"nsTraceRefcntImpl.cpp:void (* leakyLogRelease)(void*, int, int)": true,
};
function indirectCallCannotGC(caller, name)
function indirectCallCannotGC(fullCaller, fullVariable)
{
var caller = readable(fullCaller);
// This is usually a simple variable name, but sometimes a full name gets
// passed through. And sometimes that name is truncated. Examples:
// _ZL13gAbortHandler|mozalloc_oom.cpp:void (* gAbortHandler)(size_t)
// _ZL14pMutexUnlockFn|umutex.cpp:void (* pMutexUnlockFn)(const void*
var name = readable(fullVariable);
if (name in ignoreIndirectCalls)
return true;
@ -42,8 +50,7 @@ function indirectCallCannotGC(caller, name)
return true;
// template method called during marking and hence cannot GC
if (name == "op" &&
/^bool js::WeakMap<Key, Value, HashPolicy>::keyNeedsMark\(JSObject\*\)/.test(caller))
if (name == "op" && caller.indexOf("bool js::WeakMap<Key, Value, HashPolicy>::keyNeedsMark(JSObject*)") != -1)
{
return true;
}
@ -80,6 +87,7 @@ var ignoreCallees = {
"mozilla::CycleCollectedJSRuntime.NoteCustomGCThingXPCOMChildren" : true, // During tracing, cannot GC.
"nsIThreadManager.GetIsMainThread" : true,
"PLDHashTableOps.hashKey" : true,
"z_stream_s.zfree" : true,
};
function fieldCallCannotGC(csu, fullfield)
@ -91,16 +99,6 @@ function fieldCallCannotGC(csu, fullfield)
return false;
}
function shouldSuppressGC(name)
{
// Various dead code that should only be called inside AutoEnterAnalysis.
// Functions with no known caller are by default treated as not suppressing GC.
return /TypeScript::Purge/.test(name)
|| /StackTypeSet::addPropagateThis/.test(name)
|| /ScriptAnalysis::addPushedType/.test(name)
|| /IonBuilder/.test(name);
}
function ignoreEdgeUse(edge, variable)
{
// Functions which should not be treated as using variable.
@ -177,8 +175,11 @@ var ignoreFunctions = {
"void js::AutoCompartment::AutoCompartment(js::ExclusiveContext*, JSCompartment*)": true,
};
function ignoreGCFunction(fun)
function ignoreGCFunction(mangled)
{
assert(mangled in readableNames);
var fun = readableNames[mangled][0];
if (fun in ignoreFunctions)
return true;
@ -247,5 +248,9 @@ function isOverridableField(csu, field)
return false;
if (field == 'IsOnCurrentThread')
return false;
if (field == 'GetNativeContext')
return false;
if (field == 'GetThreadFromPRThread')
return false;
return true;
}

View File

@ -123,12 +123,7 @@ function getCallees(edge)
var callees = [];
if (callee.Kind == "Var") {
assert(callee.Variable.Kind == "Func");
var origName = callee.Variable.Name[0];
var names = [ origName, otherDestructorName(origName) ];
for (var name of names) {
if (name)
callees.push({'kind': 'direct', 'name': name});
}
callees.push({'kind': 'direct', 'name': callee.Variable.Name[0]});
} else {
assert(callee.Kind == "Drf");
if (callee.Exp[0].Kind == "Fld") {

View File

@ -23,17 +23,21 @@ printErr("Writing " + gcFunctions_filename);
redirect(gcFunctions_filename);
for (var name in gcFunctions) {
print("");
print("GC Function: " + name);
print("GC Function: " + name + "|" + readableNames[name][0]);
do {
name = gcFunctions[name];
print(" " + name);
if (name in readableNames)
print(" " + readableNames[name][0]);
else
print(" " + name);
} while (name in gcFunctions);
}
printErr("Writing " + gcFunctionsList_filename);
redirect(gcFunctionsList_filename);
for (var name in gcFunctions) {
print(name);
for (var readable of readableNames[name])
print(name + "|" + readable);
}
// gcEdges is a list of edges that can GC for more specific reasons than just

View File

@ -1,3 +1,3 @@
{
"expect-hazards": 15
"expect-hazards": 3
}

View File

@ -2,11 +2,38 @@
"use strict";
var calleeGraph = {};
var callerGraph = {};
var gcFunctions = {};
loadRelativeToScript('utility.js');
// Functions come out of sixgill in the form "mangled|readable". The mangled
// name is Truth. One mangled name might correspond to multiple readable names,
// for multiple reasons, including (1) sixgill/gcc doesn't always qualify types
// the same way or de-typedef the same amount; (2) sixgill's output treats
// references and pointers the same, and so doesn't distinguish them, but C++
// treats them as separate for overloading and linking; (3) (identical)
// destructors sometimes have an int32 parameter, sometimes not.
//
// The readable names are useful because they're far more meaningful to the
// user, and are what should show up in reports and questions to mrgiggles. At
// least in most cases, it's fine to have the extra mangled name tacked onto
// the beginning for these.
//
// The strategy used is to separate out the pieces whenever they are read in,
// create a table mapping mangled names to (one of the) readable names, and
// use the mangled names in all computation.
//
// Note that callgraph.txt uses a compressed representation -- each name is
// mapped to an integer, and those integers are what is recorded in the edges.
// But the integers depend on the full name, whereas the true edge should only
// consider the mangled name. And some of the names encoded in callgraph.txt
// are FieldCalls, not just function names.
var readableNames = {}; // map from mangled name => list of readable names
var mangledName = {}; // map from demangled names => mangled names. Could be eliminated.
var calleeGraph = {}; // map from mangled => list of tuples of {'callee':mangled, 'suppressed':bool}
var callerGraph = {}; // map from mangled => list of tuples of {'caller':mangled, 'suppressed':bool}
var gcFunctions = {}; // map from mangled callee => reason
var suppressedFunctions = {}; // set of mangled names (map from mangled name => true)
var gcEdges = {};
var suppressedFunctions = {};
function addGCFunction(caller, reason)
{
@ -35,8 +62,13 @@ function addCallEdge(caller, callee, suppressed)
callerGraph[callee].push({caller:caller, suppressed:suppressed});
}
// Map from identifier to full "mangled|readable" name. Or sometimes to a
// Class.Field name.
var functionNames = [""];
// Map from identifier to mangled name (or to a Class.Field)
var idToMangled = [""];
function loadCallgraph(file)
{
var suppressedFieldCalls = {};
@ -45,37 +77,45 @@ function loadCallgraph(file)
var textLines = snarf(file).split('\n');
for (var line of textLines) {
var match;
if (match = /^\#(\d+) (.*)/.exec(line)) {
if (match = line.charAt(0) == "#" && /^\#(\d+) (.*)/.exec(line)) {
assert(functionNames.length == match[1]);
functionNames.push(match[2]);
var [ mangled, readable ] = splitFunction(match[2]);
if (mangled in readableNames)
readableNames[mangled].push(readable);
else
readableNames[mangled] = [ readable ];
mangledName[readable] = mangled;
idToMangled.push(mangled);
continue;
}
var suppressed = false;
if (/SUPPRESS_GC/.test(line)) {
if (line.indexOf("SUPPRESS_GC") != -1) {
match = /^(..)SUPPRESS_GC (.*)/.exec(line);
line = match[1] + match[2];
suppressed = true;
}
if (match = /^I (\d+) VARIABLE ([^\,]*)/.exec(line)) {
var caller = functionNames[match[1]];
var tag = line.charAt(0);
if (match = tag == 'I' && /^I (\d+) VARIABLE ([^\,]*)/.exec(line)) {
var mangledCaller = idToMangled[match[1]];
var name = match[2];
if (!indirectCallCannotGC(caller, name) && !suppressed)
addGCFunction(caller, "IndirectCall: " + name);
} else if (match = /^F (\d+) CLASS (.*?) FIELD (.*)/.exec(line)) {
var caller = functionNames[match[1]];
if (!indirectCallCannotGC(functionNames[match[1]], name) && !suppressed)
addGCFunction(mangledCaller, "IndirectCall: " + name);
} else if (match = tag == 'F' && /^F (\d+) CLASS (.*?) FIELD (.*)/.exec(line)) {
var caller = idToMangled[match[1]];
var csu = match[2];
var fullfield = csu + "." + match[3];
if (suppressed)
suppressedFieldCalls[fullfield] = true;
else if (!fieldCallCannotGC(csu, fullfield))
addGCFunction(caller, "FieldCall: " + fullfield);
} else if (match = /^D (\d+) (\d+)/.exec(line)) {
var caller = functionNames[match[1]];
var callee = functionNames[match[2]];
} else if (match = tag == 'D' && /^D (\d+) (\d+)/.exec(line)) {
var caller = idToMangled[match[1]];
var callee = idToMangled[match[2]];
addCallEdge(caller, callee, suppressed);
} else if (match = /^R (\d+) (\d+)/.exec(line)) {
var callerField = functionNames[match[1]];
var callee = functionNames[match[2]];
} else if (match = tag == 'R' && /^R (\d+) (\d+)/.exec(line)) {
var callerField = idToMangled[match[1]];
var callee = idToMangled[match[2]];
addCallEdge(callerField, callee, false);
resolvedFunctions[callerField] = true;
}
@ -99,8 +139,6 @@ function loadCallgraph(file)
var top = worklist.length;
while (top > 0) {
name = worklist[--top];
if (shouldSuppressGC(name))
continue;
if (!(name in suppressedFunctions))
continue;
delete suppressedFunctions[name];
@ -125,8 +163,8 @@ function loadCallgraph(file)
for (var gcName of [ 'jsgc.cpp:void Collect(JSRuntime*, uint8, int64, uint32, uint32)',
'void js::MinorGC(JSRuntime*, uint32)' ])
{
assert(gcName in callerGraph);
addGCFunction(gcName, "GC");
assert(gcName in mangledName);
addGCFunction(mangledName[gcName], "GC");
}
// Initialize the worklist to all known gcFunctions.

View File

@ -233,8 +233,10 @@ sub run_build
print CONFIG "$prefix_dir\n";
print CONFIG Cwd::abs_path("$result_dir/build_xgill.log")."\n";
print CONFIG "$address\n";
print CONFIG "-fplugin-arg-xgill-annfile=$ann_file\n"
if ($ann_file ne "" && -e $ann_file);
my @extra = ("-fplugin-arg-xgill-mangle=1");
push(@extra, "-fplugin-arg-xgill-annfile=$ann_file")
if ($ann_file ne "" && -e $ann_file);
print CONFIG join(" ", @extra) . "\n";
close(CONFIG);
# Tell the wrapper where to find the config

View File

@ -103,21 +103,30 @@ function getSuccessors(body)
return body.successors;
}
function otherDestructorName(name)
// Split apart a function from sixgill into its mangled and unmangled name. If
// no mangled name was given, use the unmangled name as its mangled name
function splitFunction(func)
{
// gcc's information for destructors can be pretty messed up. Some functions
// have destructors with no arguments, some have destructors with an int32
// argument, some have both, and which one matches what the programmer wrote
// is anyone's guess. Work around this by treating calls to one destructor
// form as a call to both destructor forms.
if (!/::~/.test(name))
return null;
var split = func.indexOf("|");
if (split == -1)
return [ func, func ];
return [ func.substr(0, split), func.substr(split+1) ];
}
if (/\(int32\)/.test(name))
return name.replace("(int32)","()");
if (/\(\)/.test(name))
return name.replace("()","(int32)");
return null;
function mangled(fullname)
{
var split = fullname.indexOf("|");
if (split == -1)
return fullname;
return fullname.substr(0, split);
}
function readable(fullname)
{
var split = fullname.indexOf("|");
if (split == -1)
return fullname;
return fullname.substr(split+1);
}
function xdbLibrary()

View File

@ -9235,6 +9235,7 @@ IonBuilder::jsop_setarg(uint32_t arg)
JS_ASSERT(script()->uninlineable() && !isInlineBuilder());
MSetFrameArgument *store = MSetFrameArgument::New(alloc(), arg, val);
modifiesFrameArguments_ = true;
current->add(store);
current->setArg(arg);
return true;

View File

@ -767,7 +767,13 @@ LinearScanAllocator::assign(LAllocation allocation)
}
}
if (reg && allocation.isMemory()) {
bool useAsCanonicalSpillSlot = allocation.isMemory();
// Only canonically spill argument values when frame arguments are not
// modified in the body.
if (mir->modifiesFrameArguments())
useAsCanonicalSpillSlot = allocation.isStackSlot();
if (reg && useAsCanonicalSpillSlot) {
if (reg->canonicalSpill()) {
JS_ASSERT(allocation == *reg->canonicalSpill());

View File

@ -127,6 +127,10 @@ class MIRGenerator
return asmJSGlobalAccesses_;
}
bool modifiesFrameArguments() const {
return modifiesFrameArguments_;
}
public:
CompileCompartment *compartment;
@ -146,6 +150,11 @@ class MIRGenerator
AsmJSGlobalAccessVector asmJSGlobalAccesses_;
uint32_t minAsmJSHeapLength_;
// Keep track of whether frame arguments are modified during execution.
// RegAlloc needs to know this as spilling values back to their register
// slots is not compatible with that.
bool modifiesFrameArguments_;
#if defined(JS_ION_PERF)
AsmJSPerfSpewer asmJSPerfSpewer_;

View File

@ -30,7 +30,8 @@ MIRGenerator::MIRGenerator(CompileCompartment *compartment,
performsAsmJSCall_(false),
asmJSHeapAccesses_(*alloc),
asmJSGlobalAccesses_(*alloc),
minAsmJSHeapLength_(AsmJSAllocationGranularity)
minAsmJSHeapLength_(AsmJSAllocationGranularity),
modifiesFrameArguments_(false)
{ }
bool

View File

@ -93,6 +93,8 @@ AsyncStatementParams::NewResolve(
bool *_retval
)
{
JS::Rooted<JSObject*> scopeObj(aCtx, aScopeObj);
NS_ENSURE_TRUE(mStatement, NS_ERROR_NOT_INITIALIZED);
// We do not throw at any point after this because we want to allow the
// prototype chain to be checked for the property.
@ -103,7 +105,7 @@ AsyncStatementParams::NewResolve(
uint32_t idx = JSID_TO_INT(aId);
// All indexes are good because we don't know how many parameters there
// really are.
ok = ::JS_DefineElement(aCtx, aScopeObj, idx, JSVAL_VOID, nullptr,
ok = ::JS_DefineElement(aCtx, scopeObj, idx, JSVAL_VOID, nullptr,
nullptr, 0);
resolved = true;
}
@ -111,13 +113,13 @@ AsyncStatementParams::NewResolve(
// We are unable to tell if there's a parameter with this name and so
// we must assume that there is. This screws the rest of the prototype
// chain, but people really shouldn't be depending on this anyways.
ok = ::JS_DefinePropertyById(aCtx, aScopeObj, aId, JSVAL_VOID, nullptr,
ok = ::JS_DefinePropertyById(aCtx, scopeObj, aId, JSVAL_VOID, nullptr,
nullptr, 0);
resolved = true;
}
*_retval = ok;
*_objp = resolved && ok ? aScopeObj : nullptr;
*_objp = resolved && ok ? scopeObj.get() : nullptr;
return NS_OK;
}

View File

@ -123,6 +123,8 @@ StatementRow::NewResolve(nsIXPConnectWrappedNative *aWrapper,
JSObject **_objp,
bool *_retval)
{
JS::Rooted<JSObject*> scopeObj(aCtx, aScopeObj);
NS_ENSURE_TRUE(mStatement, NS_ERROR_NOT_INITIALIZED);
// We do not throw at any point after this because we want to allow the
// prototype chain to be checked for the property.
@ -142,9 +144,9 @@ StatementRow::NewResolve(nsIXPConnectWrappedNative *aWrapper,
return NS_OK;
}
*_retval = ::JS_DefinePropertyById(aCtx, aScopeObj, aId, JSVAL_VOID,
*_retval = ::JS_DefinePropertyById(aCtx, scopeObj, aId, JSVAL_VOID,
nullptr, nullptr, 0);
*_objp = aScopeObj;
*_objp = scopeObj;
return NS_OK;
}