@@ -32,6 +32,8 @@ import {
3232} from "./parser" ;
3333import { OwlError } from "../common/owl_error" ;
3434
35+ const zero = Symbol ( "" ) ;
36+
3537type BlockType = "block" | "text" | "multi" | "list" | "html" | "comment" ;
3638const whitespaceRE = / \s + / g;
3739
@@ -279,7 +281,7 @@ export class CodeGenerator {
279281 translatableAttributes : string [ ] = TRANSLATABLE_ATTRS ;
280282 ast : AST ;
281283 staticDefs : { id : string ; expr : string } [ ] = [ ] ;
282- slotNames : Set < String > = new Set ( ) ;
284+ slotNames : Set < String | Symbol > = new Set ( ) ;
283285 helpers : Set < string > = new Set ( ) ;
284286
285287 constructor ( ast : AST , options : CodeGenOptions ) {
@@ -791,12 +793,22 @@ export class CodeGenerator {
791793 return block ! . varName ;
792794 }
793795
796+ compileZero ( ) {
797+ this . helpers . add ( "zero" ) ;
798+ const isMultiple = this . slotNames . has ( zero ) ;
799+ this . slotNames . add ( zero ) ;
800+ let key = this . target . loopLevel ? `key${ this . target . loopLevel } ` : "key" ;
801+ if ( isMultiple ) {
802+ key = this . generateComponentKey ( key ) ;
803+ }
804+ return `ctx[zero]?.(node, ${ key } )` ;
805+ }
806+
794807 compileTEsc ( ast : ASTTEsc , ctx : Context ) : string {
795808 let { block, forceNewBlock } = ctx ;
796809 let expr : string ;
797810 if ( ast . expr === "0" ) {
798- this . helpers . add ( "zero" ) ;
799- expr = `ctx[zero]` ;
811+ expr = this . compileZero ( ) ;
800812 } else {
801813 expr = compileExpr ( ast . expr ) ;
802814 if ( ast . defaultValue ) {
@@ -824,8 +836,7 @@ export class CodeGenerator {
824836 block = this . createBlock ( block , "html" , ctx ) ;
825837 let blockStr ;
826838 if ( ast . expr === "0" ) {
827- this . helpers . add ( "zero" ) ;
828- blockStr = `ctx[zero]` ;
839+ blockStr = this . compileZero ( ) ;
829840 } else if ( ast . body ) {
830841 let bodyValue = null ;
831842 bodyValue = BlockDescription . nextBlockId ;
@@ -1044,8 +1055,12 @@ export class CodeGenerator {
10441055
10451056 compileTCall ( ast : ASTTCall , ctx : Context ) : string {
10461057 let { block, forceNewBlock } = ctx ;
1058+
1059+ const attrs : string [ ] = ast . attrs
1060+ ? this . formatPropObject ( ast . attrs , ast . attrsTranslationCtx , ctx . translationCtx )
1061+ : [ ] ;
10471062 let ctxVar = ctx . ctxVar || "ctx" ;
1048- if ( ast . context ) {
1063+ if ( ! ast . attrs && ast . context ) {
10491064 ctxVar = generateId ( "ctx" ) ;
10501065 this . addLine ( `let ${ ctxVar } = ${ compileExpr ( ast . context ) } ;` ) ;
10511066 }
@@ -1056,38 +1071,59 @@ export class CodeGenerator {
10561071 }
10571072 block = this . createBlock ( block , "multi" , ctx ) ;
10581073 if ( ast . body ) {
1059- this . addLine ( `${ ctxVar } = Object.create(${ ctxVar } );` ) ;
1060- this . addLine ( `${ ctxVar } [isBoundary] = 1;` ) ;
1061- this . helpers . add ( "isBoundary" ) ;
1062- const subCtx = createContext ( ctx , { ctxVar } ) ;
1063- const bl = this . compileMulti ( { type : ASTType . Multi , content : ast . body } , subCtx ) ;
1064- if ( bl ) {
1074+ if ( ast . attrs ) {
1075+ let ctxStr = "ctx" ;
1076+ if ( this . target . loopLevel || ! this . hasSafeContext ) {
1077+ ctxStr = generateId ( "ctx" ) ;
1078+ this . helpers . add ( "capture" ) ;
1079+ this . define ( ctxStr , `capture(ctx)` ) ;
1080+ }
10651081 this . helpers . add ( "zero" ) ;
1066- this . addLine ( `${ ctxVar } [zero] = ${ bl } ;` ) ;
1082+ const name = this . compileInNewTarget ( "callBody" , ast . body , ctx ) ;
1083+ const zeroStr = generateId ( "lazyBlock" ) ;
1084+ this . define ( zeroStr , `${ name } .bind(this, Object.create(${ ctxStr } ))` ) ;
1085+ attrs . push ( `[zero]: ${ zeroStr } ` ) ;
1086+ } else {
1087+ this . addLine ( `${ ctxVar } = Object.create(${ ctxVar } );` ) ;
1088+ this . addLine ( `${ ctxVar } [isBoundary] = 1;` ) ;
1089+ this . helpers . add ( "isBoundary" ) ;
1090+ const subCtx = createContext ( ctx , { ctxVar } ) ;
1091+ const bl = this . compileAST ( ast . body , subCtx ) ;
1092+ if ( bl ) {
1093+ this . helpers . add ( "zero" ) ;
1094+ this . addLine ( `${ ctxVar } [zero] = () => ${ bl } ;` ) ;
1095+ }
10671096 }
10681097 }
10691098
1099+ let ctxString = `{${ attrs . join ( ", " ) } }` ;
1100+ if ( ast . attrs && ast . context ) {
1101+ const dynCtxVar = generateId ( "ctx" ) ;
1102+ this . addLine ( `let ${ dynCtxVar } = ${ compileExpr ( ast . context ) } ;` ) ;
1103+ ctxString = `Object.assign({}, ${ dynCtxVar } ${ attrs . length ? ", " + ctxString : "" } )` ;
1104+ }
1105+ const ctxExpr = ast . attrs ? ctxString : ctxVar ;
10701106 const key = this . generateComponentKey ( ) ;
10711107 if ( isDynamic ) {
10721108 const templateVar = generateId ( "template" ) ;
10731109 if ( ! this . staticDefs . find ( ( d ) => d . id === "call" ) ) {
10741110 this . staticDefs . push ( { id : "call" , expr : `app.callTemplate.bind(app)` } ) ;
10751111 }
10761112 this . define ( templateVar , subTemplate ) ;
1077- this . insertBlock ( `call(this, ${ templateVar } , ${ ctxVar } , node, ${ key } )` , block ! , {
1113+ this . insertBlock ( `call(this, ${ templateVar } , ${ ctxExpr } , node, ${ key } )` , block ! , {
10781114 ...ctx ,
10791115 forceNewBlock : ! block ,
10801116 } ) ;
10811117 } else {
10821118 const id = generateId ( `callTemplate_` ) ;
10831119 this . staticDefs . push ( { id, expr : `app.getTemplate(${ subTemplate } )` } ) ;
1084- this . insertBlock ( `${ id } .call(this, ${ ctxVar } , node, ${ key } )` , block ! , {
1120+ this . insertBlock ( `${ id } .call(this, ${ ctxExpr } , node, ${ key } )` , block ! , {
10851121 ...ctx ,
10861122 forceNewBlock : ! block ,
10871123 } ) ;
10881124 }
1089- if ( ast . body && ! ctx . isLast ) {
1090- this . addLine ( `${ ctxVar } = ${ ctxVar } .__proto__;` ) ;
1125+ if ( ! ast . attrs && ast . body && ! ctx . isLast ) {
1126+ this . addLine ( `${ ctxExpr } = ${ ctxExpr } .__proto__;` ) ;
10911127 }
10921128 return block . varName ;
10931129 }
0 commit comments