Use null-prototype objects for maps

A {} has a bunch of names already defined on it (like ‘toString’).  When using an object as a map with arbitrary keys, it should not inherit from Object.prototype.
This commit is contained in:
Neil Fraser
2021-06-08 06:03:14 -07:00
committed by Neil Fraser
parent 53d8754ee9
commit 76b5517008
20 changed files with 46 additions and 60 deletions

View File

@@ -135,7 +135,7 @@ this.BLOCKLY_BOOT = function(root) {
f.write('\n')
f.write('// Load Blockly.\n')
f.write('goog.require(\'Blockly.requires\')\n')
f.write('goog.require(\'Blockly.requires\');\n')
f.write("""
delete root.BLOCKLY_DIR;

View File

@@ -28,14 +28,14 @@ Blockly.ComponentManager = function() {
* @type {!Object<string, !Blockly.ComponentManager.ComponentDatum>}
* @private
*/
this.componentData_ = {};
this.componentData_ = Object.create(null);
/**
* A map of capabilities to component ids.
* @type {!Object<string, Array<string>>}
* A map of capabilities to component IDs.
* @type {!Object<string, !Array<string>>}
* @private
*/
this.capabilityToComponentIds_ = {};
this.capabilityToComponentIds_ = Object.create(null);
};
/**

View File

@@ -31,11 +31,11 @@ Blockly.ContextMenuRegistry = function() {
Blockly.ContextMenuRegistry.registry = this;
/**
* Registry of all registered RegistryItems, keyed by id.
* @type {!Object<string, Blockly.ContextMenuRegistry.RegistryItem>}
* Registry of all registered RegistryItems, keyed by ID.
* @type {!Object<string, !Blockly.ContextMenuRegistry.RegistryItem>}
* @private
*/
this.registry_ = {};
this.registry_ = Object.create(null);
};
/**
@@ -97,7 +97,7 @@ Blockly.ContextMenuRegistry.registry = null;
*/
Blockly.ContextMenuRegistry.prototype.register = function(item) {
if (this.registry_[item.id]) {
throw Error('Menu item with id "' + item.id + '" is already registered.');
throw Error('Menu item with ID "' + item.id + '" is already registered.');
}
this.registry_[item.id] = item;
};
@@ -108,11 +108,10 @@ Blockly.ContextMenuRegistry.prototype.register = function(item) {
* @throws {Error} if an item with the given ID does not exist.
*/
Blockly.ContextMenuRegistry.prototype.unregister = function(id) {
if (this.registry_[id]) {
delete this.registry_[id];
} else {
throw new Error('Menu item with id "' + id + '" not found.');
if (!this.registry_[id]) {
throw new Error('Menu item with ID "' + id + '" not found.');
}
delete this.registry_[id];
};
/**
@@ -120,10 +119,7 @@ Blockly.ContextMenuRegistry.prototype.unregister = function(id) {
* @return {?Blockly.ContextMenuRegistry.RegistryItem} RegistryItem or null if not found
*/
Blockly.ContextMenuRegistry.prototype.getItem = function(id) {
if (this.registry_[id]) {
return this.registry_[id];
}
return null;
return this.registry_[id] || null;
};
/**

View File

@@ -28,7 +28,7 @@ goog.requireType('Blockly.Block');
* The set of all registered extensions, keyed by extension name/id.
* @private
*/
Blockly.Extensions.ALL_ = {};
Blockly.Extensions.ALL_ = Object.create(null);
/**
* Registers a new extension function. Extensions are functions that help

View File

@@ -228,5 +228,6 @@ Blockly.Names.prototype.safeName_ = function(name) {
* @return {boolean} True if names are the same.
*/
Blockly.Names.equals = function(name1, name2) {
// name1.localeCompare(name2) is slower.
return name1.toLowerCase() == name2.toLowerCase();
};

View File

@@ -404,7 +404,7 @@ Blockly.Procedures.getDefinition = function(name, workspace) {
blocks[i]);
var tuple = procedureBlock.getProcedureDef();
if (tuple && Blockly.Names.equals(tuple[0], name)) {
return blocks[i];
return procedureBlock;
}
}
}

View File

@@ -33,7 +33,7 @@ goog.requireType('Blockly.ToolboxItem');
*
* @type {Object<string, Object<string, function(new:?)>>}
*/
Blockly.registry.typeMap_ = {};
Blockly.registry.typeMap_ = Object.create(null);
/**
* The string used to register the default class for a type of plugin.
@@ -137,7 +137,7 @@ Blockly.registry.register = function(
var typeRegistry = Blockly.registry.typeMap_[type];
// If the type registry has not been created, create it.
if (!typeRegistry) {
typeRegistry = Blockly.registry.typeMap_[type] = {};
typeRegistry = Blockly.registry.typeMap_[type] = Object.create(null);
}
// Validate that the given class has all the required properties.

View File

@@ -587,10 +587,10 @@ Blockly.blockRendering.ConstantProvider.prototype.setTheme = function(
/**
* The block styles map.
* @type {Object<string, Blockly.Theme.BlockStyle>}
* @type {Object<string, !Blockly.Theme.BlockStyle>}
* @package
*/
this.blockStyles = {};
this.blockStyles = Object.create(null);
var blockStyles = theme.blockStyles;
for (var key in blockStyles) {

View File

@@ -45,17 +45,17 @@ Blockly.zelos.PathObject = function(root, style, constants) {
/**
* The selected path of the block.
* @type {SVGElement}
* @type {?SVGElement}
* @private
*/
this.svgPathSelected_ = null;
/**
* The outline paths on the block.
* @type {!Object<string,!SVGElement>}
* @type {!Object<string, !SVGElement>}
* @private
*/
this.outlines_ = {};
this.outlines_ = Object.create(null);
/**
* A set used to determine which outlines were used during a draw pass. The
@@ -98,8 +98,7 @@ Blockly.zelos.PathObject.prototype.applyColour = function(block) {
}
// Apply colour to outlines.
for (var i = 0, keys = Object.keys(this.outlines_),
key; (key = keys[i]); i++) {
for (var key in this.outlines_) {
this.outlines_[key].setAttribute('fill', this.style.colourTertiary);
}
};
@@ -110,8 +109,7 @@ Blockly.zelos.PathObject.prototype.applyColour = function(block) {
Blockly.zelos.PathObject.prototype.flipRTL = function() {
Blockly.zelos.PathObject.superClass_.flipRTL.call(this);
// Mirror each input outline path.
for (var i = 0, keys = Object.keys(this.outlines_),
key; (key = keys[i]); i++) {
for (var key in this.outlines_) {
this.outlines_[key].setAttribute('transform', 'scale(-1 1)');
}
};
@@ -175,9 +173,8 @@ Blockly.zelos.PathObject.prototype.updateShapeForInputHighlight = function(
* @package
*/
Blockly.zelos.PathObject.prototype.beginDrawing = function() {
this.remainingOutlines_ = {};
for (var i = 0, keys = Object.keys(this.outlines_),
key; (key = keys[i]); i++) {
this.remainingOutlines_ = Object.create(null);
for (var key in this.outlines_) {
// The value set here isn't used anywhere, we are just using the
// object as a Set data structure.
this.remainingOutlines_[key] = 1;
@@ -192,8 +189,7 @@ Blockly.zelos.PathObject.prototype.endDrawing = function() {
// Go through all remaining outlines that were not used this draw pass, and
// remove them.
if (this.remainingOutlines_) {
for (var i = 0, keys = Object.keys(this.remainingOutlines_),
key; (key = keys[i]); i++) {
for (var key in this.remainingOutlines_) {
this.removeOutlinePath_(key);
}
}

View File

@@ -125,10 +125,10 @@ Blockly.Toolbox = function(workspace) {
/**
* A map from toolbox item IDs to toolbox items.
* @type {!Object<string, Blockly.IToolboxItem>}
* @type {!Object<string, !Blockly.IToolboxItem>}
* @protected
*/
this.contentMap_ = {};
this.contentMap_ = Object.create(null);
/**
* Position of the toolbox and flyout relative to the workspace.
@@ -393,7 +393,7 @@ Blockly.Toolbox.prototype.render = function(toolboxDef) {
}
}
this.contents_ = [];
this.contentMap_ = {};
this.contentMap_ = Object.create(null);
this.renderContents_(toolboxDef['contents']);
this.position();
};
@@ -537,7 +537,7 @@ Blockly.Toolbox.prototype.getClientRect = function() {
* @public
*/
Blockly.Toolbox.prototype.getToolboxItemById = function(id) {
return this.contentMap_[id];
return this.contentMap_[id] || null;
};
/**

View File

@@ -411,11 +411,7 @@ Blockly.Trashcan.prototype.openFlyout = function() {
if (this.contentsIsOpen()) {
return;
}
var xml = [];
for (var i = 0, text; (text = this.contents_[i]); i++) {
xml[i] = Blockly.Xml.textToDom(text);
}
var xml = this.contents_.map(Blockly.Xml.textToDom);
this.flyout.show(xml);
this.fireUiEvent_(true);
};
@@ -427,7 +423,6 @@ Blockly.Trashcan.prototype.closeFlyout = function() {
if (!this.contentsIsOpen()) {
return;
}
this.flyout.hide();
this.fireUiEvent_(false);
};

View File

@@ -232,7 +232,7 @@ Blockly.utils.dom.setCssTransform = function(element, transform) {
Blockly.utils.dom.startTextWidthCache = function() {
Blockly.utils.dom.cacheReference_++;
if (!Blockly.utils.dom.cacheWidths_) {
Blockly.utils.dom.cacheWidths_ = {};
Blockly.utils.dom.cacheWidths_ = Object.create(null);
}
};

View File

@@ -353,7 +353,7 @@ Blockly.utils.toolbox.xmlToJsonArray_ = function(toolboxDef) {
obj['contents'] = Blockly.utils.toolbox.xmlToJsonArray_(child);
}
// Add xml attributes to object
// Add XML attributes to object
Blockly.utils.toolbox.addAttributes_(child, obj);
arr.push(obj);
}

View File

@@ -36,7 +36,7 @@ Blockly.Warning = function(block) {
Blockly.Warning.superClass_.constructor.call(this, block);
this.createIcon();
// The text_ object can contain multiple warnings.
this.text_ = {};
this.text_ = Object.create(null);
};
Blockly.utils.object.inherits(Blockly.Warning, Blockly.Icon);

View File

@@ -169,7 +169,7 @@ Blockly.WorkspaceSvg = function(
* @type {!Object<string, ?function(!Blockly.Workspace):!Array<!Element>>}
* @private
*/
this.toolboxCategoryCallbacks_ = {};
this.toolboxCategoryCallbacks_ = Object.create(null);
/**
* Map from function names to callbacks, for deciding what to do when a button
@@ -177,7 +177,7 @@ Blockly.WorkspaceSvg = function(
* @type {!Object<string, ?function(!Blockly.FlyoutButton)>}
* @private
*/
this.flyoutButtonCallbacks_ = {};
this.flyoutButtonCallbacks_ = Object.create(null);
if (Blockly.Variables && Blockly.Variables.flyoutCategory) {
this.registerToolboxCategoryCallback(Blockly.VARIABLE_CATEGORY_NAME,

View File

@@ -887,7 +887,6 @@ WorkspaceFactoryController.prototype.clearAll = function() {
if (!confirm(msg)) {
return;
}
var hasCategories = this.model.hasElements();
this.model.clearToolboxList();
this.view.clearToolboxTabs();
this.model.savePreloadXml(Blockly.utils.xml.createElement('xml'));
@@ -1209,9 +1208,9 @@ WorkspaceFactoryController.prototype.importBlocks = function(file, format) {
// If an imported block type is already defined, check if the user wants
// to override the current block definition.
if (controller.model.hasDefinedBlockTypes(blockTypes)) {
var msg = 'An imported block uses the same name as a block '
+ 'already in your toolbox. Are you sure you want to override the '
+ 'currently defined block?';
var msg = 'An imported block uses the same name as a block ' +
'already in your toolbox. Are you sure you want to override the ' +
'currently defined block?';
var continueAnyway = confirm(msg);
BlocklyDevTools.Analytics.onWarning(msg);
if (!continueAnyway) {

View File

@@ -360,7 +360,7 @@ WorkspaceFactoryModel.prototype.addCustomTag = function(category, tag) {
* @param {!Element} xml The XML to be saved.
*/
WorkspaceFactoryModel.prototype.savePreloadXml = function(xml) {
this.preloadXml = xml
this.preloadXml = xml;
};
/**
@@ -445,8 +445,8 @@ WorkspaceFactoryModel.prototype.updateLibBlockTypes = function(blockTypes) {
* @return {boolean} True if blockType is defined, false otherwise.
*/
WorkspaceFactoryModel.prototype.isDefinedBlockType = function(blockType) {
var isStandardBlock = StandardCategories.coreBlockTypes.indexOf(blockType)
!= -1;
var isStandardBlock =
StandardCategories.coreBlockTypes.indexOf(blockType) != -1;
var isLibBlock = this.libBlockTypes.indexOf(blockType) != -1;
var isImportedBlock = this.importedBlockTypes.indexOf(blockType) != -1;
return (isStandardBlock || isLibBlock || isImportedBlock);

View File

@@ -14,12 +14,12 @@ goog.provide('Blockly.PHP.procedures');
goog.require('Blockly.PHP');
Blockly.PHP['procedures_defreturn'] = function(block) {
// Define a procedure with a return value.
// First, add a 'global' statement for every variable that is not shadowed by
// a local parameter.
var globals = [];
var varName;
var workspace = block.workspace;
var variables = Blockly.Variables.allUsedVarModels(workspace) || [];
for (var i = 0, variable; variable = variables[i]; i++) {

View File

@@ -20,7 +20,6 @@ Blockly.Python['procedures_defreturn'] = function(block) {
// First, add a 'global' statement for every variable that is not shadowed by
// a local parameter.
var globals = [];
var varName;
var workspace = block.workspace;
var variables = Blockly.Variables.allUsedVarModels(workspace) || [];
for (var i = 0, variable; variable = variables[i]; i++) {

View File

@@ -398,7 +398,7 @@ return gulp.src(maybeAddClosureLibrary(['core/**/**/*.js']))
const requires = `goog.addDependency("base.js", [], []);
// Load Blockly.
goog.require('Blockly.requires')
goog.require('Blockly.requires');
`;
fs.writeFileSync('blockly_uncompressed.js',
header +