[IMP] *: use lower level loops in hot code

The goal is to gain a few % of perf improvement.

closes #125
This commit is contained in:
Géry Debongnie
2019-05-28 14:04:08 +02:00
parent 94208d9cbb
commit bced9f3d04
8 changed files with 166 additions and 122 deletions
+74 -62
View File
@@ -235,24 +235,27 @@ export class Component<
} }
_callMounted() { _callMounted() {
const children = this.__owl__.children; const __owl__ = this.__owl__;
const children = __owl__.children;
for (let id in children) { for (let id in children) {
const comp = children[id]; const comp = children[id];
if (!comp.__owl__.isMounted && this.el!.contains(comp.el)) { if (!comp.__owl__.isMounted && this.el!.contains(comp.el)) {
comp._callMounted(); comp._callMounted();
} }
} }
this.__owl__.isMounted = true; __owl__.isMounted = true;
for (let key in this.__owl__.mountedHandlers) { const handlers = __owl__.mountedHandlers;
this.__owl__.mountedHandlers[key](); for (let key in handlers) {
handlers[key]();
} }
this.mounted(); this.mounted();
} }
_callWillUnmount() { _callWillUnmount() {
this.willUnmount(); this.willUnmount();
this.__owl__.isMounted = false; const __owl__ = this.__owl__;
const children = this.__owl__.children; __owl__.isMounted = false;
const children = __owl__.children;
for (let id in children) { for (let id in children) {
const comp = children[id]; const comp = children[id];
if (comp.__owl__.isMounted) { if (comp.__owl__.isMounted) {
@@ -269,7 +272,8 @@ export class Component<
} }
async render(force: boolean = false, patchQueue?: any[]): Promise<void> { async render(force: boolean = false, patchQueue?: any[]): Promise<void> {
if (!this.__owl__.isMounted) { const __owl__ = this.__owl__;
if (!__owl__.isMounted) {
return; return;
} }
const shouldPatch: boolean = !patchQueue; const shouldPatch: boolean = !patchQueue;
@@ -277,26 +281,23 @@ export class Component<
patchQueue = []; patchQueue = [];
} }
const renderVDom = this._render(force, patchQueue); const renderVDom = this._render(force, patchQueue);
const renderId = this.__owl__.renderId; const renderId = __owl__.renderId;
await renderVDom; await renderVDom;
if ( if (shouldPatch && __owl__.isMounted && renderId === __owl__.renderId) {
shouldPatch &&
this.__owl__.isMounted &&
renderId === this.__owl__.renderId
) {
// we only update the vnode and the actual DOM if no other rendering // we only update the vnode and the actual DOM if no other rendering
// occurred between now and when the render method was initially called. // occurred between now and when the render method was initially called.
for (let i = 0; i < patchQueue!.length; i++) { const patchLen = patchQueue!.length;
for (let i = 0; i < patchLen; i++) {
const patch = patchQueue![i]; const patch = patchQueue![i];
patch.push(patch[0].willPatch()); patch.push(patch[0].willPatch());
} }
for (let i = 0; i < patchQueue!.length; i++) { for (let i = 0; i < patchLen; i++) {
const patch = patchQueue![i]; const patch = patchQueue![i];
patch[0]._patch(patch[1]); patch[0]._patch(patch[1]);
} }
for (let i = patchQueue!.length - 1; i >= 0; i--) { for (let i = patchLen - 1; i >= 0; i--) {
const patch = patchQueue![i]; const patch = patchQueue![i];
patch[0].patched(patch[2]); patch[0].patched(patch[2]);
} }
@@ -304,9 +305,10 @@ export class Component<
} }
destroy() { destroy() {
if (!this.__owl__.isDestroyed) { const __owl__ = this.__owl__;
if (!__owl__.isDestroyed) {
const el = this.el; const el = this.el;
this._destroy(this.__owl__.parent); this._destroy(__owl__.parent);
if (el) { if (el) {
el.remove(); el.remove();
} }
@@ -314,23 +316,24 @@ export class Component<
} }
_destroy(parent) { _destroy(parent) {
const isMounted = this.__owl__.isMounted; const __owl__ = this.__owl__;
const isMounted = __owl__.isMounted;
if (isMounted) { if (isMounted) {
this.willUnmount(); this.willUnmount();
this.__owl__.isMounted = false; __owl__.isMounted = false;
} }
const children = Object.values(this.__owl__.children); const children = __owl__.children;
for (let child of children) { for (let key in children) {
child._destroy(this); children[key]._destroy(this);
} }
if (parent) { if (parent) {
let id = this.__owl__.id; let id = __owl__.id;
delete parent.__owl__.children[id]; delete parent.__owl__.children[id];
this.__owl__.parent = null; __owl__.parent = null;
} }
this.clear(); this.clear();
this.__owl__.isDestroyed = true; __owl__.isDestroyed = true;
delete this.__owl__.vnode; delete __owl__.vnode;
} }
shouldUpdate(nextProps: Props): boolean { shouldUpdate(nextProps: Props): boolean {
@@ -347,11 +350,12 @@ export class Component<
* mode or not. * mode or not.
*/ */
async updateEnv(nextEnv: Partial<T>): Promise<void> { async updateEnv(nextEnv: Partial<T>): Promise<void> {
if (this.__owl__.parent && this.__owl__.parent.env === this.env) { const __owl__ = this.__owl__;
if (__owl__.parent && __owl__.parent.env === this.env) {
this.env = Object.create(this.env); this.env = Object.create(this.env);
} }
Object.assign(this.env, nextEnv); Object.assign(this.env, nextEnv);
if (this.__owl__.isMounted) { if (__owl__.isMounted) {
await this.render(true); await this.render(true);
} }
} }
@@ -385,19 +389,22 @@ export class Component<
} }
_patch(vnode) { _patch(vnode) {
this.__owl__.renderPromise = null; const __owl__ = this.__owl__;
const target = this.__owl__.vnode || document.createElement(vnode.sel!); __owl__.renderPromise = null;
this.__owl__.vnode = patch(target, vnode); const target = __owl__.vnode || document.createElement(vnode.sel!);
__owl__.vnode = patch(target, vnode);
} }
_prepare(): Promise<VNode> { _prepare(): Promise<VNode> {
this.__owl__.renderProps = this.props; const __owl__ = this.__owl__;
this.__owl__.renderPromise = this._prepareAndRender(); __owl__.renderProps = this.props;
return this.__owl__.renderPromise; __owl__.renderPromise = this._prepareAndRender();
return __owl__.renderPromise;
} }
async _prepareAndRender(): Promise<VNode> { async _prepareAndRender(): Promise<VNode> {
await this.willStart(); await this.willStart();
if (this.__owl__.isDestroyed) { const __owl__ = this.__owl__;
if (__owl__.isDestroyed) {
return Promise.resolve(h("div")); return Promise.resolve(h("div"));
} }
const qweb = this.env.qweb; const qweb = this.env.qweb;
@@ -428,7 +435,7 @@ export class Component<
} }
} }
} }
this.__owl__.render = qweb.render.bind(qweb, this.template); __owl__.render = qweb.render.bind(qweb, this.template);
this._observeState(); this._observeState();
return this._render(); return this._render();
} }
@@ -436,25 +443,26 @@ export class Component<
force: boolean = false, force: boolean = false,
patchQueue: any[] = [] patchQueue: any[] = []
): Promise<VNode> { ): Promise<VNode> {
this.__owl__.renderId++; const __owl__ = this.__owl__;
__owl__.renderId++;
const promises: Promise<void>[] = []; const promises: Promise<void>[] = [];
const patch: any[] = [this]; const patch: any[] = [this];
if (this.__owl__.isMounted) { if (__owl__.isMounted) {
patchQueue.push(patch); patchQueue.push(patch);
} }
if (this.__owl__.observer) { if (__owl__.observer) {
this.__owl__.observer.allowMutations = false; __owl__.observer.allowMutations = false;
} }
let vnode = this.__owl__.render!(this, { let vnode = __owl__.render!(this, {
promises, promises,
handlers: this.__owl__.boundHandlers, handlers: __owl__.boundHandlers,
mountedHandlers: this.__owl__.mountedHandlers, mountedHandlers: __owl__.mountedHandlers,
forceUpdate: force, forceUpdate: force,
patchQueue patchQueue
}); });
patch.push(vnode); patch.push(vnode);
if (this.__owl__.observer) { if (__owl__.observer) {
this.__owl__.observer.allowMutations = true; __owl__.observer.allowMutations = true;
} }
// this part is critical for the patching process to be done correctly. The // this part is critical for the patching process to be done correctly. The
@@ -462,38 +470,41 @@ export class Component<
// will update its own vnode representation without the knowledge of the // will update its own vnode representation without the knowledge of the
// parent widget. With this, we make sure that the parent widget will be // parent widget. With this, we make sure that the parent widget will be
// able to patch itself properly after // able to patch itself properly after
vnode.key = this.__owl__.id; vnode.key = __owl__.id;
this.__owl__.renderProps = this.props; __owl__.renderProps = this.props;
this.__owl__.renderPromise = Promise.all(promises).then(() => vnode); __owl__.renderPromise = Promise.all(promises).then(() => vnode);
return this.__owl__.renderPromise; return __owl__.renderPromise;
} }
/** /**
* Only called by qweb t-widget directive * Only called by qweb t-widget directive
*/ */
_mount(vnode: VNode, elm: HTMLElement): VNode { _mount(vnode: VNode, elm: HTMLElement): VNode {
this.__owl__.vnode = patch(elm, vnode); const __owl__ = this.__owl__;
if (this.__owl__.parent!.__owl__.isMounted && !this.__owl__.isMounted) { __owl__.vnode = patch(elm, vnode);
if (__owl__.parent!.__owl__.isMounted && !__owl__.isMounted) {
this._callMounted(); this._callMounted();
} }
return this.__owl__.vnode; return __owl__.vnode;
} }
/** /**
* Only called by qweb t-widget directive (when t-keepalive is set) * Only called by qweb t-widget directive (when t-keepalive is set)
*/ */
_remount() { _remount() {
if (!this.__owl__.isMounted) { const __owl__ = this.__owl__;
this.__owl__.isMounted = true; if (!__owl__.isMounted) {
__owl__.isMounted = true;
this.mounted(); this.mounted();
} }
} }
_observeState() { _observeState() {
if (this.state) { if (this.state) {
this.__owl__.observer = new Observer(); const __owl__ = this.__owl__;
this.__owl__.observer.observe(this.state); __owl__.observer = new Observer();
this.__owl__.observer.notifyCB = this.render.bind(this); __owl__.observer.observe(this.state);
__owl__.observer.notifyCB = this.render.bind(this);
} }
} }
@@ -577,7 +588,7 @@ function isValidProp(prop, propDef): boolean {
// If this code is executed, this means that we want to check if a prop // If this code is executed, this means that we want to check if a prop
// matches at least one of its descriptor. // matches at least one of its descriptor.
let result = false; let result = false;
for (let i = 0; i < propDef.length; i++) { for (let i = 0, iLen = propDef.length; i < iLen; i++) {
result = result || isValidProp(prop, propDef[i]); result = result || isValidProp(prop, propDef[i]);
} }
return result; return result;
@@ -585,13 +596,14 @@ function isValidProp(prop, propDef): boolean {
// propsDef is an object // propsDef is an object
let result = isValidProp(prop, propDef.type); let result = isValidProp(prop, propDef.type);
if (propDef.type === Array) { if (propDef.type === Array) {
for (let i = 0; i < prop.length; i++) { for (let i = 0, iLen = prop.length; i < iLen; i++) {
result = result && isValidProp(prop[i], propDef.element); result = result && isValidProp(prop[i], propDef.element);
} }
} }
if (propDef.type === Object) { if (propDef.type === Object) {
for (let key in propDef.shape) { const shape = propDef.shape;
result = result && isValidProp(prop[key], propDef.shape[key]); for (let key in shape) {
result = result && isValidProp(prop[key], shape[key]);
} }
} }
return result; return result;
+2 -1
View File
@@ -64,7 +64,8 @@ export class EventBus {
*/ */
trigger(eventType: string, ...args: any[]) { trigger(eventType: string, ...args: any[]) {
const subs = this.subscriptions[eventType] || []; const subs = this.subscriptions[eventType] || [];
for (let sub of subs) { for (let i = 0, iLen = subs.length; i < iLen; i++) {
const sub = subs[i];
sub.callback.call(sub.owner, ...args); sub.callback.call(sub.owner, ...args);
} }
} }
+8 -6
View File
@@ -30,10 +30,13 @@ const methodsToPatch = [
"sort", "sort",
"reverse" "reverse"
]; ];
const methodLen = methodsToPatch.length;
const ArrayProto = Array.prototype; const ArrayProto = Array.prototype;
const ModifiedArrayProto = Object.create(ArrayProto); const ModifiedArrayProto = Object.create(ArrayProto);
for (let method of methodsToPatch) { for (let i = 0; i < methodLen; i++) {
const method = methodsToPatch[i];
const initialMethod = ArrayProto[method]; const initialMethod = ArrayProto[method];
ModifiedArrayProto[method] = function(...args) { ModifiedArrayProto[method] = function(...args) {
if (!this.__observer__.allowMutations) { if (!this.__observer__.allowMutations) {
@@ -57,8 +60,8 @@ for (let method of methodsToPatch) {
break; break;
} }
if (inserted) { if (inserted) {
for (let elem of inserted) { for (let i = 0, iLen = inserted.length; i < iLen; i++) {
this.__observer__.observe(elem, this); this.__observer__.observe(inserted[i], this);
} }
} }
return initialMethod.call(this, ...args); return initialMethod.call(this, ...args);
@@ -112,10 +115,9 @@ export class Observer {
} }
_observeObj<T extends { __owl__?: any }>(obj: T, parent?: any) { _observeObj<T extends { __owl__?: any }>(obj: T, parent?: any) {
const keys = Object.keys(obj);
obj.__owl__ = { rev: this.rev, deepRev: this.rev, parent }; obj.__owl__ = { rev: this.rev, deepRev: this.rev, parent };
Object.defineProperty(obj, "__owl__", { enumerable: false }); Object.defineProperty(obj, "__owl__", { enumerable: false });
for (let key of keys) { for (let key in obj) {
this._addProp(obj, key, obj[key]); this._addProp(obj, key, obj[key]);
} }
} }
@@ -125,7 +127,7 @@ export class Observer {
Object.defineProperty(arr, "__owl__", { enumerable: false }); Object.defineProperty(arr, "__owl__", { enumerable: false });
(<any>arr).__proto__ = Object.create(ModifiedArrayProto); (<any>arr).__proto__ = Object.create(ModifiedArrayProto);
(<any>arr).__proto__.__observer__ = this; (<any>arr).__proto__.__observer__ = this;
for (let i = 0; i < arr.length; i++) { for (let i = 0, iLen = arr.length; i < iLen; i++) {
this.observe(arr[i], arr); this.observe(arr[i], arr);
} }
} }
+10 -7
View File
@@ -100,7 +100,7 @@ interface Utils {
h: typeof h; h: typeof h;
objectToAttrString(obj: Object): string; objectToAttrString(obj: Object): string;
[key: string]: any; [key: string]: any;
}; }
export const UTILS: Utils = { export const UTILS: Utils = {
h: h, h: h,
@@ -361,17 +361,20 @@ export class QWeb {
} }
} }
for (let directive of DIRECTIVES) { const DIR_N = DIRECTIVES.length;
const ATTR_N = attributes.length;
for (let i = 0; i < DIR_N; i++) {
let directive = DIRECTIVES[i];
let fullName; let fullName;
let value; let value;
for (let i = 0; i < attributes.length; i++) { for (let j = 0; j < ATTR_N; j++) {
const name = attributes[i].name; const name = attributes[j].name;
if ( if (
name === "t-" + directive.name || name === "t-" + directive.name ||
name.startsWith("t-" + directive.name + "-") name.startsWith("t-" + directive.name + "-")
) { ) {
fullName = name; fullName = name;
value = attributes[i].textContent; value = attributes[j].textContent;
validDirectives.push({ directive, value, fullName }); validDirectives.push({ directive, value, fullName });
if (directive.name === "on") { if (directive.name === "on") {
withHandlers = true; withHandlers = true;
@@ -721,8 +724,8 @@ export class Context {
let invarPos = 0; let invarPos = 0;
let r = ""; let r = "";
chars.push(" "); chars.push(" ");
for (var i = 0, ilen = chars.length; i < ilen; i++) { for (let i = 0, ilen = chars.length; i < ilen; i++) {
var c = chars[i]; let c = chars[i];
if (instring.length) { if (instring.length) {
if (c === instring && chars[i - 1] !== "\\") { if (c === instring && chars[i - 1] !== "\\") {
instring = ""; instring = "";
+3 -2
View File
@@ -250,14 +250,15 @@ QWeb.addDirective({
ctx.addLine( ctx.addLine(
`var _${keysID} = _${arrayID} instanceof Array ? _${arrayID} : Object.keys(_${arrayID});` `var _${keysID} = _${arrayID} instanceof Array ? _${arrayID} : Object.keys(_${arrayID});`
); );
ctx.addLine(`var _length${keysID} = _${keysID}.length;`);
let valuesID = ctx.generateID(); let valuesID = ctx.generateID();
ctx.addLine( ctx.addLine(
`var _${valuesID} = _${arrayID} instanceof Array ? _${arrayID} : Object.values(_${arrayID});` `var _${valuesID} = _${arrayID} instanceof Array ? _${arrayID} : Object.values(_${arrayID});`
); );
ctx.addLine(`for (let i = 0; i < _${keysID}.length; i++) {`); ctx.addLine(`for (let i = 0; i < _length${keysID}; i++) {`);
ctx.indent(); ctx.indent();
ctx.addLine(`context.${name}_first = i === 0;`); ctx.addLine(`context.${name}_first = i === 0;`);
ctx.addLine(`context.${name}_last = i === _${keysID}.length - 1;`); ctx.addLine(`context.${name}_last = i === _length${keysID} - 1;`);
ctx.addLine(`context.${name}_parity = i % 2 === 0 ? 'even' : 'odd';`); ctx.addLine(`context.${name}_parity = i % 2 === 0 ? 'even' : 'odd';`);
ctx.addLine(`context.${name}_index = i;`); ctx.addLine(`context.${name}_index = i;`);
ctx.addLine(`context.${name} = _${keysID}[i];`); ctx.addLine(`context.${name} = _${keysID}[i];`);
+29 -17
View File
@@ -15,12 +15,15 @@
* - patch function (to apply a vnode to an actual DOM node) * - patch function (to apply a vnode to an actual DOM node)
*/ */
// because those in TypeScript are too restrictive: https://github.com/Microsoft/TSJS-lib-generator/pull/237 // because those in TypeScript are too restrictive: https://github.com/Microsoft/TSJS-lib-generator/pull/237
declare global { declare global {
interface Element { interface Element {
setAttribute(name: string, value: string | number | boolean): void; setAttribute(name: string, value: string | number | boolean): void;
setAttributeNS(namespaceURI: string, qualifiedName: string, value: string | number | boolean): void; setAttributeNS(
namespaceURI: string,
qualifiedName: string,
value: string | number | boolean
): void;
} }
} }
@@ -162,6 +165,7 @@ export function init(modules: Array<Partial<Module>>, domApi?: DOMAPI) {
function createElm(vnode: VNode, insertedVnodeQueue: VNodeQueue): Node { function createElm(vnode: VNode, insertedVnodeQueue: VNodeQueue): Node {
let i: any, let i: any,
iLen: number,
data = vnode.data; data = vnode.data;
if (data !== undefined) { if (data !== undefined) {
if (isDef((i = data.hook)) && isDef((i = i.init))) { if (isDef((i = data.hook)) && isDef((i = i.init))) {
@@ -193,9 +197,10 @@ export function init(modules: Array<Partial<Module>>, domApi?: DOMAPI) {
if (hash < dot) elm.setAttribute("id", sel.slice(hash + 1, dot)); if (hash < dot) elm.setAttribute("id", sel.slice(hash + 1, dot));
if (dotIdx > 0) if (dotIdx > 0)
elm.setAttribute("class", sel.slice(dot + 1).replace(/\./g, " ")); elm.setAttribute("class", sel.slice(dot + 1).replace(/\./g, " "));
for (i = 0; i < cbs.create.length; ++i) cbs.create[i](emptyNode, vnode); for (i = 0, iLen = cbs.create.length; i < iLen; ++i)
cbs.create[i](emptyNode, vnode);
if (array(children)) { if (array(children)) {
for (i = 0; i < children.length; ++i) { for (i = 0, iLen = children.length; i < iLen; ++i) {
const ch = children[i]; const ch = children[i];
if (ch != null) { if (ch != null) {
api.appendChild(elm, createElm(ch as VNode, insertedVnodeQueue)); api.appendChild(elm, createElm(ch as VNode, insertedVnodeQueue));
@@ -233,13 +238,16 @@ export function init(modules: Array<Partial<Module>>, domApi?: DOMAPI) {
function invokeDestroyHook(vnode: VNode) { function invokeDestroyHook(vnode: VNode) {
let i: any, let i: any,
iLen: number,
j: number, j: number,
jLen: number,
data = vnode.data; data = vnode.data;
if (data !== undefined) { if (data !== undefined) {
if (isDef((i = data.hook)) && isDef((i = i.destroy))) i(vnode); if (isDef((i = data.hook)) && isDef((i = i.destroy))) i(vnode);
for (i = 0; i < cbs.destroy.length; ++i) cbs.destroy[i](vnode); for (i = 0, iLen = cbs.destroy.length; i < iLen; ++i)
cbs.destroy[i](vnode);
if (vnode.children !== undefined) { if (vnode.children !== undefined) {
for (j = 0; j < vnode.children.length; ++j) { for (j = 0, jLen = vnode.children.length; j < jLen; ++j) {
i = vnode.children[j]; i = vnode.children[j];
if (i != null && typeof i !== "string") { if (i != null && typeof i !== "string") {
invokeDestroyHook(i); invokeDestroyHook(i);
@@ -257,6 +265,7 @@ export function init(modules: Array<Partial<Module>>, domApi?: DOMAPI) {
): void { ): void {
for (; startIdx <= endIdx; ++startIdx) { for (; startIdx <= endIdx; ++startIdx) {
let i: any, let i: any,
iLen: number,
listeners: number, listeners: number,
rm: () => void, rm: () => void,
ch = vnodes[startIdx]; ch = vnodes[startIdx];
@@ -265,7 +274,8 @@ export function init(modules: Array<Partial<Module>>, domApi?: DOMAPI) {
invokeDestroyHook(ch); invokeDestroyHook(ch);
listeners = cbs.remove.length + 1; listeners = cbs.remove.length + 1;
rm = createRmCb(ch.elm as Node, listeners); rm = createRmCb(ch.elm as Node, listeners);
for (i = 0; i < cbs.remove.length; ++i) cbs.remove[i](ch, rm); for (i = 0, iLen = cbs.remove.length; i < iLen; ++i)
cbs.remove[i](ch, rm);
if ( if (
isDef((i = ch.data)) && isDef((i = ch.data)) &&
isDef((i = i.hook)) && isDef((i = i.hook)) &&
@@ -395,7 +405,7 @@ export function init(modules: Array<Partial<Module>>, domApi?: DOMAPI) {
vnode: VNode, vnode: VNode,
insertedVnodeQueue: VNodeQueue insertedVnodeQueue: VNodeQueue
) { ) {
let i: any, hook: any; let i: any, iLen: number, hook: any;
if ( if (
isDef((i = vnode.data)) && isDef((i = vnode.data)) &&
isDef((hook = i.hook)) && isDef((hook = i.hook)) &&
@@ -408,7 +418,8 @@ export function init(modules: Array<Partial<Module>>, domApi?: DOMAPI) {
let ch = vnode.children; let ch = vnode.children;
if (oldVnode === vnode) return; if (oldVnode === vnode) return;
if (vnode.data !== undefined) { if (vnode.data !== undefined) {
for (i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode); for (i = 0, iLen = cbs.update.length; i < iLen; ++i)
cbs.update[i](oldVnode, vnode);
i = vnode.data.hook; i = vnode.data.hook;
if (isDef(i) && isDef((i = i.update))) i(oldVnode, vnode); if (isDef(i) && isDef((i = i.update))) i(oldVnode, vnode);
} }
@@ -458,9 +469,9 @@ export function init(modules: Array<Partial<Module>>, domApi?: DOMAPI) {
} }
return function patch(oldVnode: VNode | Element, vnode: VNode): VNode { return function patch(oldVnode: VNode | Element, vnode: VNode): VNode {
let i: number, elm: Node, parent: Node; let i: number, iLen: number, elm: Node, parent: Node;
const insertedVnodeQueue: VNodeQueue = []; const insertedVnodeQueue: VNodeQueue = [];
for (i = 0; i < cbs.pre.length; ++i) cbs.pre[i](); for (i = 0, iLen = cbs.pre.length; i < iLen; ++i) cbs.pre[i]();
if (!isVnode(oldVnode)) { if (!isVnode(oldVnode)) {
oldVnode = emptyNodeAt(oldVnode); oldVnode = emptyNodeAt(oldVnode);
@@ -480,12 +491,12 @@ export function init(modules: Array<Partial<Module>>, domApi?: DOMAPI) {
} }
} }
for (i = 0; i < insertedVnodeQueue.length; ++i) { for (i = 0, iLen = insertedVnodeQueue.length; i < iLen; ++i) {
(((insertedVnodeQueue[i].data as VNodeData).hook as Hooks).insert as any)( (((insertedVnodeQueue[i].data as VNodeData).hook as Hooks).insert as any)(
insertedVnodeQueue[i] insertedVnodeQueue[i]
); );
} }
for (i = 0; i < cbs.post.length; ++i) cbs.post[i](); for (i = 0, iLen = cbs.post.length; i < iLen; ++i) cbs.post[i]();
return vnode; return vnode;
}; };
} }
@@ -647,7 +658,7 @@ function addNS(
): void { ): void {
data.ns = "http://www.w3.org/2000/svg"; data.ns = "http://www.w3.org/2000/svg";
if (sel !== "foreignObject" && children !== undefined) { if (sel !== "foreignObject" && children !== undefined) {
for (let i = 0; i < children.length; ++i) { for (let i = 0, iLen = children.length; i < iLen; ++i) {
let childData = children[i].data; let childData = children[i].data;
if (childData !== undefined) { if (childData !== undefined) {
addNS( addNS(
@@ -668,7 +679,8 @@ export function h(sel: any, b?: any, c?: any): VNode {
var data: VNodeData = {}, var data: VNodeData = {},
children: any, children: any,
text: any, text: any,
i: number; i: number,
iLen: number;
if (c !== undefined) { if (c !== undefined) {
data = b; data = b;
if (array(c)) { if (array(c)) {
@@ -690,7 +702,7 @@ export function h(sel: any, b?: any, c?: any): VNode {
} }
} }
if (children !== undefined) { if (children !== undefined) {
for (i = 0; i < children.length; ++i) { for (i = 0, iLen = children.length; i < iLen; ++i) {
if (primitive(children[i])) if (primitive(children[i]))
children[i] = vnode( children[i] = vnode(
undefined, undefined,
@@ -788,7 +800,7 @@ function invokeHandler(handler: any, vnode?: VNode, event?: Event): void {
} }
} else { } else {
// call multiple handlers // call multiple handlers
for (var i = 0; i < handler.length; i++) { for (let i = 0, iLen = handler.length; i < iLen; i++) {
invokeHandler(handler[i], vnode, event); invokeHandler(handler[i], vnode, event);
} }
} }
+3 -2
View File
@@ -90,10 +90,11 @@ exports[`composition sub widgets with some state rendered in a loop 1`] = `
if (!_2) { throw new Error('QWeb error: Invalid loop expression')} if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
if (typeof _2 === 'number') { _2 = Array.from(Array(_2).keys())} if (typeof _2 === 'number') { _2 = Array.from(Array(_2).keys())}
var _3 = _2 instanceof Array ? _2 : Object.keys(_2); var _3 = _2 instanceof Array ? _2 : Object.keys(_2);
var _length3 = _3.length;
var _4 = _2 instanceof Array ? _2 : Object.values(_2); var _4 = _2 instanceof Array ? _2 : Object.values(_2);
for (let i = 0; i < _3.length; i++) { for (let i = 0; i < _length3; i++) {
context.number_first = i === 0; context.number_first = i === 0;
context.number_last = i === _3.length - 1; context.number_last = i === _length3 - 1;
context.number_parity = i % 2 === 0 ? 'even' : 'odd'; context.number_parity = i % 2 === 0 ? 'even' : 'odd';
context.number_index = i; context.number_index = i;
context.number = _3[i]; context.number = _3[i];
+36 -24
View File
@@ -308,10 +308,11 @@ exports[`foreach does not pollute the rendering context 1`] = `
if (!_2) { throw new Error('QWeb error: Invalid loop expression')} if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
if (typeof _2 === 'number') { _2 = Array.from(Array(_2).keys())} if (typeof _2 === 'number') { _2 = Array.from(Array(_2).keys())}
var _3 = _2 instanceof Array ? _2 : Object.keys(_2); var _3 = _2 instanceof Array ? _2 : Object.keys(_2);
var _length3 = _3.length;
var _4 = _2 instanceof Array ? _2 : Object.values(_2); var _4 = _2 instanceof Array ? _2 : Object.values(_2);
for (let i = 0; i < _3.length; i++) { for (let i = 0; i < _length3; i++) {
context.item_first = i === 0; context.item_first = i === 0;
context.item_last = i === _3.length - 1; context.item_last = i === _length3 - 1;
context.item_parity = i % 2 === 0 ? 'even' : 'odd'; context.item_parity = i % 2 === 0 ? 'even' : 'odd';
context.item_index = i; context.item_index = i;
context.item = _3[i]; context.item = _3[i];
@@ -336,10 +337,11 @@ exports[`foreach iterate on items (on a element node) 1`] = `
if (!_2) { throw new Error('QWeb error: Invalid loop expression')} if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
if (typeof _2 === 'number') { _2 = Array.from(Array(_2).keys())} if (typeof _2 === 'number') { _2 = Array.from(Array(_2).keys())}
var _3 = _2 instanceof Array ? _2 : Object.keys(_2); var _3 = _2 instanceof Array ? _2 : Object.keys(_2);
var _length3 = _3.length;
var _4 = _2 instanceof Array ? _2 : Object.values(_2); var _4 = _2 instanceof Array ? _2 : Object.values(_2);
for (let i = 0; i < _3.length; i++) { for (let i = 0; i < _length3; i++) {
context.item_first = i === 0; context.item_first = i === 0;
context.item_last = i === _3.length - 1; context.item_last = i === _length3 - 1;
context.item_parity = i % 2 === 0 ? 'even' : 'odd'; context.item_parity = i % 2 === 0 ? 'even' : 'odd';
context.item_index = i; context.item_index = i;
context.item = _3[i]; context.item = _3[i];
@@ -367,10 +369,11 @@ exports[`foreach iterate on items 1`] = `
if (!_2) { throw new Error('QWeb error: Invalid loop expression')} if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
if (typeof _2 === 'number') { _2 = Array.from(Array(_2).keys())} if (typeof _2 === 'number') { _2 = Array.from(Array(_2).keys())}
var _3 = _2 instanceof Array ? _2 : Object.keys(_2); var _3 = _2 instanceof Array ? _2 : Object.keys(_2);
var _length3 = _3.length;
var _4 = _2 instanceof Array ? _2 : Object.values(_2); var _4 = _2 instanceof Array ? _2 : Object.values(_2);
for (let i = 0; i < _3.length; i++) { for (let i = 0; i < _length3; i++) {
context.item_first = i === 0; context.item_first = i === 0;
context.item_last = i === _3.length - 1; context.item_last = i === _length3 - 1;
context.item_parity = i % 2 === 0 ? 'even' : 'odd'; context.item_parity = i % 2 === 0 ? 'even' : 'odd';
context.item_index = i; context.item_index = i;
context.item = _3[i]; context.item = _3[i];
@@ -407,10 +410,11 @@ exports[`foreach iterate, dict param 1`] = `
if (!_2) { throw new Error('QWeb error: Invalid loop expression')} if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
if (typeof _2 === 'number') { _2 = Array.from(Array(_2).keys())} if (typeof _2 === 'number') { _2 = Array.from(Array(_2).keys())}
var _3 = _2 instanceof Array ? _2 : Object.keys(_2); var _3 = _2 instanceof Array ? _2 : Object.keys(_2);
var _length3 = _3.length;
var _4 = _2 instanceof Array ? _2 : Object.values(_2); var _4 = _2 instanceof Array ? _2 : Object.values(_2);
for (let i = 0; i < _3.length; i++) { for (let i = 0; i < _length3; i++) {
context.item_first = i === 0; context.item_first = i === 0;
context.item_last = i === _3.length - 1; context.item_last = i === _length3 - 1;
context.item_parity = i % 2 === 0 ? 'even' : 'odd'; context.item_parity = i % 2 === 0 ? 'even' : 'odd';
context.item_index = i; context.item_index = i;
context.item = _3[i]; context.item = _3[i];
@@ -452,10 +456,11 @@ exports[`foreach iterate, integer param 1`] = `
if (!_2) { throw new Error('QWeb error: Invalid loop expression')} if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
if (typeof _2 === 'number') { _2 = Array.from(Array(_2).keys())} if (typeof _2 === 'number') { _2 = Array.from(Array(_2).keys())}
var _3 = _2 instanceof Array ? _2 : Object.keys(_2); var _3 = _2 instanceof Array ? _2 : Object.keys(_2);
var _length3 = _3.length;
var _4 = _2 instanceof Array ? _2 : Object.values(_2); var _4 = _2 instanceof Array ? _2 : Object.values(_2);
for (let i = 0; i < _3.length; i++) { for (let i = 0; i < _length3; i++) {
context.item_first = i === 0; context.item_first = i === 0;
context.item_last = i === _3.length - 1; context.item_last = i === _length3 - 1;
context.item_parity = i % 2 === 0 ? 'even' : 'odd'; context.item_parity = i % 2 === 0 ? 'even' : 'odd';
context.item_index = i; context.item_index = i;
context.item = _3[i]; context.item = _3[i];
@@ -492,10 +497,11 @@ exports[`foreach iterate, position 1`] = `
if (!_2) { throw new Error('QWeb error: Invalid loop expression')} if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
if (typeof _2 === 'number') { _2 = Array.from(Array(_2).keys())} if (typeof _2 === 'number') { _2 = Array.from(Array(_2).keys())}
var _3 = _2 instanceof Array ? _2 : Object.keys(_2); var _3 = _2 instanceof Array ? _2 : Object.keys(_2);
var _length3 = _3.length;
var _4 = _2 instanceof Array ? _2 : Object.values(_2); var _4 = _2 instanceof Array ? _2 : Object.values(_2);
for (let i = 0; i < _3.length; i++) { for (let i = 0; i < _length3; i++) {
context.elem_first = i === 0; context.elem_first = i === 0;
context.elem_last = i === _3.length - 1; context.elem_last = i === _length3 - 1;
context.elem_parity = i % 2 === 0 ? 'even' : 'odd'; context.elem_parity = i % 2 === 0 ? 'even' : 'odd';
context.elem_index = i; context.elem_index = i;
context.elem = _3[i]; context.elem = _3[i];
@@ -529,10 +535,11 @@ exports[`foreach warn if no key in some case 1`] = `
if (!_2) { throw new Error('QWeb error: Invalid loop expression')} if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
if (typeof _2 === 'number') { _2 = Array.from(Array(_2).keys())} if (typeof _2 === 'number') { _2 = Array.from(Array(_2).keys())}
var _3 = _2 instanceof Array ? _2 : Object.keys(_2); var _3 = _2 instanceof Array ? _2 : Object.keys(_2);
var _length3 = _3.length;
var _4 = _2 instanceof Array ? _2 : Object.values(_2); var _4 = _2 instanceof Array ? _2 : Object.values(_2);
for (let i = 0; i < _3.length; i++) { for (let i = 0; i < _length3; i++) {
context.item_first = i === 0; context.item_first = i === 0;
context.item_last = i === _3.length - 1; context.item_last = i === _length3 - 1;
context.item_parity = i % 2 === 0 ? 'even' : 'odd'; context.item_parity = i % 2 === 0 ? 'even' : 'odd';
context.item_index = i; context.item_index = i;
context.item = _3[i]; context.item = _3[i];
@@ -589,10 +596,11 @@ exports[`misc global 1`] = `
if (!_2) { throw new Error('QWeb error: Invalid loop expression')} if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
if (typeof _2 === 'number') { _2 = Array.from(Array(_2).keys())} if (typeof _2 === 'number') { _2 = Array.from(Array(_2).keys())}
var _3 = _2 instanceof Array ? _2 : Object.keys(_2); var _3 = _2 instanceof Array ? _2 : Object.keys(_2);
var _length3 = _3.length;
var _4 = _2 instanceof Array ? _2 : Object.values(_2); var _4 = _2 instanceof Array ? _2 : Object.values(_2);
for (let i = 0; i < _3.length; i++) { for (let i = 0; i < _length3; i++) {
context.value_first = i === 0; context.value_first = i === 0;
context.value_last = i === _3.length - 1; context.value_last = i === _length3 - 1;
context.value_parity = i % 2 === 0 ? 'even' : 'odd'; context.value_parity = i % 2 === 0 ? 'even' : 'odd';
context.value_index = i; context.value_index = i;
context.value = _3[i]; context.value = _3[i];
@@ -1098,10 +1106,11 @@ exports[`t-key t-key directive in a list 1`] = `
if (!_2) { throw new Error('QWeb error: Invalid loop expression')} if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
if (typeof _2 === 'number') { _2 = Array.from(Array(_2).keys())} if (typeof _2 === 'number') { _2 = Array.from(Array(_2).keys())}
var _3 = _2 instanceof Array ? _2 : Object.keys(_2); var _3 = _2 instanceof Array ? _2 : Object.keys(_2);
var _length3 = _3.length;
var _4 = _2 instanceof Array ? _2 : Object.values(_2); var _4 = _2 instanceof Array ? _2 : Object.values(_2);
for (let i = 0; i < _3.length; i++) { for (let i = 0; i < _length3; i++) {
context.beer_first = i === 0; context.beer_first = i === 0;
context.beer_last = i === _3.length - 1; context.beer_last = i === _length3 - 1;
context.beer_parity = i % 2 === 0 ? 'even' : 'odd'; context.beer_parity = i % 2 === 0 ? 'even' : 'odd';
context.beer_index = i; context.beer_index = i;
context.beer = _3[i]; context.beer = _3[i];
@@ -1197,10 +1206,11 @@ exports[`t-on can bind handlers with loop variable as argument 1`] = `
if (!_2) { throw new Error('QWeb error: Invalid loop expression')} if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
if (typeof _2 === 'number') { _2 = Array.from(Array(_2).keys())} if (typeof _2 === 'number') { _2 = Array.from(Array(_2).keys())}
var _3 = _2 instanceof Array ? _2 : Object.keys(_2); var _3 = _2 instanceof Array ? _2 : Object.keys(_2);
var _length3 = _3.length;
var _4 = _2 instanceof Array ? _2 : Object.values(_2); var _4 = _2 instanceof Array ? _2 : Object.values(_2);
for (let i = 0; i < _3.length; i++) { for (let i = 0; i < _length3; i++) {
context.action_first = i === 0; context.action_first = i === 0;
context.action_last = i === _3.length - 1; context.action_last = i === _length3 - 1;
context.action_parity = i % 2 === 0 ? 'even' : 'odd'; context.action_parity = i % 2 === 0 ? 'even' : 'odd';
context.action_index = i; context.action_index = i;
context.action = _3[i]; context.action = _3[i];
@@ -1524,10 +1534,11 @@ exports[`t-ref refs in a loop 1`] = `
if (!_2) { throw new Error('QWeb error: Invalid loop expression')} if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
if (typeof _2 === 'number') { _2 = Array.from(Array(_2).keys())} if (typeof _2 === 'number') { _2 = Array.from(Array(_2).keys())}
var _3 = _2 instanceof Array ? _2 : Object.keys(_2); var _3 = _2 instanceof Array ? _2 : Object.keys(_2);
var _length3 = _3.length;
var _4 = _2 instanceof Array ? _2 : Object.values(_2); var _4 = _2 instanceof Array ? _2 : Object.values(_2);
for (let i = 0; i < _3.length; i++) { for (let i = 0; i < _length3; i++) {
context.item_first = i === 0; context.item_first = i === 0;
context.item_last = i === _3.length - 1; context.item_last = i === _length3 - 1;
context.item_parity = i % 2 === 0 ? 'even' : 'odd'; context.item_parity = i % 2 === 0 ? 'even' : 'odd';
context.item_index = i; context.item_index = i;
context.item = _3[i]; context.item = _3[i];
@@ -1682,10 +1693,11 @@ exports[`t-set t-set should reuse variable if possible 1`] = `
if (!_3) { throw new Error('QWeb error: Invalid loop expression')} if (!_3) { throw new Error('QWeb error: Invalid loop expression')}
if (typeof _3 === 'number') { _3 = Array.from(Array(_3).keys())} if (typeof _3 === 'number') { _3 = Array.from(Array(_3).keys())}
var _4 = _3 instanceof Array ? _3 : Object.keys(_3); var _4 = _3 instanceof Array ? _3 : Object.keys(_3);
var _length4 = _4.length;
var _5 = _3 instanceof Array ? _3 : Object.values(_3); var _5 = _3 instanceof Array ? _3 : Object.values(_3);
for (let i = 0; i < _4.length; i++) { for (let i = 0; i < _length4; i++) {
context.elem_first = i === 0; context.elem_first = i === 0;
context.elem_last = i === _4.length - 1; context.elem_last = i === _length4 - 1;
context.elem_parity = i % 2 === 0 ? 'even' : 'odd'; context.elem_parity = i % 2 === 0 ? 'even' : 'odd';
context.elem_index = i; context.elem_index = i;
context.elem = _4[i]; context.elem = _4[i];