@@ -17,58 +17,9 @@ import {createPureProxy} from './identifier_util';
1717
1818const VAL_UNWRAPPER_VAR = o . variable ( `valUnwrapper` ) ;
1919
20- export interface NameResolver {
21- callPipe ( name : string , input : o . Expression , args : o . Expression [ ] ) : o . Expression ;
22- getLocal ( name : string ) : o . Expression ;
23- }
24-
2520export class EventHandlerVars { static event = o . variable ( '$event' ) ; }
2621
27- export class ConvertPropertyBindingResult {
28- constructor (
29- public stmts : o . Statement [ ] , public currValExpr : o . Expression ,
30- public forceUpdate : o . Expression ) { }
31- }
32-
33- /**
34- * Converts the given expression AST into an executable output AST, assuming the expression is
35- * used in a property binding.
36- */
37- export function convertPropertyBinding (
38- builder : ClassBuilder , nameResolver : NameResolver , implicitReceiver : o . Expression ,
39- expression : cdAst . AST , bindingId : string ) : ConvertPropertyBindingResult {
40- const currValExpr = createCurrValueExpr ( bindingId ) ;
41- const stmts : o . Statement [ ] = [ ] ;
42- if ( ! nameResolver ) {
43- nameResolver = new DefaultNameResolver ( ) ;
44- }
45- const visitor = new _AstToIrVisitor (
46- builder , nameResolver , implicitReceiver , VAL_UNWRAPPER_VAR , bindingId , false ) ;
47- const outputExpr : o . Expression = expression . visit ( visitor , _Mode . Expression ) ;
48-
49- if ( ! outputExpr ) {
50- // e.g. an empty expression was given
51- return null ;
52- }
53-
54- if ( visitor . temporaryCount ) {
55- for ( let i = 0 ; i < visitor . temporaryCount ; i ++ ) {
56- stmts . push ( temporaryDeclaration ( bindingId , i ) ) ;
57- }
58- }
59-
60- if ( visitor . needsValueUnwrapper ) {
61- const initValueUnwrapperStmt = VAL_UNWRAPPER_VAR . callMethod ( 'reset' , [ ] ) . toStmt ( ) ;
62- stmts . push ( initValueUnwrapperStmt ) ;
63- }
64- stmts . push ( currValExpr . set ( outputExpr ) . toDeclStmt ( null , [ o . StmtModifier . Final ] ) ) ;
65- if ( visitor . needsValueUnwrapper ) {
66- return new ConvertPropertyBindingResult (
67- stmts , currValExpr , VAL_UNWRAPPER_VAR . prop ( 'hasWrappedValue' ) ) ;
68- } else {
69- return new ConvertPropertyBindingResult ( stmts , currValExpr , null ) ;
70- }
71- }
22+ export interface LocalResolver { getLocal ( name : string ) : o . Expression ; }
7223
7324export class ConvertActionBindingResult {
7425 constructor ( public stmts : o . Statement [ ] , public allowDefault : o . ReadVarExpr ) { }
@@ -79,15 +30,31 @@ export class ConvertActionBindingResult {
7930 * used in an action binding (e.g. an event handler).
8031 */
8132export function convertActionBinding (
82- builder : ClassBuilder , nameResolver : NameResolver , implicitReceiver : o . Expression ,
83- action : cdAst . AST , bindingId : string ) : ConvertActionBindingResult {
84- if ( ! nameResolver ) {
85- nameResolver = new DefaultNameResolver ( ) ;
86- }
87- const visitor =
88- new _AstToIrVisitor ( builder , nameResolver , implicitReceiver , null , bindingId , true ) ;
33+ localResolver : LocalResolver , implicitReceiver : o . Expression , action : cdAst . AST ,
34+ bindingId : string ) : ConvertActionBindingResult {
35+ if ( ! localResolver ) {
36+ localResolver = new DefaultLocalResolver ( ) ;
37+ }
38+ const actionWithoutBuiltins = convertPropertyBindingBuiltins (
39+ {
40+ createLiteralArrayConverter : ( argCount : number ) => {
41+ // Note: no caching for literal arrays in actions.
42+ return ( args : o . Expression [ ] ) => o . literalArr ( args ) ;
43+ } ,
44+ createLiteralMapConverter : ( keys : string [ ] ) => {
45+ // Note: no caching for literal maps in actions.
46+ return ( args : o . Expression [ ] ) =>
47+ o . literalMap ( < [ string , o . Expression ] [ ] > keys . map ( ( key , i ) => [ key , args [ i ] ] ) ) ;
48+ } ,
49+ createPipeConverter : ( name : string ) => {
50+ throw new Error ( `Illegal State: Actions are not allowed to contain pipes. Pipe: ${ name } ` ) ;
51+ }
52+ } ,
53+ action ) ;
54+
55+ const visitor = new _AstToIrVisitor ( localResolver , implicitReceiver , bindingId ) ;
8956 const actionStmts : o . Statement [ ] = [ ] ;
90- flattenStatements ( action . visit ( visitor , _Mode . Statement ) , actionStmts ) ;
57+ flattenStatements ( actionWithoutBuiltins . visit ( visitor , _Mode . Statement ) , actionStmts ) ;
9158 prependTemporaryDecls ( visitor . temporaryCount , bindingId , actionStmts ) ;
9259 const lastIndex = actionStmts . length - 1 ;
9360 let preventDefaultVar : o . ReadVarExpr = null ;
@@ -106,11 +73,105 @@ export function convertActionBinding(
10673 return new ConvertActionBindingResult ( actionStmts , preventDefaultVar ) ;
10774}
10875
76+ export interface BuiltinConverter { ( args : o . Expression [ ] ) : o . Expression ; }
77+
78+ export interface BuiltinConverterFactory {
79+ createLiteralArrayConverter ( argCount : number ) : BuiltinConverter ;
80+ createLiteralMapConverter ( keys : string [ ] ) : BuiltinConverter ;
81+ createPipeConverter ( name : string , argCount : number ) : BuiltinConverter ;
82+ }
83+
84+ export function convertPropertyBindingBuiltins (
85+ converterFactory : BuiltinConverterFactory , ast : cdAst . AST ) : cdAst . AST {
86+ return convertBuiltins ( converterFactory , ast ) ;
87+ }
88+
89+ export class ConvertPropertyBindingResult {
90+ constructor ( public stmts : o . Statement [ ] , public currValExpr : o . Expression ) { }
91+ }
92+
93+ /**
94+ * Converts the given expression AST into an executable output AST, assuming the expression
95+ * is used in property binding. The expression has to be preprocessed via
96+ * `convertPropertyBindingBuiltins`.
97+ */
98+ export function convertPropertyBinding (
99+ localResolver : LocalResolver , implicitReceiver : o . Expression ,
100+ expressionWithoutBuiltins : cdAst . AST , bindingId : string ) : ConvertPropertyBindingResult {
101+ if ( ! localResolver ) {
102+ localResolver = new DefaultLocalResolver ( ) ;
103+ }
104+ const currValExpr = createCurrValueExpr ( bindingId ) ;
105+ const stmts : o . Statement [ ] = [ ] ;
106+ const visitor = new _AstToIrVisitor ( localResolver , implicitReceiver , bindingId ) ;
107+ const outputExpr : o . Expression = expressionWithoutBuiltins . visit ( visitor , _Mode . Expression ) ;
108+
109+ if ( visitor . temporaryCount ) {
110+ for ( let i = 0 ; i < visitor . temporaryCount ; i ++ ) {
111+ stmts . push ( temporaryDeclaration ( bindingId , i ) ) ;
112+ }
113+ }
114+
115+ stmts . push ( currValExpr . set ( outputExpr ) . toDeclStmt ( null , [ o . StmtModifier . Final ] ) ) ;
116+ return new ConvertPropertyBindingResult ( stmts , currValExpr ) ;
117+ }
118+
119+
120+ export class LegacyConvertPropertyBindingResult implements ConvertPropertyBindingResult {
121+ constructor (
122+ public stmts : o . Statement [ ] , public currValExpr : o . Expression ,
123+ public forceUpdate : o . Expression ) { }
124+ }
125+
126+ export interface LegacyNameResolver {
127+ callPipe ( name : string , input : o . Expression , args : o . Expression [ ] ) : o . Expression ;
128+ getLocal ( name : string ) : o . Expression ;
129+ }
130+
131+ /**
132+ * Converts the given expression AST into an executable output AST, assuming the expression is
133+ * used in a property binding.
134+ */
135+ export function legacyConvertPropertyBinding (
136+ builder : ClassBuilder , nameResolver : LegacyNameResolver , implicitReceiver : o . Expression ,
137+ expression : cdAst . AST , bindingId : string ) : LegacyConvertPropertyBindingResult {
138+ if ( ! nameResolver ) {
139+ nameResolver = new LegacyDefaultNameResolver ( ) ;
140+ }
141+ let needsValueUnwrapper = false ;
142+ const expressionWithoutBuiltins = convertBuiltins (
143+ {
144+ createLiteralArrayConverter : ( argCount : number ) => {
145+ return ( args : o . Expression [ ] ) => legacyCreateCachedLiteralArray ( builder , args ) ;
146+ } ,
147+ createLiteralMapConverter : ( keys : string [ ] ) => {
148+ return ( args : o . Expression [ ] ) => legacyCreateCachedLiteralMap (
149+ builder , < [ string , o . Expression ] [ ] > keys . map ( ( key , i ) => [ key , args [ i ] ] ) ) ;
150+ } ,
151+ createPipeConverter : ( name : string ) => {
152+ needsValueUnwrapper = true ;
153+ return ( args : o . Expression [ ] ) => VAL_UNWRAPPER_VAR . callMethod (
154+ 'unwrap' , [ nameResolver . callPipe ( name , args [ 0 ] , args . slice ( 1 ) ) ] ) ;
155+ }
156+ } ,
157+ expression ) ;
158+
159+ const { stmts, currValExpr} =
160+ convertPropertyBinding ( nameResolver , implicitReceiver , expressionWithoutBuiltins , bindingId ) ;
161+ let forceUpdate : o . Expression = null ;
162+ if ( needsValueUnwrapper ) {
163+ const initValueUnwrapperStmt = VAL_UNWRAPPER_VAR . callMethod ( 'reset' , [ ] ) . toStmt ( ) ;
164+ stmts . unshift ( initValueUnwrapperStmt ) ;
165+ forceUpdate = VAL_UNWRAPPER_VAR . prop ( 'hasWrappedValue' ) ;
166+ }
167+ return new LegacyConvertPropertyBindingResult ( stmts , currValExpr , forceUpdate ) ;
168+ }
169+
109170/**
110171 * Creates variables that are shared by multiple calls to `convertActionBinding` /
111172 * `convertPropertyBinding`
112173 */
113- export function createSharedBindingVariablesIfNeeded ( stmts : o . Statement [ ] ) : o . Statement [ ] {
174+ export function legacyCreateSharedBindingVariablesIfNeeded ( stmts : o . Statement [ ] ) : o . Statement [ ] {
114175 const unwrapperStmts : o . Statement [ ] = [ ] ;
115176 const readVars = o . findReadVarNames ( stmts ) ;
116177 if ( readVars . has ( VAL_UNWRAPPER_VAR . name ) ) {
@@ -122,6 +183,11 @@ export function createSharedBindingVariablesIfNeeded(stmts: o.Statement[]): o.St
122183 return unwrapperStmts ;
123184}
124185
186+ function convertBuiltins ( converterFactory : BuiltinConverterFactory , ast : cdAst . AST ) : cdAst . AST {
187+ const visitor = new _BuiltinAstConverter ( converterFactory ) ;
188+ return ast . visit ( visitor ) ;
189+ }
190+
125191function temporaryName ( bindingId : string , temporaryNumber : number ) : string {
126192 return `tmp_${ bindingId } _${ temporaryNumber } ` ;
127193}
@@ -162,17 +228,34 @@ function convertToStatementIfNeeded(mode: _Mode, expr: o.Expression): o.Expressi
162228 }
163229}
164230
231+ class _BuiltinAstConverter extends cdAst . AstTransformer {
232+ constructor ( private _converterFactory : BuiltinConverterFactory ) { super ( ) ; }
233+ visitPipe ( ast : cdAst . BindingPipe , context : any ) : any {
234+ const args = [ ast . exp , ...ast . args ] . map ( ast => ast . visit ( this , context ) ) ;
235+ return new BuiltinFunctionCall (
236+ ast . span , args , this . _converterFactory . createPipeConverter ( ast . name , args . length ) ) ;
237+ }
238+ visitLiteralArray ( ast : cdAst . LiteralArray , context : any ) : any {
239+ const args = ast . expressions . map ( ast => ast . visit ( this , context ) ) ;
240+ return new BuiltinFunctionCall (
241+ ast . span , args , this . _converterFactory . createLiteralArrayConverter ( ast . expressions . length ) ) ;
242+ }
243+ visitLiteralMap ( ast : cdAst . LiteralMap , context : any ) : any {
244+ const args = ast . values . map ( ast => ast . visit ( this , context ) ) ;
245+ return new BuiltinFunctionCall (
246+ ast . span , args , this . _converterFactory . createLiteralMapConverter ( ast . keys ) ) ;
247+ }
248+ }
249+
165250class _AstToIrVisitor implements cdAst . AstVisitor {
166251 private _nodeMap = new Map < cdAst . AST , cdAst . AST > ( ) ;
167252 private _resultMap = new Map < cdAst . AST , o . Expression > ( ) ;
168253 private _currentTemporary : number = 0 ;
169- public needsValueUnwrapper : boolean = false ;
170254 public temporaryCount : number = 0 ;
171255
172256 constructor (
173- private _builder : ClassBuilder , private _nameResolver : NameResolver ,
174- private _implicitReceiver : o . Expression , private _valueUnwrapper : o . ReadVarExpr ,
175- private bindingId : string , private isAction : boolean ) { }
257+ private _localResolver : LocalResolver , private _implicitReceiver : o . Expression ,
258+ private bindingId : string ) { }
176259
177260 visitBinary ( ast : cdAst . Binary , mode : _Mode ) : any {
178261 let op : o . BinaryOperator ;
@@ -246,20 +329,19 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
246329 }
247330
248331 visitPipe ( ast : cdAst . BindingPipe , mode : _Mode ) : any {
249- const input = this . visit ( ast . exp , _Mode . Expression ) ;
250- const args = this . visitAll ( ast . args , _Mode . Expression ) ;
251- const value = this . _nameResolver . callPipe ( ast . name , input , args ) ;
252- if ( ! value ) {
253- throw new Error ( `Illegal state: Pipe ${ ast . name } is not allowed here!` ) ;
254- }
255- this . needsValueUnwrapper = true ;
256- return convertToStatementIfNeeded ( mode , this . _valueUnwrapper . callMethod ( 'unwrap' , [ value ] ) ) ;
332+ throw new Error (
333+ `Illegal state: Pipes should have been converted into functions. Pipe: ${ ast . name } ` ) ;
257334 }
258335
259336 visitFunctionCall ( ast : cdAst . FunctionCall , mode : _Mode ) : any {
260- return convertToStatementIfNeeded (
261- mode ,
262- this . visit ( ast . target , _Mode . Expression ) . callFn ( this . visitAll ( ast . args , _Mode . Expression ) ) ) ;
337+ const convertedArgs = this . visitAll ( ast . args , _Mode . Expression ) ;
338+ let fnResult : o . Expression ;
339+ if ( ast instanceof BuiltinFunctionCall ) {
340+ fnResult = ast . converter ( convertedArgs ) ;
341+ } else {
342+ fnResult = this . visit ( ast . target , _Mode . Expression ) . callFn ( convertedArgs ) ;
343+ }
344+ return convertToStatementIfNeeded ( mode , fnResult ) ;
263345 }
264346
265347 visitImplicitReceiver ( ast : cdAst . ImplicitReceiver , mode : _Mode ) : any {
@@ -301,32 +383,18 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
301383 }
302384
303385 visitLiteralArray ( ast : cdAst . LiteralArray , mode : _Mode ) : any {
304- const parts = this . visitAll ( ast . expressions , mode ) ;
305- const literalArr =
306- this . isAction ? o . literalArr ( parts ) : createCachedLiteralArray ( this . _builder , parts ) ;
307- return convertToStatementIfNeeded ( mode , literalArr ) ;
386+ throw new Error ( `Illegal State: literal arrays should have been converted into functions` ) ;
308387 }
309388
310389 visitLiteralMap ( ast : cdAst . LiteralMap , mode : _Mode ) : any {
311- const parts : any [ ] = [ ] ;
312- for ( let i = 0 ; i < ast . keys . length ; i ++ ) {
313- parts . push ( [ ast . keys [ i ] , this . visit ( ast . values [ i ] , _Mode . Expression ) ] ) ;
314- }
315- const literalMap =
316- this . isAction ? o . literalMap ( parts ) : createCachedLiteralMap ( this . _builder , parts ) ;
317- return convertToStatementIfNeeded ( mode , literalMap ) ;
390+ throw new Error ( `Illegal State: literal maps should have been converted into functions` ) ;
318391 }
319392
320393 visitLiteralPrimitive ( ast : cdAst . LiteralPrimitive , mode : _Mode ) : any {
321394 return convertToStatementIfNeeded ( mode , o . literal ( ast . value ) ) ;
322395 }
323396
324- private _getLocal ( name : string ) : o . Expression {
325- if ( this . isAction && name == EventHandlerVars . event . name ) {
326- return EventHandlerVars . event ;
327- }
328- return this . _nameResolver . getLocal ( name ) ;
329- }
397+ private _getLocal ( name : string ) : o . Expression { return this . _localResolver . getLocal ( name ) ; }
330398
331399 visitMethodCall ( ast : cdAst . MethodCall , mode : _Mode ) : any {
332400 const leftMostSafe = this . leftMostSafeNode ( ast ) ;
@@ -581,7 +649,8 @@ function flattenStatements(arg: any, output: o.Statement[]) {
581649 }
582650}
583651
584- function createCachedLiteralArray ( builder : ClassBuilder , values : o . Expression [ ] ) : o . Expression {
652+ function legacyCreateCachedLiteralArray (
653+ builder : ClassBuilder , values : o . Expression [ ] ) : o . Expression {
585654 if ( values . length === 0 ) {
586655 return o . importExpr ( createIdentifier ( Identifiers . EMPTY_ARRAY ) ) ;
587656 }
@@ -601,7 +670,7 @@ function createCachedLiteralArray(builder: ClassBuilder, values: o.Expression[])
601670 return proxyExpr . callFn ( values ) ;
602671}
603672
604- function createCachedLiteralMap (
673+ function legacyCreateCachedLiteralMap (
605674 builder : ClassBuilder , entries : [ string , o . Expression ] [ ] ) : o . Expression {
606675 if ( entries . length === 0 ) {
607676 return o . importExpr ( createIdentifier ( Identifiers . EMPTY_MAP ) ) ;
@@ -624,10 +693,23 @@ function createCachedLiteralMap(
624693 return proxyExpr . callFn ( values ) ;
625694}
626695
696+ class DefaultLocalResolver implements LocalResolver {
697+ getLocal ( name : string ) : o . Expression {
698+ if ( name === EventHandlerVars . event . name ) {
699+ return EventHandlerVars . event ;
700+ }
701+ return null ;
702+ }
703+ }
627704
628- class DefaultNameResolver implements NameResolver {
705+ class LegacyDefaultNameResolver implements LegacyNameResolver {
629706 callPipe ( name : string , input : o . Expression , args : o . Expression [ ] ) : o . Expression { return null ; }
630- getLocal ( name : string ) : o . Expression { return null ; }
707+ getLocal ( name : string ) : o . Expression {
708+ if ( name === EventHandlerVars . event . name ) {
709+ return EventHandlerVars . event ;
710+ }
711+ return null ;
712+ }
631713}
632714
633715function createCurrValueExpr ( bindingId : string ) : o . ReadVarExpr {
@@ -646,3 +728,9 @@ function convertStmtIntoExpression(stmt: o.Statement): o.Expression {
646728 }
647729 return null ;
648730}
731+
732+ class BuiltinFunctionCall extends cdAst . FunctionCall {
733+ constructor ( span : cdAst . ParseSpan , public args : cdAst . AST [ ] , public converter : BuiltinConverter ) {
734+ super ( span , null , args ) ;
735+ }
736+ }
0 commit comments