perf: don't create holey arrays (#32155)
Don't use `Array` constructor with the size value (ex. `new Array(5)`) - this will create a `HOLEY_ELEMENTS` array (even if this array is filled in later on!); https://v8.dev/blog/elements-kinds https://stackoverflow.com/questions/32054170/how-to-resize-an-array PR Close #32155
This commit is contained in:

committed by
Andrew Kushnir

parent
c957dfc167
commit
64770571b2
@ -11,9 +11,8 @@ import '../util/ng_dev_mode';
|
||||
import {OnDestroy} from '../interface/lifecycle_hooks';
|
||||
import {Type} from '../interface/type';
|
||||
import {throwCyclicDependencyError, throwInvalidProviderError, throwMixedMultiProviderError} from '../render3/errors';
|
||||
import {deepForEach} from '../util/array_utils';
|
||||
import {deepForEach, newArray} from '../util/array_utils';
|
||||
import {stringify} from '../util/stringify';
|
||||
|
||||
import {resolveForwardRef} from './forward_ref';
|
||||
import {InjectionToken} from './injection_token';
|
||||
import {Injector} from './injector';
|
||||
@ -428,7 +427,7 @@ function getUndecoratedInjectableFactory(token: Function) {
|
||||
// If the token has parameters then it has dependencies that we cannot resolve implicitly.
|
||||
const paramLength = token.length;
|
||||
if (paramLength > 0) {
|
||||
const args: string[] = new Array(paramLength).fill('?');
|
||||
const args: string[] = newArray(paramLength, '?');
|
||||
throw new Error(`Can't resolve all parameters for ${stringify(token)}: (${args.join(', ')}).`);
|
||||
}
|
||||
|
||||
|
@ -288,8 +288,8 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
|
||||
|
||||
const len = _providers.length;
|
||||
|
||||
this.keyIds = new Array(len);
|
||||
this.objs = new Array(len);
|
||||
this.keyIds = [];
|
||||
this.objs = [];
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
this.keyIds[i] = _providers[i].key.id;
|
||||
@ -339,7 +339,7 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
|
||||
|
||||
private _instantiateProvider(provider: ResolvedReflectiveProvider): any {
|
||||
if (provider.multiProvider) {
|
||||
const res = new Array(provider.resolvedFactories.length);
|
||||
const res = [];
|
||||
for (let i = 0; i < provider.resolvedFactories.length; ++i) {
|
||||
res[i] = this._instantiate(provider, provider.resolvedFactories[i]);
|
||||
}
|
||||
@ -455,7 +455,7 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
|
||||
}
|
||||
|
||||
function _mapProviders(injector: ReflectiveInjector_, fn: Function): any[] {
|
||||
const res: any[] = new Array(injector._providers.length);
|
||||
const res: any[] = [];
|
||||
for (let i = 0; i < injector._providers.length; ++i) {
|
||||
res[i] = fn(injector.getProviderAtIndex(i));
|
||||
}
|
||||
|
@ -7,10 +7,10 @@
|
||||
*/
|
||||
|
||||
import {Type, isType} from '../interface/type';
|
||||
import {newArray} from '../util/array_utils';
|
||||
import {ANNOTATIONS, PARAMETERS, PROP_METADATA} from '../util/decorators';
|
||||
import {global} from '../util/global';
|
||||
import {stringify} from '../util/stringify';
|
||||
|
||||
import {PlatformReflectionCapabilities} from './platform_reflection_capabilities';
|
||||
import {GetterFn, MethodFn, SetterFn} from './types';
|
||||
|
||||
@ -53,9 +53,9 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
|
||||
let result: any[][];
|
||||
|
||||
if (typeof paramTypes === 'undefined') {
|
||||
result = new Array(paramAnnotations.length);
|
||||
result = newArray(paramAnnotations.length);
|
||||
} else {
|
||||
result = new Array(paramTypes.length);
|
||||
result = newArray(paramTypes.length);
|
||||
}
|
||||
|
||||
for (let i = 0; i < result.length; i++) {
|
||||
@ -120,7 +120,7 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
|
||||
// based on function.length.
|
||||
// Note: We know that this is a real constructor as we checked
|
||||
// the content of the constructor above.
|
||||
return new Array((<any>type.length)).fill(undefined);
|
||||
return newArray<any[]>(type.length);
|
||||
}
|
||||
|
||||
parameters(type: Type<any>): any[][] {
|
||||
|
@ -5,6 +5,7 @@
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {newArray} from '../../util/array_utils';
|
||||
import {TAttributes, TElementNode, TNode, TNodeType} from '../interfaces/node';
|
||||
import {ProjectionSlots} from '../interfaces/projection';
|
||||
import {TVIEW, T_HOST} from '../interfaces/view';
|
||||
@ -81,7 +82,7 @@ export function ɵɵprojectionDef(projectionSlots?: ProjectionSlots): void {
|
||||
// projection slot with the wildcard selector.
|
||||
const numProjectionSlots = projectionSlots ? projectionSlots.length : 1;
|
||||
const projectionHeads: (TNode | null)[] = componentNode.projection =
|
||||
new Array(numProjectionSlots).fill(null);
|
||||
newArray(numProjectionSlots, null !as TNode);
|
||||
const tails: (TNode | null)[] = projectionHeads.slice();
|
||||
|
||||
let componentChild: TNode|null = componentNode.child;
|
||||
|
@ -48,7 +48,7 @@ class LQueries_ implements LQueries {
|
||||
if (tQueries !== null) {
|
||||
const noOfInheritedQueries =
|
||||
tView.contentQueries !== null ? tView.contentQueries[0] : tQueries.length;
|
||||
const viewLQueries: LQuery<any>[] = new Array(noOfInheritedQueries);
|
||||
const viewLQueries: LQuery<any>[] = [];
|
||||
|
||||
// An embedded view has queries propagated from a declaration view at the beginning of the
|
||||
// TQueries collection and up until a first content query declared in the embedded view. Only
|
||||
@ -57,7 +57,7 @@ class LQueries_ implements LQueries {
|
||||
for (let i = 0; i < noOfInheritedQueries; i++) {
|
||||
const tQuery = tQueries.getByIndex(i);
|
||||
const parentLQuery = this.queries[tQuery.indexInDeclarationView];
|
||||
viewLQueries[i] = parentLQuery.clone();
|
||||
viewLQueries.push(parentLQuery.clone());
|
||||
}
|
||||
|
||||
return new LQueries_(viewLQueries);
|
||||
@ -313,19 +313,18 @@ function materializeViewResults<T>(lView: LView, tQuery: TQuery, queryIndex: num
|
||||
if (lQuery.matches === null) {
|
||||
const tViewData = lView[TVIEW].data;
|
||||
const tQueryMatches = tQuery.matches !;
|
||||
const result: T|null[] = new Array(tQueryMatches.length / 2);
|
||||
const result: T|null[] = [];
|
||||
for (let i = 0; i < tQueryMatches.length; i += 2) {
|
||||
const matchedNodeIdx = tQueryMatches[i];
|
||||
if (matchedNodeIdx < 0) {
|
||||
// we at the <ng-template> marker which might have results in views created based on this
|
||||
// <ng-template> - those results will be in separate views though, so here we just leave
|
||||
// null as a placeholder
|
||||
result[i / 2] = null;
|
||||
result.push(null);
|
||||
} else {
|
||||
ngDevMode && assertDataInRange(tViewData, matchedNodeIdx);
|
||||
const tNode = tViewData[matchedNodeIdx] as TNode;
|
||||
result[i / 2] =
|
||||
createResultForNode(lView, tNode, tQueryMatches[i + 1], tQuery.metadata.read);
|
||||
result.push(createResultForNode(lView, tNode, tQueryMatches[i + 1], tQuery.metadata.read));
|
||||
}
|
||||
}
|
||||
lQuery.matches = result;
|
||||
|
@ -60,4 +60,14 @@ export function removeFromArray(arr: any[], index: number): any {
|
||||
} else {
|
||||
return arr.splice(index, 1)[0];
|
||||
}
|
||||
}
|
||||
|
||||
export function newArray<T = any>(size: number): T[];
|
||||
export function newArray<T>(size: number, value: T): T[];
|
||||
export function newArray<T>(size: number, value?: T): T[] {
|
||||
const list: T[] = [];
|
||||
for (let i = 0; i < size; i++) {
|
||||
list.push(value !);
|
||||
}
|
||||
return list;
|
||||
}
|
@ -73,7 +73,7 @@ export function elementDef(
|
||||
[ns, name] = splitNamespace(namespaceAndName);
|
||||
}
|
||||
bindings = bindings || [];
|
||||
const bindingDefs: BindingDef[] = new Array(bindings.length);
|
||||
const bindingDefs: BindingDef[] = [];
|
||||
for (let i = 0; i < bindings.length; i++) {
|
||||
const [bindingFlags, namespaceAndName, suffixOrSecurityContext] = bindings[i];
|
||||
|
||||
@ -93,7 +93,7 @@ export function elementDef(
|
||||
{flags: bindingFlags, ns, name, nonMinifiedName: name, securityContext, suffix};
|
||||
}
|
||||
outputs = outputs || [];
|
||||
const outputDefs: OutputDef[] = new Array(outputs.length);
|
||||
const outputDefs: OutputDef[] = [];
|
||||
for (let i = 0; i < outputs.length; i++) {
|
||||
const [target, eventName] = outputs[i];
|
||||
outputDefs[i] = {
|
||||
|
@ -12,6 +12,7 @@ import {INJECTOR, setCurrentInjector} from '../di/injector_compatibility';
|
||||
import {getInjectableDef, ɵɵInjectableDef} from '../di/interface/defs';
|
||||
import {APP_ROOT} from '../di/scope';
|
||||
import {NgModuleRef} from '../linker/ng_module_factory';
|
||||
import {newArray} from '../util/array_utils';
|
||||
import {stringify} from '../util/stringify';
|
||||
|
||||
import {DepDef, DepFlags, NgModuleData, NgModuleDefinition, NgModuleProviderDef, NodeFlags} from './types';
|
||||
@ -65,7 +66,7 @@ export function moduleDef(providers: NgModuleProviderDef[]): NgModuleDefinition
|
||||
|
||||
export function initNgModule(data: NgModuleData) {
|
||||
const def = data._def;
|
||||
const providers = data._providers = new Array(def.providers.length);
|
||||
const providers = data._providers = newArray(def.providers.length);
|
||||
for (let i = 0; i < def.providers.length; i++) {
|
||||
const provDef = def.providers[i];
|
||||
if (!(provDef.flags & NodeFlags.LazyProvider)) {
|
||||
@ -179,7 +180,7 @@ function _createClass(ngModule: NgModuleData, ctor: any, deps: DepDef[]): any {
|
||||
resolveNgModuleDep(ngModule, deps[0]), resolveNgModuleDep(ngModule, deps[1]),
|
||||
resolveNgModuleDep(ngModule, deps[2]));
|
||||
default:
|
||||
const depValues = new Array(len);
|
||||
const depValues = [];
|
||||
for (let i = 0; i < len; i++) {
|
||||
depValues[i] = resolveNgModuleDep(ngModule, deps[i]);
|
||||
}
|
||||
@ -201,7 +202,7 @@ function _callFactory(ngModule: NgModuleData, factory: any, deps: DepDef[]): any
|
||||
resolveNgModuleDep(ngModule, deps[0]), resolveNgModuleDep(ngModule, deps[1]),
|
||||
resolveNgModuleDep(ngModule, deps[2]));
|
||||
default:
|
||||
const depValues = Array(len);
|
||||
const depValues = [];
|
||||
for (let i = 0; i < len; i++) {
|
||||
depValues[i] = resolveNgModuleDep(ngModule, deps[i]);
|
||||
}
|
||||
|
@ -278,9 +278,9 @@ function createClass(
|
||||
resolveDep(view, elDef, allowPrivateServices, deps[1]),
|
||||
resolveDep(view, elDef, allowPrivateServices, deps[2]));
|
||||
default:
|
||||
const depValues = new Array(len);
|
||||
const depValues = [];
|
||||
for (let i = 0; i < len; i++) {
|
||||
depValues[i] = resolveDep(view, elDef, allowPrivateServices, deps[i]);
|
||||
depValues.push(resolveDep(view, elDef, allowPrivateServices, deps[i]));
|
||||
}
|
||||
return new ctor(...depValues);
|
||||
}
|
||||
@ -305,9 +305,9 @@ function callFactory(
|
||||
resolveDep(view, elDef, allowPrivateServices, deps[1]),
|
||||
resolveDep(view, elDef, allowPrivateServices, deps[2]));
|
||||
default:
|
||||
const depValues = Array(len);
|
||||
const depValues = [];
|
||||
for (let i = 0; i < len; i++) {
|
||||
depValues[i] = resolveDep(view, elDef, allowPrivateServices, deps[i]);
|
||||
depValues.push(resolveDep(view, elDef, allowPrivateServices, deps[i]));
|
||||
}
|
||||
return factory(...depValues);
|
||||
}
|
||||
|
@ -6,26 +6,28 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {newArray} from '../util/array_utils';
|
||||
|
||||
import {BindingDef, BindingFlags, NodeDef, NodeFlags, PureExpressionData, ViewData, asPureExpressionData} from './types';
|
||||
import {calcBindingFlags, checkAndUpdateBinding} from './util';
|
||||
|
||||
export function purePipeDef(checkIndex: number, argCount: number): NodeDef {
|
||||
// argCount + 1 to include the pipe as first arg
|
||||
return _pureExpressionDef(NodeFlags.TypePurePipe, checkIndex, new Array(argCount + 1));
|
||||
return _pureExpressionDef(NodeFlags.TypePurePipe, checkIndex, newArray(argCount + 1));
|
||||
}
|
||||
|
||||
export function pureArrayDef(checkIndex: number, argCount: number): NodeDef {
|
||||
return _pureExpressionDef(NodeFlags.TypePureArray, checkIndex, new Array(argCount));
|
||||
return _pureExpressionDef(NodeFlags.TypePureArray, checkIndex, newArray(argCount));
|
||||
}
|
||||
|
||||
export function pureObjectDef(checkIndex: number, propToIndex: {[p: string]: number}): NodeDef {
|
||||
const keys = Object.keys(propToIndex);
|
||||
const nbKeys = keys.length;
|
||||
const propertyNames = new Array(nbKeys);
|
||||
const propertyNames = [];
|
||||
for (let i = 0; i < nbKeys; i++) {
|
||||
const key = keys[i];
|
||||
const index = propToIndex[key];
|
||||
propertyNames[index] = key;
|
||||
propertyNames.push(key);
|
||||
}
|
||||
|
||||
return _pureExpressionDef(NodeFlags.TypePureObject, checkIndex, propertyNames);
|
||||
@ -33,17 +35,17 @@ export function pureObjectDef(checkIndex: number, propToIndex: {[p: string]: num
|
||||
|
||||
function _pureExpressionDef(
|
||||
flags: NodeFlags, checkIndex: number, propertyNames: string[]): NodeDef {
|
||||
const bindings: BindingDef[] = new Array(propertyNames.length);
|
||||
const bindings: BindingDef[] = [];
|
||||
for (let i = 0; i < propertyNames.length; i++) {
|
||||
const prop = propertyNames[i];
|
||||
bindings[i] = {
|
||||
bindings.push({
|
||||
flags: BindingFlags.TypeProperty,
|
||||
name: prop,
|
||||
ns: null,
|
||||
nonMinifiedName: prop,
|
||||
securityContext: null,
|
||||
suffix: null
|
||||
};
|
||||
});
|
||||
}
|
||||
return {
|
||||
// will bet set by the view definition
|
||||
@ -99,17 +101,17 @@ export function checkAndUpdatePureExpressionInline(
|
||||
let value: any;
|
||||
switch (def.flags & NodeFlags.Types) {
|
||||
case NodeFlags.TypePureArray:
|
||||
value = new Array(bindings.length);
|
||||
if (bindLen > 0) value[0] = v0;
|
||||
if (bindLen > 1) value[1] = v1;
|
||||
if (bindLen > 2) value[2] = v2;
|
||||
if (bindLen > 3) value[3] = v3;
|
||||
if (bindLen > 4) value[4] = v4;
|
||||
if (bindLen > 5) value[5] = v5;
|
||||
if (bindLen > 6) value[6] = v6;
|
||||
if (bindLen > 7) value[7] = v7;
|
||||
if (bindLen > 8) value[8] = v8;
|
||||
if (bindLen > 9) value[9] = v9;
|
||||
value = [];
|
||||
if (bindLen > 0) value.push(v0);
|
||||
if (bindLen > 1) value.push(v1);
|
||||
if (bindLen > 2) value.push(v2);
|
||||
if (bindLen > 3) value.push(v3);
|
||||
if (bindLen > 4) value.push(v4);
|
||||
if (bindLen > 5) value.push(v5);
|
||||
if (bindLen > 6) value.push(v6);
|
||||
if (bindLen > 7) value.push(v7);
|
||||
if (bindLen > 8) value.push(v8);
|
||||
if (bindLen > 9) value.push(v9);
|
||||
break;
|
||||
case NodeFlags.TypePureObject:
|
||||
value = {};
|
||||
|
@ -11,7 +11,7 @@ import {checkAndUpdateBinding, getParentRenderElement} from './util';
|
||||
|
||||
export function textDef(
|
||||
checkIndex: number, ngContentIndex: number | null, staticText: string[]): NodeDef {
|
||||
const bindings: BindingDef[] = new Array(staticText.length - 1);
|
||||
const bindings: BindingDef[] = [];
|
||||
for (let i = 1; i < staticText.length; i++) {
|
||||
bindings[i - 1] = {
|
||||
flags: BindingFlags.TypeProperty,
|
||||
|
Reference in New Issue
Block a user