test(ivy): support `className` in micro benchmarks (#33392)

The styling algorithm requires that the `RNode` has a `className`
property in order to execute the fast-path. This changes adds the
emulation of this property.

PR Close #33392
This commit is contained in:
Misko Hevery 2019-10-24 20:28:11 -07:00 committed by Andrew Kushnir
parent b381497126
commit 6323a35468
5 changed files with 101 additions and 13 deletions

View File

@ -79,6 +79,7 @@ module.exports = function(config) {
'dist/all/@angular/compiler/test/render3/**',
'dist/all/@angular/core/test/bundling/**',
'dist/all/@angular/core/test/render3/ivy/**',
'dist/all/@angular/core/test/render3/perf/**',
'dist/all/@angular/elements/schematics/**',
'dist/all/@angular/examples/**/e2e_test/*',
'dist/all/@angular/language-service/**',

View File

@ -1,6 +1,6 @@
package(default_visibility = ["//visibility:private"])
load("//tools:defaults.bzl", "ng_rollup_bundle", "ts_library")
load("//tools:defaults.bzl", "jasmine_node_test", "ng_rollup_bundle", "ts_library")
ts_library(
name = "perf_lib",
@ -9,10 +9,18 @@ ts_library(
),
deps = [
"//packages/core",
"@npm//@types/jasmine",
"@npm//@types/node",
],
)
jasmine_node_test(
name = "perf",
deps = [
":perf_lib",
],
)
ng_rollup_bundle(
name = "class_binding",
entry_point = ":class_binding/index.ts",

View File

@ -7,7 +7,7 @@
*/
import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, RendererFactory3, RendererStyleFlags3} from '../../../src/render3/interfaces/renderer';
export class WebWorkerRenderNode implements RNode, RComment, RText {
export class MicroBenchmarkRenderNode implements RNode, RComment, RText {
textContent: string|null = null;
parentNode: RNode|null = null;
parentElement: RElement|null = null;
@ -15,15 +15,16 @@ export class WebWorkerRenderNode implements RNode, RComment, RText {
removeChild(oldChild: RNode): RNode { return oldChild; }
insertBefore(newChild: RNode, refChild: RNode|null, isViewRoot: boolean): void {}
appendChild(newChild: RNode): RNode { return newChild; }
className: string = '';
}
export class NoopRenderer implements ProceduralRenderer3 {
export class MicroBenchmarkRenderer implements ProceduralRenderer3 {
destroy(): void { throw new Error('Method not implemented.'); }
createComment(value: string): RComment { return new WebWorkerRenderNode(); }
createComment(value: string): RComment { return new MicroBenchmarkRenderNode(); }
createElement(name: string, namespace?: string|null|undefined): RElement {
return new WebWorkerRenderNode() as any as RElement;
return new MicroBenchmarkRenderNode() as any as RElement;
}
createText(value: string): RText { return new WebWorkerRenderNode(); }
createText(value: string): RText { return new MicroBenchmarkRenderNode(); }
destroyNode?: ((node: RNode) => void)|null|undefined;
appendChild(parent: RElement, newChild: RNode): void {}
insertBefore(parent: RNode, newChild: RNode, refChild: RNode|null): void {}
@ -32,10 +33,21 @@ export class NoopRenderer implements ProceduralRenderer3 {
parentNode(node: RNode): RElement|null { throw new Error('Method not implemented.'); }
nextSibling(node: RNode): RNode|null { throw new Error('Method not implemented.'); }
setAttribute(el: RElement, name: string, value: string, namespace?: string|null|undefined): void {
if (name === 'class' && isOurNode(el)) {
el.className = value;
}
}
removeAttribute(el: RElement, name: string, namespace?: string|null|undefined): void {}
addClass(el: RElement, name: string): void {}
removeClass(el: RElement, name: string): void {}
addClass(el: RElement, name: string): void {
if (isOurNode(el)) {
el.className = el.className === '' ? name : remove(el.className, name) + ' ' + name;
}
}
removeClass(el: RElement, name: string): void {
if (isOurNode(el)) {
el.className = remove(el.className, name);
}
}
setStyle(el: RElement, style: string, value: any, flags?: RendererStyleFlags3|undefined): void {}
removeStyle(el: RElement, style: string, flags?: RendererStyleFlags3|undefined): void {}
setProperty(el: RElement, name: string, value: any): void {}
@ -47,11 +59,39 @@ export class NoopRenderer implements ProceduralRenderer3 {
}
}
export class NoopRendererFactory implements RendererFactory3 {
export class MicroBenchmarkRendererFactory implements RendererFactory3 {
createRenderer(hostElement: RElement|null, rendererType: null): Renderer3 {
if (typeof global !== 'undefined') {
(global as any).Node = WebWorkerRenderNode;
(global as any).Node = MicroBenchmarkRenderNode;
}
return new NoopRenderer();
return new MicroBenchmarkRenderer();
}
}
function isOurNode(node: any): node is MicroBenchmarkRenderNode {
return node instanceof MicroBenchmarkRenderNode;
}
const enum Code {
SPACE = 32,
}
function remove(text: string, key: string): string {
let wasLastWhitespace = true;
for (let i = 0; i < text.length; i++) {
if (wasLastWhitespace) {
const start = i;
let same = true;
let k = 0;
while (k < key.length && (same = text.charCodeAt(i) === key.charCodeAt(k))) {
k++;
i++;
}
if (same && text.charCodeAt(i) == Code.SPACE) {
return text.substring(0, start) + text.substring(i + 1);
}
}
wasLastWhitespace = text.charCodeAt(i) <= Code.SPACE;
}
return text;
}

View File

@ -0,0 +1,38 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* 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 {ProceduralRenderer3} from '@angular/core/src/render3/interfaces/renderer';
import {MicroBenchmarkRenderNode, MicroBenchmarkRendererFactory} from './noop_renderer';
describe('MicroBenchmarkRenderNode', () => {
const renderer =
new MicroBenchmarkRendererFactory().createRenderer(null, null) as ProceduralRenderer3;
describe('className', () => {
it('should be available in global space', () => {
expect(Node).toBeDefined();
const node: any = new MicroBenchmarkRenderNode();
expect(node instanceof Node).toBeTruthy();
});
it('should emulate className', () => {
const node: any = new MicroBenchmarkRenderNode();
expect(node.className).toBe('');
renderer.setAttribute(node, 'foo', 'A AA BBB');
expect(node.className).toBe('');
renderer.setAttribute(node, 'class', 'A AA BBB');
expect(node.className).toBe('A AA BBB');
renderer.addClass(node, 'A');
expect(node.className).toBe('AA BBB A');
renderer.addClass(node, 'C');
expect(node.className).toBe('AA BBB A C');
renderer.removeClass(node, 'A');
expect(node.className).toBe('AA BBB C');
});
});
});

View File

@ -12,10 +12,11 @@ import {RendererFactory3, domRendererFactory3} from '../../../src/render3/interf
import {LView, LViewFlags, TView} from '../../../src/render3/interfaces/view';
import {insertView} from '../../../src/render3/node_manipulation';
import {NoopRendererFactory} from './noop_renderer';
import {MicroBenchmarkRendererFactory} from './noop_renderer';
const isBrowser = typeof process === 'undefined';
const rendererFactory: RendererFactory3 = isBrowser ? domRendererFactory3 : new NoopRendererFactory;
const rendererFactory: RendererFactory3 =
isBrowser ? domRendererFactory3 : new MicroBenchmarkRendererFactory;
const renderer = rendererFactory.createRenderer(null, null);
export function createAndRenderLView(