Merge mozilla-inbound to mozilla-central. a=merge

This commit is contained in:
Daniel Varga 2019-02-09 23:47:19 +02:00
commit db72fbd65d
54 changed files with 806 additions and 945 deletions

View File

@ -28,23 +28,23 @@ const CLASSES = new WeakMap();
* @param {Inspector} inspector
* The current inspector instance.
*/
function ClassList(inspector) {
EventEmitter.decorate(this);
class ClassList {
constructor(inspector) {
EventEmitter.decorate(this);
this.inspector = inspector;
this.inspector = inspector;
this.onMutations = this.onMutations.bind(this);
this.inspector.on("markupmutation", this.onMutations);
this.onMutations = this.onMutations.bind(this);
this.inspector.on("markupmutation", this.onMutations);
this.classListProxyNode = this.inspector.panelDoc.createElement("div");
}
this.classListProxyNode = this.inspector.panelDoc.createElement("div");
}
ClassList.prototype = {
destroy() {
this.inspector.off("markupmutation", this.onMutations);
this.inspector = null;
this.classListProxyNode = null;
},
}
/**
* The current node selection (which only returns if the node is an ELEMENT_NODE type
@ -56,7 +56,7 @@ ClassList.prototype = {
return this.inspector.selection.nodeFront;
}
return null;
},
}
/**
* The class states for the current node selection. See the documentation of the CLASSES
@ -79,7 +79,7 @@ ClassList.prototype = {
}
return CLASSES.get(this.currentNode);
},
}
/**
* Same as currentClasses, but returns it in the form of a className string, where only
@ -89,7 +89,7 @@ ClassList.prototype = {
return this.currentClasses.filter(({ isApplied }) => isApplied)
.map(({ name }) => name)
.join(" ");
},
}
/**
* Set the state for a given class on the current node.
@ -106,7 +106,7 @@ ClassList.prototype = {
nodeClasses.find(({ name: cName }) => cName === name).isApplied = isApplied;
return this.applyClassState();
},
}
/**
* Add several classes to the current node at once.
@ -120,7 +120,7 @@ ClassList.prototype = {
return Promise.all([...new Set([...this.classListProxyNode.classList])].map(name => {
return this.addClass(name);
}));
},
}
/**
* Add a class to the current node at once.
@ -139,7 +139,7 @@ ClassList.prototype = {
this.currentClasses.push({ name, isApplied: true });
return this.applyClassState();
},
}
/**
* Used internally by other functions like addClass or setClassState. Actually applies
@ -165,7 +165,7 @@ ClassList.prototype = {
const mod = this.currentNode.startModifyingAttributes();
mod.setAttribute("class", this.currentClassesPreview);
return mod.apply();
},
}
onMutations(mutations) {
for (const {type, target, attributeName} of mutations) {
@ -185,7 +185,7 @@ ClassList.prototype = {
}
}
}
},
};
}
}
module.exports = ClassList;

View File

@ -19,50 +19,51 @@ loader.lazyRequireGetter(this, "isCssVariable", "devtools/shared/fronts/css-prop
* ElementStyle is responsible for the following:
* Keeps track of which properties are overridden.
* Maintains a list of Rule objects for a given element.
*
* @param {Element} element
* The element whose style we are viewing.
* @param {CssRuleView} ruleView
* The instance of the rule-view panel.
* @param {Object} store
* The ElementStyle can use this object to store metadata
* that might outlast the rule view, particularly the current
* set of disabled properties.
* @param {PageStyleFront} pageStyle
* Front for the page style actor that will be providing
* the style information.
* @param {Boolean} showUserAgentStyles
* Should user agent styles be inspected?
*/
function ElementStyle(element, ruleView, store, pageStyle, showUserAgentStyles) {
this.element = element;
this.ruleView = ruleView;
this.store = store || {};
this.pageStyle = pageStyle;
this.showUserAgentStyles = showUserAgentStyles;
this.rules = [];
this.cssProperties = this.ruleView.cssProperties;
this.variables = new Map();
class ElementStyle {
/**
* @param {Element} element
* The element whose style we are viewing.
* @param {CssRuleView} ruleView
* The instance of the rule-view panel.
* @param {Object} store
* The ElementStyle can use this object to store metadata
* that might outlast the rule view, particularly the current
* set of disabled properties.
* @param {PageStyleFront} pageStyle
* Front for the page style actor that will be providing
* the style information.
* @param {Boolean} showUserAgentStyles
* Should user agent styles be inspected?
*/
constructor(element, ruleView, store, pageStyle, showUserAgentStyles) {
this.element = element;
this.ruleView = ruleView;
this.store = store || {};
this.pageStyle = pageStyle;
this.showUserAgentStyles = showUserAgentStyles;
this.rules = [];
this.cssProperties = this.ruleView.cssProperties;
this.variables = new Map();
// We don't want to overwrite this.store.userProperties so we only create it
// if it doesn't already exist.
if (!("userProperties" in this.store)) {
this.store.userProperties = new UserProperties();
// We don't want to overwrite this.store.userProperties so we only create it
// if it doesn't already exist.
if (!("userProperties" in this.store)) {
this.store.userProperties = new UserProperties();
}
if (!("disabled" in this.store)) {
this.store.disabled = new WeakMap();
}
this.onStyleSheetUpdated = this.onStyleSheetUpdated.bind(this);
if (this.ruleView.isNewRulesView) {
this.pageStyle.on("stylesheet-updated", this.onStyleSheetUpdated);
}
}
if (!("disabled" in this.store)) {
this.store.disabled = new WeakMap();
}
this.onStyleSheetUpdated = this.onStyleSheetUpdated.bind(this);
if (this.ruleView.isNewRulesView) {
this.pageStyle.on("stylesheet-updated", this.onStyleSheetUpdated);
}
}
ElementStyle.prototype = {
destroy: function() {
destroy() {
if (this.destroyed) {
return;
}
@ -78,17 +79,17 @@ ElementStyle.prototype = {
if (this.ruleView.isNewRulesView) {
this.pageStyle.off("stylesheet-updated", this.onStyleSheetUpdated);
}
},
}
/**
* Called by the Rule object when it has been changed through the
* setProperty* methods.
*/
_changed: function() {
_changed() {
if (this.onChanged) {
this.onChanged();
}
},
}
/**
* Refresh the list of rules to be displayed for the active element.
@ -97,7 +98,7 @@ ElementStyle.prototype = {
* Returns a promise that will be resolved when the elementStyle is
* ready.
*/
populate: function() {
populate() {
const populated = this.pageStyle.getApplied(this.element, {
inherited: true,
matchedSelectors: true,
@ -140,7 +141,7 @@ ElementStyle.prototype = {
});
this.populated = populated;
return this.populated;
},
}
/**
* Returns the Rule object of the given rule id.
@ -149,9 +150,9 @@ ElementStyle.prototype = {
* The id of the Rule object.
* @return {Rule|undefined} of the given rule id or undefined if it cannot be found.
*/
getRule: function(id) {
getRule(id) {
return this.rules.find(rule => rule.domRule.actorID === id);
},
}
/**
* Get the font families in use by the element.
@ -159,7 +160,7 @@ ElementStyle.prototype = {
* Returns a promise that will be resolved to a list of CSS family
* names. The list might have duplicates.
*/
getUsedFontFamilies: function() {
getUsedFontFamilies() {
return new Promise((resolve, reject) => {
this.ruleView.styleWindow.requestIdleCallback(async () => {
try {
@ -171,16 +172,16 @@ ElementStyle.prototype = {
}
});
});
},
}
/**
* Put pseudo elements in front of others.
*/
_sortRulesForPseudoElement: function() {
_sortRulesForPseudoElement() {
this.rules = this.rules.sort((a, b) => {
return (a.pseudoElement || "z") > (b.pseudoElement || "z");
});
},
}
/**
* Add a rule if it's one we care about. Filters out duplicates and
@ -193,7 +194,7 @@ ElementStyle.prototype = {
* it will be deleted from this array.
* @return {Boolean} true if we added the rule.
*/
_maybeAddRule: function(options, existingRules) {
_maybeAddRule(options, existingRules) {
// If we've already included this domRule (for example, when a
// common selector is inherited), ignore it.
if (options.system ||
@ -226,19 +227,19 @@ ElementStyle.prototype = {
this.rules.push(rule);
return true;
},
}
/**
* Calls markOverridden with all supported pseudo elements
*/
markOverriddenAll: function() {
markOverriddenAll() {
this.variables.clear();
this.markOverridden();
for (const pseudo of this.cssProperties.pseudoElements) {
this.markOverridden(pseudo);
}
},
}
/**
* Mark the properties listed in this.rules for a given pseudo element
@ -248,7 +249,7 @@ ElementStyle.prototype = {
* Which pseudo element to flag as overridden.
* Empty string or undefined will default to no pseudo element.
*/
markOverridden: function(pseudo = "") {
markOverridden(pseudo = "") {
// Gather all the text properties applied by these rules, ordered
// from more- to less-specific. Text properties from keyframes rule are
// excluded from being marked as overridden since a number of criteria such
@ -343,7 +344,7 @@ ElementStyle.prototype = {
textProp.updateEditor();
}
}
},
}
/**
* Adds a new declaration to the rule.
@ -353,7 +354,7 @@ ElementStyle.prototype = {
* @param {String} value
* The new declaration value.
*/
addNewDeclaration: function(ruleId, value) {
addNewDeclaration(ruleId, value) {
const rule = this.getRule(ruleId);
if (!rule) {
return;
@ -366,7 +367,7 @@ ElementStyle.prototype = {
}
this._addMultipleDeclarations(rule, declarationsToAdd);
},
}
/**
* Adds a new rule. The rules view is updated from a "stylesheet-updated" event
@ -375,7 +376,7 @@ ElementStyle.prototype = {
*/
async addNewRule() {
await this.pageStyle.addNewRule(this.element, this.element.pseudoClassLocks);
},
}
/**
* Given the id of the rule and the new declaration name, modifies the existing
@ -388,7 +389,7 @@ ElementStyle.prototype = {
* @param {String} name
* The new declaration name.
*/
modifyDeclarationName: async function(ruleID, declarationId, name) {
async modifyDeclarationName(ruleID, declarationId, name) {
const rule = this.getRule(ruleID);
if (!rule) {
return;
@ -411,7 +412,7 @@ ElementStyle.prototype = {
if (!declaration.enabled) {
await declaration.setEnabled(true);
}
},
}
/**
* Helper function to addNewDeclaration() and modifyDeclarationValue() for
@ -424,14 +425,14 @@ ElementStyle.prototype = {
* @param {TextProperty|null} siblingDeclaration
* Optional declaration next to which the new declaration will be added.
*/
_addMultipleDeclarations: function(rule, declarationsToAdd, siblingDeclaration = null) {
_addMultipleDeclarations(rule, declarationsToAdd, siblingDeclaration = null) {
for (const { commentOffsets, name, value, priority } of declarationsToAdd) {
const isCommented = Boolean(commentOffsets);
const enabled = !isCommented;
siblingDeclaration = rule.createProperty(name, value, priority, enabled,
siblingDeclaration);
}
},
}
/**
* Parse a value string and break it into pieces, starting with the
@ -448,7 +449,7 @@ ElementStyle.prototype = {
* declarationsToAdd: An array with additional declarations, following the
* parseDeclarations format of { name, value, priority }
*/
_getValueAndExtraProperties: function(value) {
_getValueAndExtraProperties(value) {
// The inplace editor will prevent manual typing of multiple declarations,
// but we need to deal with the case during a paste event.
// Adding multiple declarations inside of value editor sets value with the
@ -478,7 +479,7 @@ ElementStyle.prototype = {
declarationsToAdd,
firstValue,
};
},
}
/**
* Given the id of the rule and the new declaration value, modifies the existing
@ -491,7 +492,7 @@ ElementStyle.prototype = {
* @param {String} value
* The new declaration value.
*/
modifyDeclarationValue: async function(ruleId, declarationId, value) {
async modifyDeclarationValue(ruleId, declarationId, value) {
const rule = this.getRule(ruleId);
if (!rule) {
return;
@ -519,7 +520,7 @@ ElementStyle.prototype = {
}
this._addMultipleDeclarations(rule, declarationsToAdd, declaration);
},
}
/**
* Modifies the existing rule's selector to the new given value.
@ -529,7 +530,7 @@ ElementStyle.prototype = {
* @param {String} selector
* The new selector value.
*/
modifySelector: async function(ruleId, selector) {
async modifySelector(ruleId, selector) {
try {
const rule = this.getRule(ruleId);
if (!rule) {
@ -586,7 +587,7 @@ ElementStyle.prototype = {
} catch (e) {
console.error(e);
}
},
}
/**
* Toggles the enabled state of the given CSS declaration.
@ -596,7 +597,7 @@ ElementStyle.prototype = {
* @param {String} declarationId
* The TextProperty id for the CSS declaration.
*/
toggleDeclaration: function(ruleId, declarationId) {
toggleDeclaration(ruleId, declarationId) {
const rule = this.getRule(ruleId);
if (!rule) {
return;
@ -608,7 +609,7 @@ ElementStyle.prototype = {
}
declaration.setEnabled(!declaration.enabled);
},
}
/**
* Mark a given TextProperty as overridden or not depending on the
@ -620,7 +621,7 @@ ElementStyle.prototype = {
* @return {Boolean} true if the TextProperty's overridden state (or any of
* its computed properties overridden state) changed.
*/
_updatePropertyOverridden: function(prop) {
_updatePropertyOverridden(prop) {
let overridden = true;
let dirty = false;
@ -636,7 +637,7 @@ ElementStyle.prototype = {
dirty = (!!prop.overridden !== overridden) || dirty;
prop.overridden = overridden;
return dirty;
},
}
/**
* Returns the current value of a CSS variable; or null if the
@ -647,15 +648,15 @@ ElementStyle.prototype = {
* @return {String} the variable's value or null if the variable is
* not defined.
*/
getVariable: function(name) {
getVariable(name) {
return this.variables.get(name);
},
}
/**
* Handler for page style events "stylesheet-updated". Refreshes the list of rules on
* the page.
*/
onStyleSheetUpdated: async function() {
async onStyleSheetUpdated() {
// Repopulate the element style once the current modifications are done.
const promises = [];
for (const rule of this.rules) {
@ -667,7 +668,7 @@ ElementStyle.prototype = {
await Promise.all(promises);
await this.populate();
this._changed();
},
};
}
}
module.exports = ElementStyle;

View File

@ -24,49 +24,44 @@ const STYLE_INSPECTOR_L10N = new LocalizationHelper(STYLE_INSPECTOR_PROPERTIES);
* Manages a single style declaration or rule.
* Applies changes to the properties in a rule.
* Maintains a list of TextProperty objects.
*
* @param {ElementStyle} elementStyle
* The ElementStyle to which this rule belongs.
* @param {Object} options
* The information used to construct this rule. Properties include:
* rule: A StyleRuleActor
* inherited: An element this rule was inherited from. If omitted,
* the rule applies directly to the current element.
* isSystem: Is this a user agent style?
* isUnmatched: True if the rule does not match the current selected
* element, otherwise, false.
*/
function Rule(elementStyle, options) {
this.elementStyle = elementStyle;
this.domRule = options.rule;
this.matchedSelectors = options.matchedSelectors || [];
this.pseudoElement = options.pseudoElement || "";
class Rule {
/**
* @param {ElementStyle} elementStyle
* The ElementStyle to which this rule belongs.
* @param {Object} options
* The information used to construct this rule. Properties include:
* rule: A StyleRuleActor
* inherited: An element this rule was inherited from. If omitted,
* the rule applies directly to the current element.
* isSystem: Is this a user agent style?
* isUnmatched: True if the rule does not match the current selected
* element, otherwise, false.
*/
constructor(elementStyle, options) {
this.elementStyle = elementStyle;
this.domRule = options.rule;
this.matchedSelectors = options.matchedSelectors || [];
this.pseudoElement = options.pseudoElement || "";
this.isSystem = options.isSystem;
this.isUnmatched = options.isUnmatched || false;
this.inherited = options.inherited || null;
this.keyframes = options.keyframes || null;
this.isSystem = options.isSystem;
this.isUnmatched = options.isUnmatched || false;
this.inherited = options.inherited || null;
this.keyframes = options.keyframes || null;
this.mediaText = this.domRule && this.domRule.mediaText ? this.domRule.mediaText : "";
this.cssProperties = this.elementStyle.ruleView.cssProperties;
if (this.domRule && this.domRule.mediaText) {
this.mediaText = this.domRule.mediaText;
// Populate the text properties with the style's current authoredText
// value, and add in any disabled properties from the store.
this.textProps = this._getTextProperties();
this.textProps = this.textProps.concat(this._getDisabledProperties());
this.getUniqueSelector = this.getUniqueSelector.bind(this);
}
this.cssProperties = this.elementStyle.ruleView.cssProperties;
// Populate the text properties with the style's current authoredText
// value, and add in any disabled properties from the store.
this.textProps = this._getTextProperties();
this.textProps = this.textProps.concat(this._getDisabledProperties());
this.getUniqueSelector = this.getUniqueSelector.bind(this);
}
Rule.prototype = {
mediaText: "",
get declarations() {
return this.textProps;
},
}
get inheritance() {
if (!this.inherited) {
@ -77,7 +72,7 @@ Rule.prototype = {
inherited: this.inherited,
inheritedSource: this.inheritedSource,
};
},
}
get selector() {
return {
@ -86,7 +81,7 @@ Rule.prototype = {
selectors: this.domRule.selectors,
selectorText: this.keyframes ? this.domRule.keyText : this.selectorText,
};
},
}
get sourceLink() {
return {
@ -95,7 +90,7 @@ Rule.prototype = {
mediaText: this.mediaText,
title: this.title,
};
},
}
get title() {
let title = CssLogic.shortSource(this.sheet);
@ -104,7 +99,7 @@ Rule.prototype = {
}
return title + (this.mediaText ? " @media " + this.mediaText : "");
},
}
get inheritedSource() {
if (this._inheritedSource) {
@ -120,7 +115,7 @@ Rule.prototype = {
STYLE_INSPECTOR_L10N.getFormatStr("rule.inheritedFrom", eltText);
}
return this._inheritedSource;
},
}
get keyframesName() {
if (this._keyframesName) {
@ -132,7 +127,7 @@ Rule.prototype = {
STYLE_INSPECTOR_L10N.getFormatStr("rule.keyframe", this.keyframes.name);
}
return this._keyframesName;
},
}
get keyframesRule() {
if (!this.keyframes) {
@ -143,33 +138,33 @@ Rule.prototype = {
id: this.keyframes.actorID,
keyframesName: this.keyframesName,
};
},
}
get selectorText() {
return this.domRule.selectors ? this.domRule.selectors.join(", ") :
CssLogic.l10n("rule.sourceElement");
},
}
/**
* The rule's stylesheet.
*/
get sheet() {
return this.domRule ? this.domRule.parentStyleSheet : null;
},
}
/**
* The rule's line within a stylesheet
*/
get ruleLine() {
return this.domRule ? this.domRule.line : -1;
},
}
/**
* The rule's column within a stylesheet
*/
get ruleColumn() {
return this.domRule ? this.domRule.column : null;
},
}
/**
* Returns the TextProperty with the given id or undefined if it cannot be found.
@ -179,9 +174,9 @@ Rule.prototype = {
* @return {TextProperty|undefined} with the given id in the current Rule or undefined
* if it cannot be found.
*/
getDeclaration: function(id) {
getDeclaration(id) {
return this.textProps.find(textProp => textProp.id === id);
},
}
/**
* Returns an unique selector for the CSS rule.
@ -202,7 +197,7 @@ Rule.prototype = {
}
return selector;
},
}
/**
* Returns true if the rule matches the creation options
@ -211,9 +206,9 @@ Rule.prototype = {
* @param {Object} options
* Creation options. See the Rule constructor for documentation.
*/
matches: function(options) {
matches(options) {
return this.domRule === options.rule;
},
}
/**
* Create a new TextProperty to include in the rule.
@ -229,7 +224,7 @@ Rule.prototype = {
* @param {TextProperty} siblingProp
* Optional, property next to which the new property will be added.
*/
createProperty: function(name, value, priority, enabled, siblingProp) {
createProperty(name, value, priority, enabled, siblingProp) {
const prop = new TextProperty(this, name, value, priority, enabled);
let ind;
@ -249,14 +244,14 @@ Rule.prototype = {
});
return prop;
},
}
/**
* Helper function for applyProperties that is called when the actor
* does not support as-authored styles. Store disabled properties
* in the element style's store.
*/
_applyPropertiesNoAuthored: function(modifications) {
_applyPropertiesNoAuthored(modifications) {
this.elementStyle.markOverriddenAll();
const disabledProps = [];
@ -317,14 +312,14 @@ Rule.prototype = {
textProp.priority = cssProp.priority;
}
});
},
}
/**
* A helper for applyProperties that applies properties in the "as
* authored" case; that is, when the StyleRuleActor supports
* setRuleText.
*/
_applyPropertiesAuthored: function(modifications) {
_applyPropertiesAuthored(modifications) {
return modifications.apply().then(() => {
// The rewriting may have required some other property values to
// change, e.g., to insert some needed terminators. Update the
@ -341,7 +336,7 @@ Rule.prototype = {
}
}
});
},
}
/**
* Reapply all the properties in this rule, and update their
@ -355,7 +350,7 @@ Rule.prototype = {
* @return {Promise} a promise which will resolve when the edit
* is complete
*/
applyProperties: function(modifier) {
applyProperties(modifier) {
// If there is already a pending modification, we have to wait
// until it settles before applying the next modification.
const resultPromise =
@ -378,7 +373,7 @@ Rule.prototype = {
this._applyingModifications = resultPromise;
return resultPromise;
},
}
/**
* Renames a property.
@ -389,7 +384,7 @@ Rule.prototype = {
* The new property name (such as "background" or "border-top").
* @return {Promise}
*/
setPropertyName: function(property, name) {
setPropertyName(property, name) {
if (name === property.name) {
return Promise.resolve();
}
@ -400,7 +395,7 @@ Rule.prototype = {
return this.applyProperties(modifications => {
modifications.renameProperty(index, oldName, name);
});
},
}
/**
* Sets the value and priority of a property, then reapply all properties.
@ -413,7 +408,7 @@ Rule.prototype = {
* The property's priority (either "important" or an empty string).
* @return {Promise}
*/
setPropertyValue: function(property, value, priority) {
setPropertyValue(property, value, priority) {
if (value === property.value && priority === property.priority) {
return Promise.resolve();
}
@ -425,7 +420,7 @@ Rule.prototype = {
return this.applyProperties(modifications => {
modifications.setProperty(index, property.name, value, priority);
});
},
}
/**
* Just sets the value and priority of a property, in order to preview its
@ -439,7 +434,7 @@ Rule.prototype = {
* The property's priority (either "important" or an empty string).
**@return {Promise}
*/
previewPropertyValue: function(property, value, priority) {
previewPropertyValue(property, value, priority) {
const modifications = this.domRule.startModifyingProperties(this.cssProperties);
modifications.setProperty(this.textProps.indexOf(property),
property.name, value, priority);
@ -448,7 +443,7 @@ Rule.prototype = {
// also for previews
this.elementStyle._changed();
});
},
}
/**
* Disables or enables given TextProperty.
@ -457,7 +452,7 @@ Rule.prototype = {
* The property to enable/disable
* @param {Boolean} value
*/
setPropertyEnabled: function(property, value) {
setPropertyEnabled(property, value) {
if (property.enabled === !!value) {
return;
}
@ -466,7 +461,7 @@ Rule.prototype = {
this.applyProperties((modifications) => {
modifications.setPropertyEnabled(index, property.name, property.enabled);
});
},
}
/**
* Remove a given TextProperty from the rule and update the rule
@ -475,7 +470,7 @@ Rule.prototype = {
* @param {TextProperty} property
* The property to be removed
*/
removeProperty: function(property) {
removeProperty(property) {
const index = this.textProps.indexOf(property);
this.textProps.splice(index, 1);
// Need to re-apply properties in case removing this TextProperty
@ -483,13 +478,13 @@ Rule.prototype = {
this.applyProperties((modifications) => {
modifications.removeProperty(index, property.name);
});
},
}
/**
* Get the list of TextProperties from the style. Needs
* to parse the style's authoredText.
*/
_getTextProperties: function() {
_getTextProperties() {
const textProps = [];
const store = this.elementStyle.store;
@ -519,12 +514,12 @@ Rule.prototype = {
}
return textProps;
},
}
/**
* Return the list of disabled properties from the store for this rule.
*/
_getDisabledProperties: function() {
_getDisabledProperties() {
const store = this.elementStyle.store;
// Include properties from the disabled property store, if any.
@ -544,13 +539,13 @@ Rule.prototype = {
}
return textProps;
},
}
/**
* Reread the current state of the rules and rebuild text
* properties as needed.
*/
refresh: function(options) {
refresh(options) {
this.matchedSelectors = options.matchedSelectors || [];
const newTextProps = this._getTextProperties();
@ -602,7 +597,7 @@ Rule.prototype = {
if (this.editor) {
this.editor.populate();
}
},
}
/**
* Update the current TextProperties that match a given property
@ -626,7 +621,7 @@ Rule.prototype = {
* @return {Boolean} true if a property was updated, false if no properties
* were updated.
*/
_updateTextProperty: function(newProp) {
_updateTextProperty(newProp) {
const match = { rank: 0, prop: null };
for (const prop of this.textProps) {
@ -677,7 +672,7 @@ Rule.prototype = {
}
return false;
},
}
/**
* Jump between editable properties in the UI. If the focus direction is
@ -691,7 +686,7 @@ Rule.prototype = {
* @param {Number} direction
* The move focus direction number.
*/
editClosestTextProperty: function(textProperty, direction) {
editClosestTextProperty(textProperty, direction) {
let index = this.textProps.indexOf(textProperty);
if (direction === Services.focus.MOVEFOCUS_FORWARD) {
@ -717,12 +712,12 @@ Rule.prototype = {
this.textProps[index].editor.valueSpan.click();
}
}
},
}
/**
* Return a string representation of the rule.
*/
stringifyRule: function() {
stringifyRule() {
const selectorText = this.selectorText;
let cssText = "";
const terminator = Services.appinfo.OS === "WINNT" ? "\r\n" : "\n";
@ -734,21 +729,21 @@ Rule.prototype = {
}
return selectorText + " {" + terminator + cssText + "}";
},
}
/**
* See whether this rule has any non-invisible properties.
* @return {Boolean} true if there is any visible property, or false
* if all properties are invisible
*/
hasAnyVisibleProperties: function() {
hasAnyVisibleProperties() {
for (const prop of this.textProps) {
if (!prop.invisible) {
return true;
}
}
return false;
},
};
}
}
module.exports = Rule;

View File

@ -18,40 +18,40 @@ loader.lazyRequireGetter(this, "escapeCSSComment", "devtools/shared/css/parsing-
* property declaration.
* Changes to the TextProperty are sent to its related Rule for
* application.
*
* @param {Rule} rule
* The rule this TextProperty came from.
* @param {String} name
* The text property name (such as "background" or "border-top").
* @param {String} value
* The property's value (not including priority).
* @param {String} priority
* The property's priority (either "important" or an empty string).
* @param {Boolean} enabled
* Whether the property is enabled.
* @param {Boolean} invisible
* Whether the property is invisible. In an inherited rule, only show
* the inherited declarations. The other declarations are considered
* invisible and does not show up in the UI. These are needed so that
* the index of a property in Rule.textProps is the same as the index
* coming from parseDeclarations.
*/
function TextProperty(rule, name, value, priority, enabled = true,
invisible = false) {
this.id = name + "_" + generateUUID().toString();
this.rule = rule;
this.name = name;
this.value = value;
this.priority = priority;
this.enabled = !!enabled;
this.invisible = invisible;
this.cssProperties = this.rule.elementStyle.ruleView.cssProperties;
this.panelDoc = this.rule.elementStyle.ruleView.inspector.panelDoc;
class TextProperty {
/**
* @param {Rule} rule
* The rule this TextProperty came from.
* @param {String} name
* The text property name (such as "background" or "border-top").
* @param {String} value
* The property's value (not including priority).
* @param {String} priority
* The property's priority (either "important" or an empty string).
* @param {Boolean} enabled
* Whether the property is enabled.
* @param {Boolean} invisible
* Whether the property is invisible. In an inherited rule, only show
* the inherited declarations. The other declarations are considered
* invisible and does not show up in the UI. These are needed so that
* the index of a property in Rule.textProps is the same as the index
* coming from parseDeclarations.
*/
constructor(rule, name, value, priority, enabled = true, invisible = false) {
this.id = name + "_" + generateUUID().toString();
this.rule = rule;
this.name = name;
this.value = value;
this.priority = priority;
this.enabled = !!enabled;
this.invisible = invisible;
this.cssProperties = this.rule.elementStyle.ruleView.cssProperties;
this.panelDoc = this.rule.elementStyle.ruleView.inspector.panelDoc;
this.updateComputed();
}
this.updateComputed();
}
TextProperty.prototype = {
get computedProperties() {
return this.computed
.filter(computed => computed.name !== this.name)
@ -63,7 +63,7 @@ TextProperty.prototype = {
value: computed.value,
};
});
},
}
/**
* See whether this property's name is known.
@ -72,22 +72,22 @@ TextProperty.prototype = {
*/
get isKnownProperty() {
return this.cssProperties.isKnown(this.name);
},
}
/**
* Update the editor associated with this text property,
* if any.
*/
updateEditor: function() {
updateEditor() {
if (this.editor) {
this.editor.update();
}
},
}
/**
* Update the list of computed properties for this text property.
*/
updateComputed: function() {
updateComputed() {
if (!this.name) {
return;
}
@ -115,7 +115,7 @@ TextProperty.prototype = {
priority: dummyStyle.getPropertyPriority(prop),
});
}
},
}
/**
* Set all the values from another TextProperty instance into
@ -124,7 +124,7 @@ TextProperty.prototype = {
* @param {TextProperty} prop
* The other TextProperty instance.
*/
set: function(prop) {
set(prop) {
let changed = false;
for (const item of ["name", "value", "priority", "enabled"]) {
if (this[item] !== prop[item]) {
@ -136,9 +136,9 @@ TextProperty.prototype = {
if (changed) {
this.updateEditor();
}
},
}
setValue: function(value, priority, force = false) {
setValue(value, priority, force = false) {
const store = this.rule.elementStyle.store;
if (this.editor && value !== this.editor.committed.value || force) {
@ -147,7 +147,7 @@ TextProperty.prototype = {
return this.rule.setPropertyValue(this, value, priority)
.then(() => this.updateEditor());
},
}
/**
* Called when the property's value has been updated externally, and
@ -156,14 +156,14 @@ TextProperty.prototype = {
* @param {String} value
* Property value
*/
updateValue: function(value) {
updateValue(value) {
if (value !== this.value) {
this.value = value;
this.updateEditor();
}
},
}
setName: async function(name) {
async setName(name) {
if (name !== this.name && this.editor) {
const store = this.rule.elementStyle.store;
store.userProperties.setProperty(this.rule.domRule, name,
@ -172,21 +172,21 @@ TextProperty.prototype = {
await this.rule.setPropertyName(this, name);
this.updateEditor();
},
}
setEnabled: function(value) {
setEnabled(value) {
this.rule.setPropertyEnabled(this, value);
this.updateEditor();
},
}
remove: function() {
remove() {
this.rule.removeProperty(this);
},
}
/**
* Return a string representation of the rule property.
*/
stringifyProperty: function() {
stringifyProperty() {
// Get the displayed property value
let declaration = this.name + ": " + this.editor.valueSpan.textContent +
";";
@ -197,7 +197,7 @@ TextProperty.prototype = {
}
return declaration;
},
}
/**
* Validate this property. Does it make sense for this value to be assigned
@ -205,7 +205,7 @@ TextProperty.prototype = {
*
* @return {Boolean} true if the whole CSS declaration is valid, false otherwise.
*/
isValid: function() {
isValid() {
const selfIndex = this.rule.textProps.indexOf(this);
// When adding a new property in the rule-view, the TextProperty object is
@ -217,14 +217,14 @@ TextProperty.prototype = {
}
return this.rule.domRule.declarations[selfIndex].isValid;
},
}
/**
* Validate the name of this property.
*
* @return {Boolean} true if the property name is valid, false otherwise.
*/
isNameValid: function() {
isNameValid() {
const selfIndex = this.rule.textProps.indexOf(this);
// When adding a new property in the rule-view, the TextProperty object is
@ -241,7 +241,7 @@ TextProperty.prototype = {
return (this.rule.domRule.declarations[selfIndex].isNameValid !== undefined)
? this.rule.domRule.declarations[selfIndex].isNameValid
: true;
},
};
}
}
module.exports = TextProperty;

View File

@ -8,11 +8,11 @@
* Store of CSSStyleDeclarations mapped to properties that have been changed by
* the user.
*/
function UserProperties() {
this.map = new Map();
}
class UserProperties {
constructor() {
this.map = new Map();
}
UserProperties.prototype = {
/**
* Get a named property for a given CSSStyleDeclaration.
*
@ -26,7 +26,7 @@ UserProperties.prototype = {
* The property value if it has previously been set by the user, null
* otherwise.
*/
getProperty: function(style, name, value) {
getProperty(style, name, value) {
const key = this.getKey(style);
const entry = this.map.get(key, null);
@ -34,7 +34,7 @@ UserProperties.prototype = {
return entry[name];
}
return value;
},
}
/**
* Set a named property for a given CSSStyleDeclaration.
@ -46,7 +46,7 @@ UserProperties.prototype = {
* @param {String} userValue
* The value of the property to set.
*/
setProperty: function(style, name, userValue) {
setProperty(style, name, userValue) {
const key = this.getKey(style, name);
const entry = this.map.get(key, null);
@ -57,7 +57,7 @@ UserProperties.prototype = {
props[name] = userValue;
this.map.set(key, props);
}
},
}
/**
* Check whether a named property for a given CSSStyleDeclaration is stored.
@ -67,19 +67,19 @@ UserProperties.prototype = {
* @param {String} name
* The name of the property to check.
*/
contains: function(style, name) {
contains(style, name) {
const key = this.getKey(style, name);
const entry = this.map.get(key, null);
return !!entry && name in entry;
},
}
getKey: function(style, name) {
getKey(style, name) {
return style.actorID + ":" + name;
},
}
clear: function() {
clear() {
this.map.clear();
},
};
}
}
module.exports = UserProperties;

View File

@ -2080,7 +2080,7 @@ nsresult nsGlobalWindowOuter::SetNewDocument(Document* aDocument,
return NS_ERROR_FAILURE;
}
JS::Rooted<JSObject*> obj(cx, GetWrapperPreserveColor());
JS::Rooted<JSObject*> obj(cx, GetWrapper());
MOZ_ASSERT(js::IsWindowProxy(obj));

View File

@ -344,21 +344,31 @@ class JS_FRIEND_API GCCellPtr {
uintptr_t ptr;
};
// Unwraps the given GCCellPtr and calls the given functor with a template
// argument of the actual type of the pointer.
template <typename F, typename... Args>
auto DispatchTyped(F f, GCCellPtr thing, Args&&... args) {
// Unwraps the given GCCellPtr, calls the functor |f| with a template argument
// of the actual type of the pointer, and returns the result.
template <typename F>
auto MapGCThingTyped(GCCellPtr thing, F&& f) {
switch (thing.kind()) {
#define JS_EXPAND_DEF(name, type, _) \
case JS::TraceKind::name: \
return f(&thing.as<type>(), std::forward<Args>(args)...);
return f(&thing.as<type>());
JS_FOR_EACH_TRACEKIND(JS_EXPAND_DEF);
#undef JS_EXPAND_DEF
default:
MOZ_CRASH("Invalid trace kind in DispatchTyped for GCCellPtr.");
MOZ_CRASH("Invalid trace kind in MapGCThingTyped for GCCellPtr.");
}
}
// Unwraps the given GCCellPtr and calls the functor |f| with a template
// argument of the actual type of the pointer. Doesn't return anything.
template <typename F>
void ApplyGCThingTyped(GCCellPtr thing, F&& f) {
// This function doesn't do anything but is supplied for symmetry with other
// MapGCThingTyped/ApplyGCThingTyped implementations that have to wrap the
// functor to return a dummy value that is ignored.
MapGCThingTyped(thing, f);
}
} /* namespace JS */
// These are defined in the toplevel namespace instead of within JS so that

View File

@ -20,6 +20,8 @@
// A jsid is not implicitly convertible to or from a Value; JS_ValueToId or
// JS_IdToValue must be used instead.
#include "mozilla/Maybe.h"
#include "jstypes.h"
#include "js/HeapAPI.h"
@ -207,18 +209,26 @@ struct BarrierMethods<jsid> {
}
};
// If the jsid is a GC pointer type, convert to that type and call |f| with
// the pointer. If the jsid is not a GC type, calls F::defaultValue.
template <typename F, typename... Args>
auto DispatchTyped(F f, const jsid& id, Args&&... args) {
// If the jsid is a GC pointer type, convert to that type and call |f| with the
// pointer and return the result wrapped in a Maybe, otherwise return None().
template <typename F>
auto MapGCThingTyped(const jsid& id, F&& f) {
if (JSID_IS_STRING(id)) {
return f(JSID_TO_STRING(id), std::forward<Args>(args)...);
return mozilla::Some(f(JSID_TO_STRING(id)));
}
if (JSID_IS_SYMBOL(id)) {
return f(JSID_TO_SYMBOL(id), std::forward<Args>(args)...);
return mozilla::Some(f(JSID_TO_SYMBOL(id)));
}
MOZ_ASSERT(!JSID_IS_GCTHING(id));
return F::defaultValue(id);
using ReturnType = decltype(f(static_cast<JSString*>(nullptr)));
return mozilla::Maybe<ReturnType>();
}
// If the jsid is a GC pointer type, convert to that type and call |f| with the
// pointer. Return whether this happened.
template <typename F>
bool ApplyGCThingTyped(const jsid& id, F&& f) {
return MapGCThingTyped(id, [&f](auto t) { f(t); return true; }).isSome();
}
#undef id

View File

@ -226,20 +226,33 @@ auto DispatchTraceKindTyped(F f, JS::TraceKind traceKind, Args&&... args) {
}
#undef JS_DEPENDENT_TEMPLATE_HINT
template <typename F, typename... Args>
auto DispatchTraceKindTyped(F f, void* thing, JS::TraceKind traceKind,
Args&&... args) {
// Given a GC thing specified by pointer and trace kind, calls the functor |f|
// with a template argument of the actual type of the pointer and returns the
// result.
template <typename F>
auto MapGCThingTyped(void* thing, JS::TraceKind traceKind, F&& f) {
switch (traceKind) {
#define JS_EXPAND_DEF(name, type, _) \
case JS::TraceKind::name: \
return f(static_cast<type*>(thing), std::forward<Args>(args)...);
return f(static_cast<type*>(thing));
JS_FOR_EACH_TRACEKIND(JS_EXPAND_DEF);
#undef JS_EXPAND_DEF
default:
MOZ_CRASH("Invalid trace kind in DispatchTraceKindTyped.");
MOZ_CRASH("Invalid trace kind in MapGCThingTyped.");
}
}
// Given a GC thing specified by pointer and trace kind, calls the functor |f|
// with a template argument of the actual type of the pointer and ignores the
// result.
template <typename F>
void ApplyGCThingTyped(void* thing, JS::TraceKind traceKind, F&& f) {
// This function doesn't do anything but is supplied for symmetry with other
// MapGCThingTyped/ApplyGCThingTyped implementations that have to wrap the
// functor to return a dummy value that is ignored.
MapGCThingTyped(thing, traceKind, std::move(f));
}
} // namespace JS
#endif // js_TraceKind_h

View File

@ -15,6 +15,7 @@
#include "mozilla/EndianUtils.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/Likely.h"
#include "mozilla/Maybe.h"
#include <limits> /* for std::numeric_limits */
@ -1308,54 +1309,48 @@ class HeapBase<JS::Value, Wrapper>
}
};
/*
* If the Value is a GC pointer type, convert to that type and call |f| with
* the pointer. If the Value is not a GC type, calls F::defaultValue.
*/
template <typename F, typename... Args>
auto DispatchTyped(F f, const JS::Value& val, Args&&... args) {
// If the Value is a GC pointer type, call |f| with the pointer cast to that
// type and return the result wrapped in a Maybe, otherwise return None().
template <typename F>
auto MapGCThingTyped(const JS::Value& val, F&& f) {
if (val.isString()) {
JSString* str = val.toString();
MOZ_ASSERT(gc::IsCellPointerValid(str));
return f(str, std::forward<Args>(args)...);
return mozilla::Some(f(str));
}
if (val.isObject()) {
JSObject* obj = &val.toObject();
MOZ_ASSERT(gc::IsCellPointerValid(obj));
return f(obj, std::forward<Args>(args)...);
return mozilla::Some(f(obj));
}
if (val.isSymbol()) {
JS::Symbol* sym = val.toSymbol();
MOZ_ASSERT(gc::IsCellPointerValid(sym));
return f(sym, std::forward<Args>(args)...);
return mozilla::Some(f(sym));
}
#ifdef ENABLE_BIGINT
if (val.isBigInt()) {
JS::BigInt* bi = val.toBigInt();
MOZ_ASSERT(gc::IsCellPointerValid(bi));
return f(bi, std::forward<Args>(args)...);
return mozilla::Some(f(bi));
}
#endif
if (MOZ_UNLIKELY(val.isPrivateGCThing())) {
MOZ_ASSERT(gc::IsCellPointerValid(val.toGCThing()));
return DispatchTyped(f, val.toGCCellPtr(), std::forward<Args>(args)...);
return mozilla::Some(MapGCThingTyped(val.toGCCellPtr(), std::move(f)));
}
MOZ_ASSERT(!val.isGCThing());
return F::defaultValue(val);
using ReturnType = decltype(f(static_cast<JSObject*>(nullptr)));
return mozilla::Maybe<ReturnType>();
}
template <class S>
struct VoidDefaultAdaptor {
static void defaultValue(const S&) {}
};
template <class S>
struct IdentityDefaultAdaptor {
static S defaultValue(const S& v) { return v; }
};
template <class S, bool v>
struct BoolDefaultAdaptor {
static bool defaultValue(const S&) { return v; }
};
// If the Value is a GC pointer type, call |f| with the pointer cast to that
// type. Return whether this happened.
template <typename F>
bool ApplyGCThingTyped(const JS::Value& val, F&& f) {
return MapGCThingTyped(val, [&f](auto t) { f(t); return true; }).isSome();
}
static inline JS::Value PoisonedObjectValue(uintptr_t poison) {
JS::Value v;

View File

@ -7,6 +7,7 @@
#include "builtin/ModuleObject.h"
#include "mozilla/EnumSet.h"
#include "mozilla/ScopeExit.h"
#include "builtin/Promise.h"
#include "builtin/SelfHostingDefines.h"
@ -1677,9 +1678,6 @@ JSObject* js::CallModuleResolveHook(JSContext* cx,
JSObject* js::StartDynamicModuleImport(JSContext* cx, HandleScript script,
HandleValue specifierArg) {
RootedValue referencingPrivate(cx,
script->sourceObject()->canonicalPrivate());
RootedObject promiseConstructor(cx, JS::GetPromiseConstructor(cx));
if (!promiseConstructor) {
return nullptr;
@ -1710,7 +1708,13 @@ JSObject* js::StartDynamicModuleImport(JSContext* cx, HandleScript script,
return promise;
}
RootedValue referencingPrivate(cx,
script->sourceObject()->canonicalPrivate());
cx->runtime()->addRefScriptPrivate(referencingPrivate);
if (!importHook(cx, referencingPrivate, specifier, promise)) {
cx->runtime()->releaseScriptPrivate(referencingPrivate);
// If there's no exception pending then the script is terminating
// anyway, so just return nullptr.
if (!cx->isExceptionPending() ||
@ -1729,6 +1733,10 @@ bool js::FinishDynamicModuleImport(JSContext* cx,
HandleObject promiseArg) {
Handle<PromiseObject*> promise = promiseArg.as<PromiseObject>();
auto releasePrivate = mozilla::MakeScopeExit([&] {
cx->runtime()->releaseScriptPrivate(referencingPrivate);
});
if (cx->isExceptionPending()) {
return RejectPromiseWithPendingError(cx, promise);
}

View File

@ -82,34 +82,17 @@ AutoTouchingGrayThings::~AutoTouchingGrayThings() {
#endif // DEBUG
template <typename S>
template <typename T>
void ReadBarrierFunctor<S>::operator()(T* t) {
InternalBarrierMethods<T*>::readBarrier(t);
/* static */ void InternalBarrierMethods<Value>::readBarrier(const Value& v) {
ApplyGCThingTyped(v, [](auto t) { t->readBarrier(t); });
}
// All GC things may be held in a Value, either publicly or as a private GC
// thing.
#define JS_EXPAND_DEF(name, type, _) \
template void ReadBarrierFunctor<JS::Value>::operator()<type>(type*);
JS_FOR_EACH_TRACEKIND(JS_EXPAND_DEF);
#undef JS_EXPAND_DEF
template <typename S>
template <typename T>
void PreBarrierFunctor<S>::operator()(T* t) {
InternalBarrierMethods<T*>::preBarrier(t);
/* static */ void InternalBarrierMethods<Value>::preBarrier(const Value& v) {
ApplyGCThingTyped(v, [](auto t) { t->writeBarrierPre(t); });
}
// All GC things may be held in a Value, either publicly or as a private GC
// thing.
#define JS_EXPAND_DEF(name, type, _) \
template void PreBarrierFunctor<JS::Value>::operator()<type>(type*);
JS_FOR_EACH_TRACEKIND(JS_EXPAND_DEF);
#undef JS_EXPAND_DEF
template void PreBarrierFunctor<jsid>::operator()<JS::Symbol>(JS::Symbol*);
template void PreBarrierFunctor<jsid>::operator()<JSString>(JSString*);
/* static */ void InternalBarrierMethods<jsid>::preBarrier(jsid id) {
ApplyGCThingTyped(id, [](auto t) { t->writeBarrierPre(t); });
}
template <typename T>
/* static */ bool MovableCellHasher<T>::hasHash(const Lookup& l) {

View File

@ -272,25 +272,11 @@ struct InternalBarrierMethods<T*> {
#endif
};
template <typename S>
struct PreBarrierFunctor : public VoidDefaultAdaptor<S> {
template <typename T>
void operator()(T* t);
};
template <typename S>
struct ReadBarrierFunctor : public VoidDefaultAdaptor<S> {
template <typename T>
void operator()(T* t);
};
template <>
struct InternalBarrierMethods<Value> {
static bool isMarkable(const Value& v) { return v.isGCThing(); }
static void preBarrier(const Value& v) {
DispatchTyped(PreBarrierFunctor<Value>(), v);
}
static void preBarrier(const Value& v);
static MOZ_ALWAYS_INLINE void postBarrier(Value* vp, const Value& prev,
const Value& next) {
@ -319,9 +305,7 @@ struct InternalBarrierMethods<Value> {
}
}
static void readBarrier(const Value& v) {
DispatchTyped(ReadBarrierFunctor<Value>(), v);
}
static void readBarrier(const Value& v);
#ifdef DEBUG
static void assertThingIsNotGray(const Value& v) {
@ -333,9 +317,7 @@ struct InternalBarrierMethods<Value> {
template <>
struct InternalBarrierMethods<jsid> {
static bool isMarkable(jsid id) { return JSID_IS_GCTHING(id); }
static void preBarrier(jsid id) {
DispatchTyped(PreBarrierFunctor<jsid>(), id);
}
static void preBarrier(jsid id);
static void postBarrier(jsid* idp, jsid prev, jsid next) {}
#ifdef DEBUG
static void assertThingIsNotGray(jsid id) { JS::AssertIdIsNotGray(id); }

View File

@ -12,23 +12,21 @@
#include "jsfriendapi.h"
#include "jsutil.h"
#include "js/HashTable.h"
namespace js {
namespace gc {
template <class Node>
template <typename Node>
struct GraphNodeBase {
Node* gcNextGraphNode;
Node* gcNextGraphComponent;
unsigned gcDiscoveryTime;
unsigned gcLowLink;
using NodeSet =
js::HashSet<Node*, js::DefaultHasher<Node*>, js::SystemAllocPolicy>;
GraphNodeBase()
: gcNextGraphNode(nullptr),
gcNextGraphComponent(nullptr),
gcDiscoveryTime(0),
gcLowLink(0) {}
~GraphNodeBase() {}
NodeSet gcGraphEdges;
Node* gcNextGraphNode = nullptr;
Node* gcNextGraphComponent = nullptr;
unsigned gcDiscoveryTime = 0;
unsigned gcLowLink = 0;
Node* nextNodeInGroup() const {
if (gcNextGraphNode &&
@ -45,41 +43,31 @@ struct GraphNodeBase {
* Find the strongly connected components of a graph using Tarjan's algorithm,
* and return them in topological order.
*
* Nodes derive from GraphNodeBase and implement findGraphEdges, which calls
* finder.addEdgeTo to describe the outgoing edges from that node:
* Nodes derive from GraphNodeBase and add target edge pointers to
* sourceNode.gcGraphEdges to describe the graph:
*
* struct MyComponentFinder;
*
* struct MyGraphNode : public GraphNodeBase
* struct MyGraphNode : public GraphNodeBase<MyGraphNode>
* {
* void findOutgoingEdges(MyComponentFinder& finder)
* {
* for edge in my_outgoing_edges:
* if is_relevant(edge):
* finder.addEdgeTo(edge.destination)
* }
* ...
* }
*
* struct MyComponentFinder : public ComponentFinder<MyGraphNode,
* MyComponentFinder>
* {
* ...
* };
* MyGraphNode node1, node2, node3;
* node1.gcGraphEdges.put(node2); // Error checking elided.
* node2.gcGraphEdges.put(node3);
* node3.gcGraphEdges.put(node2);
*
* MyComponentFinder finder;
* finder.addNode(v);
* ComponentFinder<MyGraphNode> finder;
* finder.addNode(node1);
* finder.addNode(node2);
* finder.addNode(node3);
* MyGraphNode* result = finder.getResultsList();
*/
template <typename Node, typename Derived>
template <typename Node>
class ComponentFinder {
public:
explicit ComponentFinder(uintptr_t sl)
: clock(1),
stack(nullptr),
firstComponent(nullptr),
cur(nullptr),
stackLimit(sl),
stackFull(false) {}
: stackLimit(sl) {}
~ComponentFinder() {
MOZ_ASSERT(!stack);
@ -131,8 +119,14 @@ class ComponentFinder {
}
}
public:
/* Call from implementation of GraphNodeBase::findOutgoingEdges(). */
private:
// Constant used to indicate an unprocessed vertex.
static const unsigned Undefined = 0;
// Constant used to indicate a processed vertex that is no longer on the
// stack.
static const unsigned Finished = (unsigned)-1;
void addEdgeTo(Node* w) {
if (w->gcDiscoveryTime == Undefined) {
processNode(w);
@ -142,14 +136,6 @@ class ComponentFinder {
}
}
private:
/* Constant used to indicate an unprocessed vertex. */
static const unsigned Undefined = 0;
// Constant used to indicate an processed vertex that is no longer on the
// stack.
static const unsigned Finished = (unsigned)-1;
void processNode(Node* v) {
v->gcDiscoveryTime = clock;
v->gcLowLink = clock;
@ -166,7 +152,9 @@ class ComponentFinder {
Node* old = cur;
cur = v;
cur->findOutgoingEdges(*static_cast<Derived*>(this));
for (auto r = cur->gcGraphEdges.all(); !r.empty(); r.popFront()) {
addEdgeTo(r.front());
}
cur = old;
if (stackFull) {
@ -201,12 +189,12 @@ class ComponentFinder {
}
private:
unsigned clock;
Node* stack;
Node* firstComponent;
Node* cur;
unsigned clock = 1;
Node* stack = nullptr;
Node* firstComponent = nullptr;
Node* cur = nullptr;
uintptr_t stackLimit;
bool stackFull;
bool stackFull = false;
};
} /* namespace gc */

View File

@ -3674,21 +3674,16 @@ void GCRuntime::freeFromBackgroundThread(AutoLockHelperThreadState& lock) {
void GCRuntime::waitBackgroundFreeEnd() { freeTask.join(); }
struct IsAboutToBeFinalizedFunctor {
template <typename T>
bool operator()(Cell** t) {
mozilla::DebugOnly<const Cell*> prior = *t;
bool result = IsAboutToBeFinalizedUnbarriered(reinterpret_cast<T**>(t));
/* static */ bool UniqueIdGCPolicy::needsSweep(Cell** cellp, uint64_t*) {
Cell* cell = *cellp;
return MapGCThingTyped(cell, cell->getTraceKind(), [](auto t) {
mozilla::DebugOnly<const Cell*> prior = t;
bool result = IsAboutToBeFinalizedUnbarriered(&t);
// Sweep should not have to deal with moved pointers, since moving GC
// handles updating the UID table manually.
MOZ_ASSERT(*t == prior);
MOZ_ASSERT(t == prior);
return result;
}
};
/* static */ bool UniqueIdGCPolicy::needsSweep(Cell** cell, uint64_t*) {
return DispatchTraceKindTyped(IsAboutToBeFinalizedFunctor(),
(*cell)->getTraceKind(), cell);
});
}
void JS::Zone::sweepUniqueIds() { uniqueIds().sweep(); }
@ -4037,15 +4032,10 @@ static bool InCrossCompartmentMap(JSObject* src, JS::GCCellPtr dst) {
return false;
}
struct MaybeCompartmentFunctor {
template <typename T>
JS::Compartment* operator()(T* t) {
return t->maybeCompartment();
}
};
void CompartmentCheckTracer::onChild(const JS::GCCellPtr& thing) {
Compartment* comp = DispatchTyped(MaybeCompartmentFunctor(), thing);
Compartment* comp = MapGCThingTyped(thing, [](auto t) {
return t->maybeCompartment();
});
if (comp && compartment) {
MOZ_ASSERT(comp == compartment ||
(srcKind == JS::TraceKind::Object &&
@ -4072,8 +4062,9 @@ void GCRuntime::checkForCompartmentMismatches() {
i.next()) {
trc.src = i.getCell();
trc.srcKind = MapAllocToTraceKind(thingKind);
trc.compartment = DispatchTraceKindTyped(MaybeCompartmentFunctor(),
trc.src, trc.srcKind);
trc.compartment = MapGCThingTyped(trc.src, trc.srcKind, [](auto t) {
return t->maybeCompartment();
});
js::TraceChildren(&trc, trc.src, trc.srcKind);
}
}
@ -4828,79 +4819,46 @@ static void DropStringWrappers(JSRuntime* rt) {
/*
* Group zones that must be swept at the same time.
*
* If compartment A has an edge to an unmarked object in compartment B, then we
* must not sweep A in a later slice than we sweep B. That's because a write
* barrier in A could lead to the unmarked object in B becoming marked.
* However, if we had already swept that object, we would be in trouble.
* From the point of view of the mutator, groups of zones transition atomically
* from marking to sweeping. If compartment A has an edge to an unmarked object
* in compartment B, then we must not start sweeping A in a later slice than we
* start sweeping B. That's because a write barrier in A could lead to the
* unmarked object in B becoming marked. However, if we had already swept that
* object, we would be in trouble.
*
* If we consider these dependencies as a graph, then all the compartments in
* any strongly-connected component of this graph must be swept in the same
* slice.
* any strongly-connected component of this graph must start sweeping in the
* same slice.
*
* Tarjan's algorithm is used to calculate the components.
*/
void Compartment::findOutgoingEdges(ZoneComponentFinder& finder) {
bool Compartment::findSweepGroupEdges() {
Zone* source = zone();
for (js::WrapperMap::Enum e(crossCompartmentWrappers); !e.empty();
e.popFront()) {
CrossCompartmentKey& key = e.front().mutableKey();
MOZ_ASSERT(!key.is<JSString*>());
// Add edge to wrapped object zone if wrapped object is not marked black to
// ensure that wrapper zone not finish marking after we start sweeping the
// wrapped zone.
if (key.is<JSObject*>() &&
key.as<JSObject*>()->asTenured().isMarkedBlack()) {
// CCW target is already marked, so we don't need to watch out for
// later marking of the CCW.
continue;
}
key.applyToWrapped([&finder](auto tp) {
/*
* Add edge to wrapped object compartment if wrapped object is not
* marked black to indicate that wrapper compartment not be swept
* after wrapped compartment.
*/
JS::Zone* zone = (*tp)->asTenured().zone();
if (zone->isGCMarking()) {
finder.addEdgeTo(zone);
}
Zone* target = key.applyToWrapped([](auto tp) {
return (*tp)->asTenured().zone();
});
}
}
void Zone::findOutgoingEdges(ZoneComponentFinder& finder) {
/*
* Any compartment may have a pointer to an atom in the atoms
* compartment, and these aren't in the cross compartment map.
*/
if (Zone* zone = finder.maybeAtomsZone) {
MOZ_ASSERT(zone->isCollecting());
finder.addEdgeTo(zone);
}
for (CompartmentsInZoneIter comp(this); !comp.done(); comp.next()) {
comp->findOutgoingEdges(finder);
}
for (ZoneSet::Range r = gcSweepGroupEdges().all(); !r.empty(); r.popFront()) {
if (r.front()->isGCMarking()) {
finder.addEdgeTo(r.front());
if (!target->isGCMarking()) {
continue;
}
}
Debugger::findZoneEdges(this, finder);
}
bool GCRuntime::findInterZoneEdges() {
/*
* Weakmaps which have keys with delegates in a different zone introduce the
* need for zone edges from the delegate's zone to the weakmap zone.
*
* Since the edges point into and not away from the zone the weakmap is in
* we must find these edges in advance and store them in a set on the Zone.
* If we run out of memory, we fall back to sweeping everything in one
* group.
*/
for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
if (!WeakMapBase::findInterZoneEdges(zone)) {
if (!source->addSweepGroupEdgeTo(target)) {
return false;
}
}
@ -4908,6 +4866,32 @@ bool GCRuntime::findInterZoneEdges() {
return true;
}
bool Zone::findSweepGroupEdges(Zone* atomsZone) {
// Any zone may have a pointer to an atom in the atoms zone, and these aren't
// in the cross compartment map.
if (atomsZone->wasGCStarted() && !addSweepGroupEdgeTo(atomsZone)) {
return false;
}
for (CompartmentsInZoneIter comp(this); !comp.done(); comp.next()) {
if (!comp->findSweepGroupEdges()) {
return false;
}
}
return WeakMapBase::findSweepGroupEdges(this) &&
Debugger::findSweepGroupEdges(this);
}
bool GCRuntime::findSweepGroupEdges() {
for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
if (!zone->findSweepGroupEdges(atomsZone)) {
return false;
}
}
return true;
}
void GCRuntime::groupZonesForSweeping(JS::GCReason reason) {
#ifdef DEBUG
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
@ -4916,10 +4900,8 @@ void GCRuntime::groupZonesForSweeping(JS::GCReason reason) {
#endif
JSContext* cx = rt->mainContextFromOwnThread();
Zone* maybeAtomsZone = atomsZone->wasGCStarted() ? atomsZone.ref() : nullptr;
ZoneComponentFinder finder(cx->nativeStackLimit[JS::StackForSystemCode],
maybeAtomsZone);
if (!isIncremental || !findInterZoneEdges()) {
ZoneComponentFinder finder(cx->nativeStackLimit[JS::StackForSystemCode]);
if (!isIncremental || !findSweepGroupEdges()) {
finder.useOneComponent();
}
@ -4939,7 +4921,7 @@ void GCRuntime::groupZonesForSweeping(JS::GCReason reason) {
sweepGroupIndex = 1;
for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
zone->gcSweepGroupEdges().clear();
zone->clearSweepGroupEdges();
}
#ifdef DEBUG
@ -8652,20 +8634,13 @@ JS_PUBLIC_API void JS::IncrementalPreWriteBarrier(JSObject* obj) {
JSObject::writeBarrierPre(obj);
}
struct IncrementalReadBarrierFunctor {
template <typename T>
void operator()(T* t) {
T::readBarrier(t);
}
};
JS_PUBLIC_API void JS::IncrementalReadBarrier(GCCellPtr thing) {
if (!thing) {
return;
}
MOZ_ASSERT(!JS::RuntimeHeapIsMajorCollecting());
DispatchTyped(IncrementalReadBarrierFunctor(), thing);
ApplyGCThingTyped(thing, [](auto t) { t->readBarrier(t); });
}
JS_PUBLIC_API bool JS::WasIncrementalGC(JSRuntime* rt) {

View File

@ -615,7 +615,7 @@ class GCRuntime {
void beginSweepPhase(JS::GCReason reason, AutoGCSession& session);
void groupZonesForSweeping(JS::GCReason reason);
MOZ_MUST_USE bool findInterZoneEdges();
MOZ_MUST_USE bool findSweepGroupEdges();
void getNextSweepGroup();
IncrementalProgress markGrayReferencesInCurrentGroup(FreeOp* fop,
SliceBudget& budget);

View File

@ -9,15 +9,53 @@
#include "gc/Marking.h"
#include "mozilla/Maybe.h"
#include "gc/RelocationOverlay.h"
#ifdef ENABLE_BIGINT
# include "vm/BigIntType.h"
#endif
#include "vm/RegExpShared.h"
namespace js {
namespace gc {
// An abstraction to re-wrap any kind of typed pointer back to the tagged pointer
// it came from with |TaggedPtr<TargetType>::wrap(sourcePtr)|.
template <typename T>
struct TaggedPtr {};
template <>
struct TaggedPtr<JS::Value> {
static JS::Value wrap(JSObject* obj) { return JS::ObjectOrNullValue(obj); }
static JS::Value wrap(JSString* str) { return JS::StringValue(str); }
static JS::Value wrap(JS::Symbol* sym) { return JS::SymbolValue(sym); }
#ifdef ENABLE_BIGINT
static JS::Value wrap(JS::BigInt* bi) { return JS::BigIntValue(bi); }
#endif
template <typename T>
static JS::Value wrap(T* priv) {
static_assert(mozilla::IsBaseOf<Cell, T>::value,
"Type must be a GC thing derived from js::gc::Cell");
return JS::PrivateGCThingValue(priv);
}
};
template <>
struct TaggedPtr<jsid> {
static jsid wrap(JSString* str) {
return NON_INTEGER_ATOM_TO_JSID(&str->asAtom());
}
static jsid wrap(JS::Symbol* sym) { return SYMBOL_TO_JSID(sym); }
};
template <>
struct TaggedPtr<TaggedProto> {
static TaggedProto wrap(JSObject* obj) { return TaggedProto(obj); }
};
template <typename T>
struct MightBeForwarded {
static_assert(mozilla::IsBaseOf<Cell, T>::value, "T must derive from Cell");
@ -45,15 +83,9 @@ inline bool IsForwarded(const T* t) {
return t->isForwarded();
}
struct IsForwardedFunctor : public BoolDefaultAdaptor<Value, false> {
template <typename T>
bool operator()(const T* t) {
return IsForwarded(t);
}
};
inline bool IsForwarded(const JS::Value& value) {
return DispatchTyped(IsForwardedFunctor(), value);
auto isForwarded = [](auto t) { return IsForwarded(t); };
return MapGCThingTyped(value, isForwarded).valueOr(false);
}
template <typename T>
@ -63,15 +95,9 @@ inline T* Forwarded(const T* t) {
return reinterpret_cast<T*>(overlay->forwardingAddress());
}
struct ForwardedFunctor : public IdentityDefaultAdaptor<Value> {
template <typename T>
inline Value operator()(const T* t) {
return js::gc::RewrapTaggedPointer<Value, T>::wrap(Forwarded(t));
}
};
inline Value Forwarded(const JS::Value& value) {
return DispatchTyped(ForwardedFunctor(), value);
auto forward = [](auto t) { return TaggedPtr<Value>::wrap(Forwarded(t)); };
return MapGCThingTyped(value, forward).valueOr(value);
}
template <typename T>
@ -113,15 +139,8 @@ inline void CheckGCThingAfterMovingGC(const ReadBarriered<T*>& t) {
CheckGCThingAfterMovingGC(t.unbarrieredGet());
}
struct CheckValueAfterMovingGCFunctor : public VoidDefaultAdaptor<Value> {
template <typename T>
void operator()(T* t) {
CheckGCThingAfterMovingGC(t);
}
};
inline void CheckValueAfterMovingGC(const JS::Value& value) {
DispatchTyped(CheckValueAfterMovingGCFunctor(), value);
ApplyGCThingTyped(value, [](auto t) { CheckGCThingAfterMovingGC(t); });
}
#endif // JSGC_HASH_TABLE_CHECKS

View File

@ -6,6 +6,7 @@
#include "gc/Marking-inl.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/IntegerRange.h"
#include "mozilla/ReentrancyGuard.h"
@ -277,17 +278,9 @@ void js::CheckTracedThing(JSTracer* trc, T* thing) {
#endif
}
template <typename S>
struct CheckTracedFunctor : public VoidDefaultAdaptor<S> {
template <typename T>
void operator()(T* t, JSTracer* trc) {
CheckTracedThing(trc, t);
}
};
template <typename T>
void js::CheckTracedThing(JSTracer* trc, T thing) {
DispatchTyped(CheckTracedFunctor<T>(), thing, trc);
ApplyGCThingTyped(thing, [](auto t) { CheckTracedThing(t); });
}
namespace js {
@ -407,6 +400,8 @@ void js::gc::AssertRootMarkingPhase(JSTracer* trc) {
/*** Tracing Interface ******************************************************/
template <typename T>
T* DoCallback(JS::CallbackTracer* trc, T** thingp, const char* name);
template <typename T>
T DoCallback(JS::CallbackTracer* trc, T* thingp, const char* name);
template <typename T>
@ -518,40 +513,40 @@ template void js::TraceProcessGlobalRoot<JSAtom>(JSTracer*, JSAtom*,
template void js::TraceProcessGlobalRoot<JS::Symbol>(JSTracer*, JS::Symbol*,
const char*);
// A typed functor adaptor for TraceRoot.
struct TraceRootFunctor {
template <typename T>
void operator()(JSTracer* trc, Cell** thingp, const char* name) {
TraceRoot(trc, reinterpret_cast<T**>(thingp), name);
}
};
void js::TraceGenericPointerRoot(JSTracer* trc, Cell** thingp,
const char* name) {
MOZ_ASSERT(thingp);
if (!*thingp) {
Cell* thing = *thingp;
if (!thing) {
return;
}
TraceRootFunctor f;
DispatchTraceKindTyped(f, (*thingp)->getTraceKind(), trc, thingp, name);
}
// A typed functor adaptor for TraceManuallyBarrieredEdge.
struct TraceManuallyBarrieredEdgeFunctor {
template <typename T>
void operator()(JSTracer* trc, Cell** thingp, const char* name) {
TraceManuallyBarrieredEdge(trc, reinterpret_cast<T**>(thingp), name);
auto traced = MapGCThingTyped(thing, thing->getTraceKind(),
[trc, name](auto t) -> Cell* {
TraceRoot(trc, &t, name);
return t;
});
if (traced != thing) {
*thingp = traced;
}
};
}
void js::TraceManuallyBarrieredGenericPointerEdge(JSTracer* trc, Cell** thingp,
const char* name) {
MOZ_ASSERT(thingp);
Cell* thing = *thingp;
if (!*thingp) {
return;
}
TraceManuallyBarrieredEdgeFunctor f;
DispatchTraceKindTyped(f, (*thingp)->getTraceKind(), trc, thingp, name);
auto traced = MapGCThingTyped(thing, thing->getTraceKind(),
[trc, name](auto t) -> Cell* {
TraceManuallyBarrieredEdge(trc, &t, name);
return t;
});
if (traced != thing) {
*thingp = traced;
}
}
// This method is responsible for dynamic dispatch to the real tracer
@ -742,17 +737,9 @@ void DoMarking(GCMarker* gcmarker, T* thing) {
SetMaybeAliveFlag(thing);
}
template <typename S>
struct DoMarkingFunctor : public VoidDefaultAdaptor<S> {
template <typename T>
void operator()(T* t, GCMarker* gcmarker) {
DoMarking(gcmarker, t);
}
};
template <typename T>
void DoMarking(GCMarker* gcmarker, const T& thing) {
DispatchTyped(DoMarkingFunctor<T>(), thing, gcmarker);
ApplyGCThingTyped(thing, [gcmarker](auto t) { DoMarking(gcmarker, t); });
}
template <typename T>
@ -934,17 +921,11 @@ void js::GCMarker::traverseEdge(S source, T* target) {
traverse(target);
}
template <typename V, typename S>
struct TraverseEdgeFunctor : public VoidDefaultAdaptor<V> {
template <typename T>
void operator()(T t, GCMarker* gcmarker, S s) {
return gcmarker->traverseEdge(s, t);
}
};
template <typename S, typename T>
void js::GCMarker::traverseEdge(S source, const T& thing) {
DispatchTyped(TraverseEdgeFunctor<T, S>(), thing, this, source);
ApplyGCThingTyped(thing, [this, source](auto t) {
this->traverseEdge(source, t);
});
}
namespace {
@ -1519,20 +1500,16 @@ void js::GCMarker::lazilyMarkChildren(ObjectGroup* group) {
void JS::BigInt::traceChildren(JSTracer* trc) { return; }
#endif
struct TraverseObjectFunctor {
template <typename T>
void operator()(T* thing, GCMarker* gcmarker, JSObject* src) {
gcmarker->traverseEdge(src, *thing);
}
};
template <typename Functor>
static void VisitTraceList(const Functor& f, const int32_t* traceList,
uint8_t* memory);
// Call the trace hook set on the object, if present. If further tracing of
// NativeObject fields is required, this will return the native object.
enum class CheckGeneration { DoChecks, NoChecks };
template <typename Functor, typename... Args>
static inline NativeObject* CallTraceHook(Functor f, JSTracer* trc,
JSObject* obj, CheckGeneration check,
Args&&... args) {
template <typename Functor>
static inline NativeObject* CallTraceHook(Functor&& f, JSTracer* trc,
JSObject* obj, CheckGeneration check) {
const Class* clasp = obj->getClass();
MOZ_ASSERT(clasp);
MOZ_ASSERT(obj->isNative() == clasp->isNative());
@ -1543,12 +1520,12 @@ static inline NativeObject* CallTraceHook(Functor f, JSTracer* trc,
if (clasp->isTrace(InlineTypedObject::obj_trace)) {
Shape** pshape = obj->as<InlineTypedObject>().addressOfShapeFromGC();
f(pshape, std::forward<Args>(args)...);
f(pshape);
InlineTypedObject& tobj = obj->as<InlineTypedObject>();
if (tobj.typeDescr().hasTraceList()) {
VisitTraceList(f, tobj.typeDescr().traceList(),
tobj.inlineTypedMemForGC(), std::forward<Args>(args)...);
tobj.inlineTypedMemForGC());
}
return nullptr;
@ -1557,7 +1534,7 @@ static inline NativeObject* CallTraceHook(Functor f, JSTracer* trc,
if (clasp == &UnboxedPlainObject::class_) {
JSObject** pexpando = obj->as<UnboxedPlainObject>().addressOfExpando();
if (*pexpando) {
f(pexpando, std::forward<Args>(args)...);
f(pexpando);
}
UnboxedPlainObject& unboxed = obj->as<UnboxedPlainObject>();
@ -1565,8 +1542,7 @@ static inline NativeObject* CallTraceHook(Functor f, JSTracer* trc,
? unboxed.layout()
: unboxed.layoutDontCheckGeneration();
if (layout.traceList()) {
VisitTraceList(f, layout.traceList(), unboxed.data(),
std::forward<Args>(args)...);
VisitTraceList(f, layout.traceList(), unboxed.data());
}
return nullptr;
@ -1580,26 +1556,24 @@ static inline NativeObject* CallTraceHook(Functor f, JSTracer* trc,
return &obj->as<NativeObject>();
}
template <typename F, typename... Args>
static void VisitTraceList(F f, const int32_t* traceList, uint8_t* memory,
Args&&... args) {
template <typename Functor>
static void VisitTraceList(const Functor& f, const int32_t* traceList,
uint8_t* memory) {
while (*traceList != -1) {
f(reinterpret_cast<JSString**>(memory + *traceList),
std::forward<Args>(args)...);
f(reinterpret_cast<JSString**>(memory + *traceList));
traceList++;
}
traceList++;
while (*traceList != -1) {
JSObject** objp = reinterpret_cast<JSObject**>(memory + *traceList);
if (*objp) {
f(objp, std::forward<Args>(args)...);
f(objp);
}
traceList++;
}
traceList++;
while (*traceList != -1) {
f(reinterpret_cast<Value*>(memory + *traceList),
std::forward<Args>(args)...);
f(reinterpret_cast<Value*>(memory + *traceList));
traceList++;
}
}
@ -1829,8 +1803,9 @@ scan_obj : {
ObjectGroup* group = obj->groupFromGC();
traverseEdge(obj, group);
NativeObject* nobj = CallTraceHook(TraverseObjectFunctor(), this, obj,
CheckGeneration::DoChecks, this, obj);
NativeObject* nobj = CallTraceHook([this, obj](auto thingp) {
this->traverseEdge(obj, *thingp);
}, this, obj, CheckGeneration::DoChecks);
if (!nobj) {
return;
}
@ -2760,19 +2735,17 @@ void TenuringTracer::traverse(JSString** strp) {
}
}
template <typename S>
struct TenuringTraversalFunctor : public IdentityDefaultAdaptor<S> {
template <typename T>
S operator()(T* t, TenuringTracer* trc) {
trc->traverse(&t);
return js::gc::RewrapTaggedPointer<S, T>::wrap(t);
}
};
template <typename T>
void TenuringTracer::traverse(T* thingp) {
*thingp = DispatchTyped(TenuringTraversalFunctor<T>(), *thingp, this);
auto tenured = MapGCThingTyped(*thingp, [this](auto t) {
this->traverse(&t);
return TaggedPtr<T>::wrap(t);
});
if (tenured.isSome() && tenured.value() != *thingp) {
*thingp = tenured.value();
}
}
} // namespace js
template <typename T>
@ -2938,17 +2911,11 @@ void js::gc::StoreBuffer::ValueEdge::trace(TenuringTracer& mover) const {
}
}
struct TenuringFunctor {
template <typename T>
void operator()(T* thing, TenuringTracer& mover) {
mover.traverse(thing);
}
};
// Visit all object children of the object and trace them.
void js::TenuringTracer::traceObject(JSObject* obj) {
NativeObject* nobj = CallTraceHook(TenuringFunctor(), this, obj,
CheckGeneration::NoChecks, *this);
NativeObject* nobj = CallTraceHook([this](auto thingp) {
this->traverse(thingp);
}, this, obj, CheckGeneration::NoChecks);
if (!nobj) {
return;
}
@ -3360,36 +3327,30 @@ bool js::gc::IsMarkedBlackInternal(JSRuntime* rt, T** thingp) {
return (*thingp)->asTenured().isMarkedBlack();
}
template <typename S>
struct IsMarkedFunctor : public IdentityDefaultAdaptor<S> {
template <typename T>
S operator()(T* t, JSRuntime* rt, bool* rv) {
*rv = IsMarkedInternal(rt, &t);
return js::gc::RewrapTaggedPointer<S, T>::wrap(t);
}
};
template <typename T>
bool js::gc::IsMarkedInternal(JSRuntime* rt, T* thingp) {
bool rv = true;
*thingp = DispatchTyped(IsMarkedFunctor<T>(), *thingp, rt, &rv);
return rv;
}
template <typename S>
struct IsMarkedBlackFunctor : public IdentityDefaultAdaptor<S> {
template <typename T>
S operator()(T* t, JSRuntime* rt, bool* rv) {
*rv = IsMarkedBlackInternal(rt, &t);
return js::gc::RewrapTaggedPointer<S, T>::wrap(t);
bool marked = true;
auto thing = MapGCThingTyped(*thingp, [rt, &marked](auto t) {
marked = IsMarkedInternal(rt, &t);
return TaggedPtr<T>::wrap(t);
});
if (thing.isSome() && thing.value() != *thingp) {
*thingp = thing.value();
}
};
return marked;
}
template <typename T>
bool js::gc::IsMarkedBlackInternal(JSRuntime* rt, T* thingp) {
bool rv = true;
*thingp = DispatchTyped(IsMarkedBlackFunctor<T>(), *thingp, rt, &rv);
return rv;
bool marked = true;
auto thing = MapGCThingTyped(*thingp, [rt, &marked](auto t) {
marked = IsMarkedBlackInternal(rt, &t);
return TaggedPtr<T>::wrap(t);
});
if (thing.isSome() && thing.value() != *thingp) {
*thingp = thing.value();
}
return marked;
}
bool js::gc::IsAboutToBeFinalizedDuringSweep(TenuredCell& tenured) {
@ -3426,21 +3387,17 @@ bool js::gc::IsAboutToBeFinalizedInternal(T** thingp) {
return false;
}
template <typename S>
struct IsAboutToBeFinalizedInternalFunctor : public IdentityDefaultAdaptor<S> {
template <typename T>
S operator()(T* t, bool* rv) {
*rv = IsAboutToBeFinalizedInternal(&t);
return js::gc::RewrapTaggedPointer<S, T>::wrap(t);
}
};
template <typename T>
bool js::gc::IsAboutToBeFinalizedInternal(T* thingp) {
bool rv = false;
*thingp =
DispatchTyped(IsAboutToBeFinalizedInternalFunctor<T>(), *thingp, &rv);
return rv;
bool dying = false;
auto thing = MapGCThingTyped(*thingp, [&dying](auto t) {
dying = IsAboutToBeFinalizedInternal(&t);
return TaggedPtr<T>::wrap(t);
});
if (thing.isSome() && thing.value() != *thingp) {
*thingp = thing.value();
}
return dying;
}
namespace js {

View File

@ -135,49 +135,6 @@ inline Cell* ToMarkable(const Value& v) {
inline Cell* ToMarkable(Cell* cell) { return cell; }
// Wrap a GC thing pointer into a new Value or jsid. The type system enforces
// that the thing pointer is a wrappable type.
template <typename S, typename T>
struct RewrapTaggedPointer {};
#define DECLARE_REWRAP(S, T, method, prefix) \
template <> \
struct RewrapTaggedPointer<S, T> { \
static S wrap(T* thing) { return method(prefix thing); } \
}
DECLARE_REWRAP(JS::Value, JSObject, JS::ObjectOrNullValue, );
DECLARE_REWRAP(JS::Value, JSString, JS::StringValue, );
DECLARE_REWRAP(JS::Value, JS::Symbol, JS::SymbolValue, );
#ifdef ENABLE_BIGINT
DECLARE_REWRAP(JS::Value, JS::BigInt, JS::BigIntValue, );
#endif
DECLARE_REWRAP(jsid, JSString, NON_INTEGER_ATOM_TO_JSID, (JSAtom*));
DECLARE_REWRAP(jsid, JS::Symbol, SYMBOL_TO_JSID, );
DECLARE_REWRAP(js::TaggedProto, JSObject, js::TaggedProto, );
#undef DECLARE_REWRAP
template <typename T>
struct IsPrivateGCThingInValue
: public mozilla::EnableIf<mozilla::IsBaseOf<Cell, T>::value &&
!mozilla::IsBaseOf<JSObject, T>::value &&
!mozilla::IsBaseOf<JSString, T>::value &&
!mozilla::IsBaseOf<JS::Symbol, T>::value
#ifdef ENABLE_BIGINT
&& !mozilla::IsBaseOf<JS::BigInt, T>::value
#endif
,
T> {
static_assert(!mozilla::IsSame<Cell, T>::value &&
!mozilla::IsSame<TenuredCell, T>::value,
"T must not be Cell or TenuredCell");
};
template <typename T>
struct RewrapTaggedPointer<Value, T> {
static Value wrap(typename IsPrivateGCThingInValue<T>::Type* thing) {
return JS::PrivateGCThingValue(thing);
}
};
} /* namespace gc */
// The return value indicates if anything was unmarked.

View File

@ -12,7 +12,6 @@
#include "NamespaceImports.h"
#include "gc/GCInternals.h"
#include "gc/Marking.h"
#include "gc/PublicIterators.h"
#include "gc/Zone.h"
#include "util/Text.h"
@ -25,6 +24,7 @@
#include "vm/SymbolType.h"
#include "gc/GC-inl.h"
#include "gc/Marking-inl.h"
#include "vm/ObjectGroup-inl.h"
#include "vm/Realm-inl.h"
#include "vm/TypeInference-inl.h"
@ -41,56 +41,33 @@ void CheckTracedThing(JSTracer* trc, T thing);
/*** Callback Tracer Dispatch ***********************************************/
template <typename T>
T DoCallback(JS::CallbackTracer* trc, T* thingp, const char* name) {
T* DoCallback(JS::CallbackTracer* trc, T** thingp, const char* name) {
CheckTracedThing(trc, *thingp);
JS::AutoTracingName ctx(trc, name);
trc->dispatchToOnEdge(thingp);
return *thingp;
}
#define INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS(name, type, _) \
template type* DoCallback<type*>(JS::CallbackTracer*, type**, const char*);
template type* DoCallback<type>(JS::CallbackTracer*, type**, const char*);
JS_FOR_EACH_TRACEKIND(INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS);
#undef INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS
template <typename S>
struct DoCallbackFunctor : public IdentityDefaultAdaptor<S> {
template <typename T>
S operator()(T* t, JS::CallbackTracer* trc, const char* name) {
return js::gc::RewrapTaggedPointer<S, T>::wrap(DoCallback(trc, &t, name));
}
};
template <>
Value DoCallback<Value>(JS::CallbackTracer* trc, Value* vp, const char* name) {
// Only update *vp if the value changed, to avoid TSan false positives for
template <typename T>
T DoCallback(JS::CallbackTracer* trc, T* thingp, const char* name) {
auto thing = MapGCThingTyped(*thingp, [trc, name](auto t) {
return TaggedPtr<T>::wrap(DoCallback(trc, &t, name));
});
// Only update *thingp if the value changed, to avoid TSan false positives for
// template objects when using DumpHeapTracer or UbiNode tracers while Ion
// compiling off-thread.
Value v = DispatchTyped(DoCallbackFunctor<Value>(), *vp, trc, name);
if (*vp != v) {
*vp = v;
if (thing.isSome() && thing.value() != *thingp) {
*thingp = thing.value();
}
return v;
}
template <>
jsid DoCallback<jsid>(JS::CallbackTracer* trc, jsid* idp, const char* name) {
jsid id = DispatchTyped(DoCallbackFunctor<jsid>(), *idp, trc, name);
if (*idp != id) {
*idp = id;
}
return id;
}
template <>
TaggedProto DoCallback<TaggedProto>(JS::CallbackTracer* trc,
TaggedProto* protop, const char* name) {
TaggedProto proto =
DispatchTyped(DoCallbackFunctor<TaggedProto>(), *protop, trc, name);
if (*protop != proto) {
*protop = proto;
}
return proto;
return *thingp;
}
template JS::Value DoCallback<JS::Value>(JS::CallbackTracer*, JS::Value*, const char*);
template JS::PropertyKey DoCallback<JS::PropertyKey>(JS::CallbackTracer*, JS::PropertyKey*, const char*);
template TaggedProto DoCallback<TaggedProto>(JS::CallbackTracer*, TaggedProto*, const char*);
void JS::CallbackTracer::getTracingEdgeName(char* buffer, size_t bufferSize) {
MOZ_ASSERT(bufferSize > 0);
@ -111,22 +88,14 @@ JS_PUBLIC_API void JS::TraceChildren(JSTracer* trc, GCCellPtr thing) {
js::TraceChildren(trc, thing.asCell(), thing.kind());
}
struct TraceChildrenFunctor {
template <typename T>
void operator()(JSTracer* trc, void* thingArg) {
T* thing = static_cast<T*>(thingArg);
MOZ_ASSERT_IF(thing->runtimeFromAnyThread() != trc->runtime(),
ThingIsPermanentAtomOrWellKnownSymbol(thing) ||
thing->zoneFromAnyThread()->isSelfHostingZone());
thing->traceChildren(trc);
}
};
void js::TraceChildren(JSTracer* trc, void* thing, JS::TraceKind kind) {
MOZ_ASSERT(thing);
TraceChildrenFunctor f;
DispatchTraceKindTyped(f, kind, trc, thing);
ApplyGCThingTyped(thing, kind, [trc](auto t) {
MOZ_ASSERT_IF(t->runtimeFromAnyThread() != trc->runtime(),
ThingIsPermanentAtomOrWellKnownSymbol(t) ||
t->zoneFromAnyThread()->isSelfHostingZone());
t->traceChildren(trc);
});
}
JS_PUBLIC_API void JS::TraceIncomingCCWs(

View File

@ -71,7 +71,7 @@ bool WeakMapBase::markZoneIteratively(JS::Zone* zone, GCMarker* marker) {
return markedAny;
}
bool WeakMapBase::findInterZoneEdges(JS::Zone* zone) {
bool WeakMapBase::findSweepGroupEdges(JS::Zone* zone) {
for (WeakMapBase* m : zone->gcWeakMapList()) {
if (!m->findZoneEdges()) {
return false;
@ -149,7 +149,7 @@ bool ObjectValueMap::findZoneEdges() {
if (delegateZone == zone() || !delegateZone->isGCMarking()) {
continue;
}
if (!delegateZone->gcSweepGroupEdges().put(key->zone())) {
if (!delegateZone->addSweepGroupEdgeTo(key->zone())) {
return false;
}
}

View File

@ -80,7 +80,7 @@ class WeakMapBase : public mozilla::LinkedListElement<WeakMapBase> {
static bool markZoneIteratively(JS::Zone* zone, GCMarker* marker);
// Add zone edges for weakmaps with key delegates in a different zone.
static bool findInterZoneEdges(JS::Zone* zone);
static MOZ_MUST_USE bool findSweepGroupEdges(JS::Zone* zone);
// Sweep the weak maps in a zone, removing dead weak maps and removing
// entries of live weak maps whose keys are dead.

View File

@ -28,14 +28,7 @@ class JitZone;
namespace gc {
struct ZoneComponentFinder
: public ComponentFinder<JS::Zone, ZoneComponentFinder> {
ZoneComponentFinder(uintptr_t sl, JS::Zone* maybeAtomsZone)
: ComponentFinder<JS::Zone, ZoneComponentFinder>(sl),
maybeAtomsZone(maybeAtomsZone) {}
JS::Zone* maybeAtomsZone;
};
using ZoneComponentFinder = ComponentFinder<JS::Zone>;
struct UniqueIdGCPolicy {
static bool needsSweep(Cell** cell, uint64_t* value);
@ -185,7 +178,7 @@ class Zone : public JS::shadow::Zone,
helperThreadUse_ = HelperThreadUse::None;
}
void findOutgoingEdges(js::gc::ZoneComponentFinder& finder);
MOZ_MUST_USE bool findSweepGroupEdges(Zone* atomsZone);
enum ShouldDiscardBaselineCode : bool {
KeepBaselineCode = false,
@ -419,15 +412,18 @@ class Zone : public JS::shadow::Zone,
public:
js::gc::WeakKeyTable& gcWeakKeys() { return gcWeakKeys_.ref(); }
private:
// A set of edges from this zone to other zones.
//
// This is used during GC while calculating sweep groups to record edges
// that can't be determined by examining this zone by itself.
js::MainThreadData<ZoneSet> gcSweepGroupEdges_;
public:
ZoneSet& gcSweepGroupEdges() { return gcSweepGroupEdges_.ref(); }
// A set of edges from this zone to other zones used during GC to calculate
// sweep groups.
NodeSet& gcSweepGroupEdges() {
return gcGraphEdges; // Defined in GraphNodeBase base class.
}
MOZ_MUST_USE bool addSweepGroupEdgeTo(Zone* otherZone) {
MOZ_ASSERT(otherZone->isGCMarking());
return gcSweepGroupEdges().put(otherZone);
}
void clearSweepGroupEdges() {
gcSweepGroupEdges().clear();
}
// Keep track of all TypeDescr and related objects in this compartment.
// This is used by the GC to trace them all first when compacting, since the

View File

@ -99,6 +99,7 @@ const I64DivUCode = 0x80;
const I64RemSCode = 0x81;
const I64RemUCode = 0x82;
const RefNull = 0xd0;
const PlaceholderRefFunc = 0xd2;
const FirstInvalidOpcode = 0xc5;
const LastInvalidOpcode = 0xfb;

View File

@ -161,7 +161,6 @@ tab_test("(table.init 1 (i32.const 7) (i32.const 0) (i32.const 4)) \n" +
"(table.copy (i32.const 19) (i32.const 20) (i32.const 5))",
[e,e,3,1,4, 1,e,2,7,1, 8,e,7,e,7, 5,2,7,e,9, e,7,e,8,8, e,e,e,e,e]);
// And now a simplified version of the above, for memory.{init,drop,copy}.
function gen_mem_mod_t(insn)
@ -298,6 +297,44 @@ checkNoDataCount([I32ConstCode, 0,
checkNoDataCount([MiscPrefix, DataDropCode, 0],
/data.drop requires a DataCount section/);
// Verification that we can handle encoding errors for passive element segments
// properly.
function checkPassiveElemSegment(mangle, err) {
let bin = moduleWithSections(
[v2vSigSection, declSection([0]), // One function
tableSection(1), // One table
{ name: elemId, // One passive segment
body: (function () {
let body = [];
body.push(1); // 1 element segment
body.push(1); // Flag: Passive
body.push(AnyFuncCode + (mangle == "type" ? 1 : 0)); // always anyfunc
body.push(1); // Element count
body.push(PlaceholderRefFunc + (mangle == "ref.func" ? 1 : 0)); // always ref.func
body.push(0); // func index
body.push(EndCode + (mangle == "end" ? 1 : 0));
return body;
})() },
bodySection( // Empty function
[funcBody(
{locals:[],
body:[]})])
]);
if (err) {
assertErrorMessage(() => new WebAssembly.Module(bin),
WebAssembly.CompileError,
err);
} else {
new WebAssembly.Module(bin);
}
}
checkPassiveElemSegment("");
checkPassiveElemSegment("type", /passive segments can only contain function references/);
checkPassiveElemSegment("ref.func", /failed to read ref.func operation/);
checkPassiveElemSegment("end", /failed to read end of ref.func expression/);
//---------------------------------------------------------------------//
//---------------------------------------------------------------------//
// Some further tests for memory.copy and memory.fill. First, validation

View File

@ -16,31 +16,15 @@ static const unsigned MaxVertices = 10;
using js::gc::ComponentFinder;
using js::gc::GraphNodeBase;
struct TestComponentFinder;
struct TestNode : public GraphNodeBase<TestNode> {
unsigned index;
bool hasEdge[MaxVertices];
void findOutgoingEdges(TestComponentFinder& finder);
};
struct TestComponentFinder
: public ComponentFinder<TestNode, TestComponentFinder> {
explicit TestComponentFinder(uintptr_t sl)
: ComponentFinder<TestNode, TestComponentFinder>(sl) {}
};
using TestComponentFinder = ComponentFinder<TestNode>;
static TestNode Vertex[MaxVertices];
void TestNode::findOutgoingEdges(TestComponentFinder& finder) {
for (unsigned i = 0; i < MaxVertices; ++i) {
if (hasEdge[i]) {
finder.addEdgeTo(&Vertex[i]);
}
}
}
BEGIN_TEST(testFindSCCs) {
// no vertices
@ -65,8 +49,8 @@ BEGIN_TEST(testFindSCCs) {
// linear
setup(3);
edge(0, 1);
edge(1, 2);
CHECK(edge(0, 1));
CHECK(edge(1, 2));
run();
CHECK(group(0, -1));
CHECK(group(1, -1));
@ -76,29 +60,34 @@ BEGIN_TEST(testFindSCCs) {
// tree
setup(3);
edge(0, 1);
edge(0, 2);
CHECK(edge(0, 1));
CHECK(edge(0, 2));
run();
CHECK(group(0, -1));
CHECK(group(2, -1));
CHECK(group(1, -1));
if (resultsList && resultsList->index == 1) {
CHECK(group(1, -1));
CHECK(group(2, -1));
} else {
CHECK(group(2, -1));
CHECK(group(1, -1));
}
CHECK(end());
// cycles
setup(3);
edge(0, 1);
edge(1, 2);
edge(2, 0);
CHECK(edge(0, 1));
CHECK(edge(1, 2));
CHECK(edge(2, 0));
run();
CHECK(group(0, 1, 2, -1));
CHECK(end());
setup(4);
edge(0, 1);
edge(1, 2);
edge(2, 1);
edge(2, 3);
CHECK(edge(0, 1));
CHECK(edge(1, 2));
CHECK(edge(2, 1));
CHECK(edge(2, 3));
run();
CHECK(group(0, -1));
CHECK(group(1, 2, -1));
@ -108,20 +97,20 @@ BEGIN_TEST(testFindSCCs) {
// remaining
setup(2);
edge(0, 1);
CHECK(edge(0, 1));
run();
CHECK(remaining(0, 1, -1));
CHECK(end());
setup(2);
edge(0, 1);
CHECK(edge(0, 1));
run();
CHECK(group(0, -1));
CHECK(remaining(1, -1));
CHECK(end());
setup(2);
edge(0, 1);
CHECK(edge(0, 1));
run();
CHECK(group(0, -1));
CHECK(group(1, -1));
@ -139,14 +128,14 @@ void setup(unsigned count) {
vertex_count = count;
for (unsigned i = 0; i < MaxVertices; ++i) {
TestNode& v = Vertex[i];
v.gcGraphEdges.clear();
v.gcNextGraphNode = nullptr;
v.index = i;
memset(&v.hasEdge, 0, sizeof(v.hasEdge));
}
}
void edge(unsigned src_index, unsigned dest_index) {
Vertex[src_index].hasEdge[dest_index] = true;
bool edge(unsigned src_index, unsigned dest_index) {
return Vertex[src_index].gcGraphEdges.put(&Vertex[dest_index]);
}
void run() {
@ -203,27 +192,6 @@ bool end() {
}
END_TEST(testFindSCCs)
struct TestComponentFinder2;
struct TestNode2 : public GraphNodeBase<TestNode2> {
TestNode2* edge;
TestNode2() : edge(nullptr) {}
void findOutgoingEdges(TestComponentFinder2& finder);
};
struct TestComponentFinder2
: public ComponentFinder<TestNode2, TestComponentFinder2> {
explicit TestComponentFinder2(uintptr_t sl)
: ComponentFinder<TestNode2, TestComponentFinder2>(sl) {}
};
void TestNode2::findOutgoingEdges(TestComponentFinder2& finder) {
if (edge) {
finder.addEdgeTo(edge);
}
}
BEGIN_TEST(testFindSCCsStackLimit) {
/*
* Test what happens if recusion causes the stack to become full while
@ -240,19 +208,19 @@ BEGIN_TEST(testFindSCCsStackLimit) {
const unsigned max = 1000000;
const unsigned initial = 10;
TestNode2* vertices = new TestNode2[max]();
TestNode* vertices = new TestNode[max]();
for (unsigned i = initial; i < (max - 10); ++i) {
vertices[i].edge = &vertices[i + 1];
CHECK(vertices[i].gcGraphEdges.put(&vertices[i + 1]));
}
TestComponentFinder2 finder(cx->nativeStackLimit[JS::StackForSystemCode]);
TestComponentFinder finder(cx->nativeStackLimit[JS::StackForSystemCode]);
for (unsigned i = 0; i < max; ++i) {
finder.addNode(&vertices[i]);
}
TestNode2* r = finder.getResultsList();
TestNode* r = finder.getResultsList();
CHECK(r);
TestNode2* v = r;
TestNode* v = r;
unsigned count = 0;
while (v) {

View File

@ -685,6 +685,7 @@ JS_PUBLIC_API JSObject* JS_TransplantObject(JSContext* cx, HandleObject origobj,
MOZ_ASSERT(!target->is<CrossCompartmentWrapperObject>());
MOZ_ASSERT(origobj->getClass() == target->getClass());
ReleaseAssertObjectHasNoWrappers(cx, target);
JS::AssertCellIsNotGray(origobj);
JS::AssertCellIsNotGray(target);
RootedValue origv(cx, ObjectValue(*origobj));
@ -702,13 +703,9 @@ JS_PUBLIC_API JSObject* JS_TransplantObject(JSContext* cx, HandleObject origobj,
// destination, then we know that we won't find a wrapper in the
// destination's cross compartment map and that the same
// object will continue to work.
AutoRealmUnchecked ar(cx, origobj->nonCCWRealm());
AutoRealm ar(cx, origobj);
JSObject::swap(cx, origobj, target);
newIdentity = origobj;
// |origobj| might be gray so unmark it to avoid returning a possibly-gray
// object.
JS::ExposeObjectToActiveJS(newIdentity);
} else if (WrapperMap::Ptr p = destination->lookupWrapper(origv)) {
// There might already be a wrapper for the original object in
// the new compartment. If there is, we use its identity and swap
@ -739,7 +736,7 @@ JS_PUBLIC_API JSObject* JS_TransplantObject(JSContext* cx, HandleObject origobj,
// Lastly, update the original object to point to the new one.
if (origobj->compartment() != destination) {
RootedObject newIdentityWrapper(cx, newIdentity);
AutoRealmUnchecked ar(cx, origobj->nonCCWRealm());
AutoRealm ar(cx, origobj);
if (!JS_WrapObject(cx, &newIdentityWrapper)) {
MOZ_CRASH();
}

View File

@ -23,10 +23,6 @@
namespace js {
namespace gc {
struct ZoneComponentFinder;
} // namespace gc
class CrossCompartmentKey {
public:
enum DebuggerObjectKind : uint8_t {
@ -558,7 +554,7 @@ class JS::Compartment {
static void fixupCrossCompartmentWrappersAfterMovingGC(JSTracer* trc);
void fixupAfterMovingGC();
void findOutgoingEdges(js::gc::ZoneComponentFinder& finder);
MOZ_MUST_USE bool findSweepGroupEdges();
};
namespace js {

View File

@ -3366,8 +3366,7 @@ void Debugger::trace(JSTracer* trc) {
}
}
/* static */ void Debugger::findZoneEdges(Zone* zone,
js::gc::ZoneComponentFinder& finder) {
/* static */ bool Debugger::findSweepGroupEdges(Zone* zone) {
JSRuntime* rt = zone->runtimeFromMainThread();
for (Debugger* dbg : rt->debuggerList()) {
Zone* debuggerZone = dbg->object->zone();
@ -3381,7 +3380,9 @@ void Debugger::trace(JSTracer* trc) {
for (auto e = dbg->debuggeeZones.all(); !e.empty(); e.popFront()) {
Zone* debuggeeZone = e.front();
if (debuggeeZone->isGCMarking()) {
finder.addEdgeTo(debuggeeZone);
if (!zone->addSweepGroupEdgeTo(debuggeeZone)) {
return false;
}
}
}
} else {
@ -3397,10 +3398,13 @@ void Debugger::trace(JSTracer* trc) {
dbg->environments.hasKeyInZone(zone) ||
dbg->wasmInstanceScripts.hasKeyInZone(zone) ||
dbg->wasmInstanceSources.hasKeyInZone(zone)) {
finder.addEdgeTo(debuggerZone);
if (!zone->addSweepGroupEdgeTo(debuggerZone)) {
return false;
}
}
}
}
return true;
}
const ClassOps Debugger::classOps_ = {nullptr, /* addProperty */

View File

@ -988,7 +988,7 @@ class Debugger : private mozilla::LinkedListElement<Debugger> {
static void traceAllForMovingGC(JSTracer* trc);
static void sweepAll(FreeOp* fop);
static void detachAllDebuggersFromGlobal(FreeOp* fop, GlobalObject* global);
static void findZoneEdges(JS::Zone* v, gc::ZoneComponentFinder& finder);
static MOZ_MUST_USE bool findSweepGroupEdges(JS::Zone* zone);
#ifdef DEBUG
static bool isDebuggerCrossCompartmentEdge(JSObject* obj,
const js::gc::Cell* cell);

View File

@ -1468,17 +1468,9 @@ void ScriptSourceObject::setPrivate(JSRuntime* rt, const Value& value) {
// private data.
JS::AutoSuppressGCAnalysis nogc;
Value prevValue = getReservedSlot(PRIVATE_SLOT);
if (!prevValue.isUndefined()) {
if (auto releaseHook = rt->scriptPrivateReleaseHook) {
releaseHook(prevValue);
}
}
rt->releaseScriptPrivate(prevValue);
setReservedSlot(PRIVATE_SLOT, value);
if (!value.isUndefined()) {
if (auto addRefHook = rt->scriptPrivateAddRefHook) {
addRefHook(value);
}
}
rt->addRefScriptPrivate(value);
}
/* static */ bool JSScript::loadSource(JSContext* cx, ScriptSource* ss,

View File

@ -183,15 +183,6 @@ void js::DtoaCache::checkCacheAfterMovingGC() {
MOZ_ASSERT(!s || !IsForwarded(s));
}
namespace {
struct CheckGCThingAfterMovingGCFunctor {
template <class T>
void operator()(T* t) {
CheckGCThingAfterMovingGC(*t);
}
};
} // namespace
#endif // JSGC_HASH_TABLE_CHECKS
LexicalEnvironmentObject*
@ -464,24 +455,6 @@ void Realm::sweepObjectRealm() { objects_.sweepNativeIterators(); }
void Realm::sweepVarNames() { varNames_.sweep(); }
namespace {
struct TraceRootFunctor {
JSTracer* trc;
const char* name;
TraceRootFunctor(JSTracer* trc, const char* name) : trc(trc), name(name) {}
template <class T>
void operator()(T* t) {
return TraceRoot(trc, t, name);
}
};
struct NeedsSweepUnbarrieredFunctor {
template <class T>
bool operator()(T* t) const {
return IsAboutToBeFinalizedUnbarriered(t);
}
};
} // namespace
void Realm::sweepTemplateObjects() {
if (mappedArgumentsTemplate_ &&
IsAboutToBeFinalized(&mappedArgumentsTemplate_)) {

View File

@ -972,6 +972,18 @@ struct JSRuntime : public js::MallocProvider<JSRuntime> {
js::MainThreadData<JS::ScriptPrivateReferenceHook> scriptPrivateAddRefHook;
js::MainThreadData<JS::ScriptPrivateReferenceHook> scriptPrivateReleaseHook;
void addRefScriptPrivate(const JS::Value& value) {
if (!value.isUndefined() && scriptPrivateAddRefHook) {
scriptPrivateAddRefHook(value);
}
}
void releaseScriptPrivate(const JS::Value& value) {
if (!value.isUndefined() && scriptPrivateReleaseHook) {
scriptPrivateReleaseHook(value);
}
}
public:
#if defined(JS_BUILD_BINAST)
js::BinaryASTSupport& binast() { return binast_; }

View File

@ -7,6 +7,8 @@
#ifndef vm_TaggedProto_h
#define vm_TaggedProto_h
#include "mozilla/Maybe.h"
#include "gc/Tracer.h"
namespace js {
@ -129,13 +131,19 @@ class WrappedPtrOperations<TaggedProto, Wrapper> {
};
// If the TaggedProto is a JSObject pointer, convert to that type and call |f|
// with the pointer. If the TaggedProto is lazy, calls F::defaultValue.
template <typename F, typename... Args>
auto DispatchTyped(F f, const TaggedProto& proto, Args&&... args) {
// with the pointer. If the TaggedProto is lazy, returns None().
template <typename F>
auto MapGCThingTyped(const TaggedProto& proto, F&& f) {
if (proto.isObject()) {
return f(proto.toObject(), std::forward<Args>(args)...);
return mozilla::Some(f(proto.toObject()));
}
return F::defaultValue(proto);
using ReturnType = decltype(f(static_cast<JSObject*>(nullptr)));
return mozilla::Maybe<ReturnType>();
}
template <typename F>
bool ApplyGCThingTyped(const TaggedProto& proto, F&& f) {
return MapGCThingTyped(proto, [&f](auto t) { f(t); return true; }).isSome();
}
// Since JSObject pointers are either nullptr or a valid object and since the

View File

@ -42,7 +42,7 @@
using namespace js;
using JS::DispatchTyped;
using JS::ApplyGCThingTyped;
using JS::HandleValue;
using JS::Value;
using JS::ZoneSet;
@ -153,20 +153,12 @@ Node::Size Concrete<void>::size(mozilla::MallocSizeOf mallocSizeof) const {
MOZ_CRASH("null ubi::Node");
}
struct Node::ConstructFunctor : public js::BoolDefaultAdaptor<Value, false> {
template <typename T>
bool operator()(T* t, Node* node) {
node->construct(t);
return true;
}
};
Node::Node(const JS::GCCellPtr& thing) {
DispatchTyped(ConstructFunctor(), thing, this);
ApplyGCThingTyped(thing, [this](auto t) { this->construct(t); });
}
Node::Node(HandleValue value) {
if (!DispatchTyped(ConstructFunctor(), value, this)) {
if (!ApplyGCThingTyped(value, [this](auto t) { this->construct(t); })) {
construct<void>(nullptr);
}
}

View File

@ -1250,6 +1250,7 @@ class AstElemSegment : public AstNode {
AstRef targetTable() const { return targetTable_; }
AstRef& targetTableRef() { return targetTable_; }
bool isPassive() const { return offsetIfActive_ == nullptr; }
AstExpr* offsetIfActive() const { return offsetIfActive_; }
AstRefVector& elems() { return elems_; }
const AstRefVector& elems() const { return elems_; }

View File

@ -357,6 +357,11 @@ enum class Op {
Limit = 0x100
};
// TODO: RefFunc can't be incorporated into the opcode table until we're willing
// to handle it generally and we've renumbered RefEq, but we need it to express
// passive element segments.
constexpr uint16_t PlaceholderRefFunc = 0xd2;
inline bool IsPrefixByte(uint8_t b) { return b >= uint8_t(Op::FirstPrefix); }
// Opcodes in the "miscellaneous" opcode space.

View File

@ -7172,14 +7172,28 @@ static bool EncodeElemSegment(Encoder& e, AstElemSegment& segment) {
return false;
}
if (segment.isPassive()) {
if (!e.writeFixedU8(uint8_t(TypeCode::AnyFunc))) {
return false;
}
}
if (!e.writeVarU32(segment.elems().length())) {
return false;
}
for (const AstRef& elem : segment.elems()) {
// Passive segments have an initializer expression, for now restricted to a
// function index.
if (segment.isPassive() && !e.writeFixedU8(PlaceholderRefFunc)) {
return false;
}
if (!e.writeVarU32(elem.index())) {
return false;
}
if (segment.isPassive() && !e.writeFixedU8(uint8_t(Op::End))) {
return false;
}
}
return true;

View File

@ -2324,13 +2324,26 @@ static bool DecodeElemSection(Decoder& d, ModuleEnvironment* env) {
seg->tableIndex = tableIndex;
if (initializerKind == InitializerKind::Active ||
initializerKind == InitializerKind::ActiveWithIndex) {
InitExpr offset;
if (!DecodeInitializerExpression(d, env, ValType::I32, &offset)) {
return false;
switch (initializerKind) {
case InitializerKind::Active:
case InitializerKind::ActiveWithIndex: {
InitExpr offset;
if (!DecodeInitializerExpression(d, env, ValType::I32, &offset)) {
return false;
}
seg->offsetIfActive.emplace(offset);
break;
}
case InitializerKind::Passive: {
uint8_t form;
if (!d.readFixedU8(&form)) {
return d.fail("expected type form");
}
if (form != uint8_t(TypeCode::AnyFunc)) {
return d.fail("passive segments can only contain function references");
}
break;
}
seg->offsetIfActive.emplace(offset);
}
uint32_t numElems;
@ -2355,7 +2368,18 @@ static bool DecodeElemSection(Decoder& d, ModuleEnvironment* env) {
env->tables[tableIndex].importedOrExported;
#endif
// For passive segments we should use DecodeInitializerExpression() but we
// don't really want to generalize that function yet, so instead read the
// required Ref.Func and End here.
for (uint32_t i = 0; i < numElems; i++) {
if (initializerKind == InitializerKind::Passive) {
OpBytes op;
if (!d.readOp(&op) || op.b0 != PlaceholderRefFunc) {
return d.fail("failed to read ref.func operation");
}
}
uint32_t funcIndex;
if (!d.readVarU32(&funcIndex)) {
return d.fail("failed to read element function index");
@ -2372,6 +2396,13 @@ static bool DecodeElemSection(Decoder& d, ModuleEnvironment* env) {
}
#endif
if (initializerKind == InitializerKind::Passive) {
OpBytes end;
if (!d.readOp(&end) || end.b0 != uint16_t(Op::End)) {
return d.fail("failed to read end of ref.func expression");
}
}
seg->elemFuncIndices.infallibleAppend(funcIndex);
}

View File

@ -1 +1 @@
NSPR_4_20_RTM
753fe0f7964c

View File

@ -10,4 +10,3 @@
*/
#error "Do not include this header file."

19
nsprpub/configure vendored
View File

@ -783,7 +783,6 @@ with_android_ndk
with_android_toolchain
with_android_version
with_android_platform
with_gonk
with_dist_prefix
with_dist_bindir
with_dist_includedir
@ -873,6 +872,7 @@ datadir='${datarootdir}'
sysconfdir='${prefix}/etc'
sharedstatedir='${prefix}/com'
localstatedir='${prefix}/var'
includedir='${prefix}/include'
oldincludedir='/usr/include'
docdir='${datarootdir}/doc/${PACKAGE}'
@ -1484,7 +1484,6 @@ Optional Packages:
Android platform version, default 5 for arm, 9 for x86/mips
--with-android-platform=DIR
location of platform dir
--with-gonk=DIR location of gonk dir
--with-dist-prefix=DIST_PREFIX
place build files in DIST_PREFIX dist
--with-dist-bindir=DIR build execuatables in DIR DIST_PREFIX/bin
@ -2488,7 +2487,7 @@ test -n "$target_alias" &&
program_prefix=${target_alias}-
MOD_MAJOR_VERSION=4
MOD_MINOR_VERSION=20
MOD_MINOR_VERSION=21
MOD_PATCH_VERSION=0
NSPR_MODNAME=nspr20
_HAVE_PTHREADS=
@ -2656,19 +2655,6 @@ mipsel-*android*)
;;
esac
# Check whether --with-gonk was given.
if test "${with_gonk+set}" = set; then :
withval=$with_gonk; gonkdir=$withval
fi
if test -n "$gonkdir" ; then
$as_echo "#define ANDROID 1" >>confdefs.h
else
case "$target" in
*-android*|*-linuxandroid*)
if test -z "$android_ndk" ; then
@ -2754,7 +2740,6 @@ $as_echo "$android_platform" >&6; }
;;
esac
fi
dist_prefix='${MOD_DEPTH}/dist'
dist_bindir='${dist_prefix}/bin'

View File

@ -15,7 +15,7 @@ dnl ========================================================
dnl = Defaults
dnl ========================================================
MOD_MAJOR_VERSION=4
MOD_MINOR_VERSION=20
MOD_MINOR_VERSION=21
MOD_PATCH_VERSION=0
NSPR_MODNAME=nspr20
_HAVE_PTHREADS=
@ -147,21 +147,6 @@ mipsel-*android*)
;;
esac
dnl ========================================================
dnl = Gonk is a fork of Android used for Mozilla's B2G project.
dnl = Configuration is done largely by the top level config
dnl = and the specified gonk directory doesn't matter here.
dnl ========================================================
AC_ARG_WITH(gonk,
[ --with-gonk=DIR location of gonk dir],
gonkdir=$withval)
if test -n "$gonkdir" ; then
dnl Most things are directly configured by env vars when building for gonk
AC_DEFINE(ANDROID)
else
case "$target" in
*-android*|*-linuxandroid*)
if test -z "$android_ndk" ; then
@ -246,7 +231,6 @@ case "$target" in
AC_DEFINE(ANDROID)
;;
esac
fi
dnl ========================================================
dnl =

View File

@ -54,7 +54,7 @@ PRVersionDescription VERSION_DESC_NAME =
/* filename */ _PRODUCTION, /* the produced library name */
/* description */ "Portable runtime", /* what we are */
/* security */ "N/A", /* not applicable here */
/* copywrite */ "Copyright (c) 1998 Netscape Communications Corporation. All Rights Reserved",
/* copywrite */ "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/.",
/* comment */ "http://www.mozilla.org/MPL/",
/* specialString */ ""
};

View File

@ -54,7 +54,7 @@ PRVersionDescription VERSION_DESC_NAME =
/* filename */ _PRODUCTION, /* the produced library name */
/* description */ "Portable runtime", /* what we are */
/* security */ "N/A", /* not applicable here */
/* copywrite */ "Copyright (c) 1998 Netscape Communications Corporation. All Rights Reserved",
/* copywrite */ "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/.",
/* comment */ "http://www.mozilla.org/MPL/",
/* specialString */ ""
};

View File

@ -54,7 +54,7 @@ PRVersionDescription VERSION_DESC_NAME =
/* filename */ _PRODUCTION, /* the produced library name */
/* description */ "Portable runtime", /* what we are */
/* security */ "N/A", /* not applicable here */
/* copywrite */ "Copyright (c) 1998 Netscape Communications Corporation. All Rights Reserved",
/* copywrite */ "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/.",
/* comment */ "http://www.mozilla.org/MPL/",
/* specialString */ ""
};

View File

@ -83,7 +83,7 @@ extern void _MD_win32_map_accept_error(PRInt32 err);
extern void _MD_win32_map_acceptex_error(PRInt32 err);
#define _PR_MD_MAP_ACCEPTEX_ERROR _MD_win32_map_acceptex_error
extern PRInt32 _MD_win32_map_connect_error(PRInt32 err);
extern void _MD_win32_map_connect_error(PRInt32 err);
#define _PR_MD_MAP_CONNECT_ERROR _MD_win32_map_connect_error
extern void _MD_win32_map_bind_error(PRInt32 err);

View File

@ -14,7 +14,8 @@ PR_BEGIN_EXTERN_C
** functions.
*/
#if defined(_WIN32) && (_MSC_VER >= 1300) && \
(defined(_M_IX86) || defined(_M_AMD64) || defined(_M_ARM))
(defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM) || \
defined(_M_ARM64))
# include <intrin.h>
# pragma intrinsic(_BitScanForward,_BitScanReverse)
__forceinline static int __prBitScanForward32(unsigned int val)
@ -33,7 +34,8 @@ PR_BEGIN_EXTERN_C
# define pr_bitscan_clz32(val) __prBitScanReverse32(val)
# define PR_HAVE_BUILTIN_BITSCAN32
#elif ((__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) && \
(defined(__i386__) || defined(__x86_64__) || defined(__arm__))
(defined(__i386__) || defined(__x86_64__) || defined(__arm__) || \
defined(__aarch64__))
# define pr_bitscan_ctz32(val) __builtin_ctz(val)
# define pr_bitscan_clz32(val) __builtin_clz(val)
# define PR_HAVE_BUILTIN_BITSCAN32
@ -136,7 +138,7 @@ NSPR_API(PRIntn) PR_FloorLog2(PRUint32 i);
*/
#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_AMD64) || \
defined(_M_X64) || defined(_M_ARM))
defined(_M_X64) || defined(_M_ARM) || defined(_M_ARM64))
#include <stdlib.h>
#pragma intrinsic(_rotl, _rotr)
#define PR_ROTATE_LEFT32(a, bits) _rotl(a, bits)

View File

@ -31,11 +31,11 @@ PR_BEGIN_EXTERN_C
** The format of the version string is
** "<major version>.<minor version>[.<patch level>] [<Beta>]"
*/
#define PR_VERSION "4.20"
#define PR_VERSION "4.21 Beta"
#define PR_VMAJOR 4
#define PR_VMINOR 20
#define PR_VMINOR 21
#define PR_VPATCH 0
#define PR_BETA PR_FALSE
#define PR_BETA PR_TRUE
/*
** PRVersionCheck

View File

@ -247,7 +247,7 @@ static PRInt32 PR_CALLBACK Ipv6ToIpv4SocketRecvFrom(PRFileDesc *fd, void *buf,
#if defined(_PR_INET6_PROBE)
static PRBool ipv6_is_present;
extern PRBool _pr_test_ipv6_socket(void);
PR_EXTERN(PRBool) _pr_test_ipv6_socket(void);
#if !defined(_PR_INET6) && defined(_PR_HAVE_GETIPNODEBYNAME)
extern PRStatus _pr_find_getipnodebyname(void);

View File

@ -54,7 +54,7 @@ PRVersionDescription VERSION_DESC_NAME =
/* filename */ _PRODUCTION, /* the produced library name */
/* description */ "Portable runtime", /* what we are */
/* security */ "N/A", /* not applicable here */
/* copywrite */ "Copyright (c) 1998 Netscape Communications Corporation. All Rights Reserved",
/* copywrite */ "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/.",
/* comment */ "License information: http://www.mozilla.org/MPL/",
/* specialString */ ""
};

View File

@ -151,7 +151,7 @@ main()
int
main()
{
prinf("PASS\n");
printf("PASS\n");
return 0;
}
#endif

View File

@ -40,7 +40,7 @@ static char *compatible_version[] = {
"4.10", "4.10.1", "4.10.2", "4.10.3", "4.10.4",
"4.10.5", "4.10.6", "4.10.7", "4.10.8", "4.10.9",
"4.10.10", "4.11", "4.12", "4.13", "4.14", "4.15",
"4.16", "4.17", "4.18", "4.19",
"4.16", "4.17", "4.18", "4.19", "4.20",
PR_VERSION
};
@ -56,8 +56,8 @@ static char *incompatible_version[] = {
"3.0", "3.0.1",
"3.1", "3.1.1", "3.1.2", "3.1.3",
"3.5", "3.5.1",
"4.20.1",
"4.21", "4.21.1",
"4.21.1",
"4.22", "4.22.1",
"10.0", "11.1", "12.14.20"
};

View File

@ -213,7 +213,9 @@ NSS_CMSSignedData_Create
NSS_CMSSignedData_CreateCertsOnly
NSS_CMSSignedData_Destroy
NSS_CMSSignedData_GetContentInfo
NSS_CMSSignedData_GetDigestAlgs
NSS_CMSSignedData_GetSignerInfo
NSS_CMSSignedData_HasDigests
NSS_CMSSignedData_ImportCerts
NSS_CMSSignedData_SetDigestValue
NSS_CMSSignedData_SignerInfoCount