@@ -489,7 +489,7 @@ private function MustacheStatement(MustacheStatement $mustache): string
489489 $ cvArgs = '$in, ' . self ::quote ($ helperName ) . ($ this ->options ->strict ? ', true ' : '' );
490490 return self ::getRuntimeFunc ($ fn , self ::getRuntimeFunc ('lookupValue ' , $ cvArgs ));
491491 }
492- $ expression = $ this ->PathExpression ($ path );
492+ return self :: getRuntimeFunc ( $ fn , $ this ->PathExpression ($ path, true ) );
493493 } else {
494494 // Literal in simple position: same lambda resolution as PathExpression above.
495495 $ literalKey = $ this ->getLiteralKeyName ($ path );
@@ -511,7 +511,7 @@ private function SubExpression(SubExpression $expression): string
511511 return $ this ->compileHelperCall ($ helperName , $ path , $ expression ->params , $ expression ->hash );
512512 }
513513
514- private function PathExpression (PathExpression $ path ): string
514+ private function PathExpression (PathExpression $ path, bool $ wrapLambda = false ): string
515515 {
516516 $ data = $ path ->data ;
517517 $ depth = $ path ->depth ;
@@ -528,10 +528,6 @@ private function PathExpression(PathExpression $path): string
528528 $ stringParts = $ path ->parts ;
529529 }
530530
531- if (!$ stringParts ) {
532- return $ base ;
533- }
534-
535531 $ isLength = end ($ stringParts ) === 'length ' ;
536532 $ isCurrentContextPath = !$ hasSubExprHead && !$ data && $ depth === 0 ;
537533 $ scoped = $ isCurrentContextPath && self ::scopedId ($ path );
@@ -541,25 +537,26 @@ private function PathExpression(PathExpression $path): string
541537 $ bp = $ this ->lookupBlockParam ($ path ->head );
542538 if ($ bp !== null ) {
543539 [$ bpDepth , $ bpIndex ] = $ bp ;
544- $ bpBase = "\$blockParams[ $ bpDepth][ $ bpIndex] " ;
540+ $ base = "\$blockParams[ $ bpDepth][ $ bpIndex] " ;
545541 // Skip the block param name since it has been resolved to a $blockParams index.
546- $ keys = $ isLength ? array_slice ($ path ->tail , 0 , -1 ) : $ path ->tail ;
547- $ lookup = $ this ->compileModeAwareLookup ($ bpBase , $ keys );
548- return $ isLength ? $ this ->buildLookupLength ($ lookup ) : $ lookup ;
542+ $ stringParts = $ path ->tail ;
549543 }
550544 }
551545
552- // Handle .length: compile parent path through the normal mode-aware logic, then wrap in
553- // lookupLength() at runtime. This mirrors HBS.js, where .length is a normal property
554- // access with no compile-time special casing.
555546 if ($ isLength ) {
556- $ partsExceptLength = array_slice ($ stringParts , 0 , -1 );
557- return $ this ->buildLookupLength (
558- $ this ->compileModeAwareLookup ($ base , $ partsExceptLength , $ scoped ),
559- );
547+ array_pop ($ stringParts );
560548 }
549+ $ result = $ this ->compileModeAwareLookup ($ base , $ stringParts , $ scoped );
561550
562- return $ this ->compileModeAwareLookup ($ base , $ stringParts , $ scoped );
551+ if ($ isLength ) {
552+ // Handle .length: compile parent path through the normal mode-aware logic, then wrap in
553+ // lookupLength() at runtime. This mirrors HBS.js, where .length is a normal property
554+ // access with no compile-time special casing.
555+ $ strict = $ this ->options ->strict || $ this ->options ->assumeObjects ;
556+ $ result = self ::getRuntimeFunc ('lookupLength ' , $ strict ? "$ result, true " : $ result );
557+ }
558+
559+ return ($ wrapLambda && !$ isLength ) ? self ::getRuntimeFunc ('lambda ' , $ result ) : $ result ;
563560 }
564561
565562 /**
@@ -805,12 +802,6 @@ private function compileProgramOrEmpty(?Program $program): string
805802 return $ this ->compileProgram ($ program );
806803 }
807804
808- private function buildLookupLength (string $ parent ): string
809- {
810- $ strict = $ this ->options ->strict || $ this ->options ->assumeObjects ;
811- return self ::getRuntimeFunc ('lookupLength ' , $ strict ? "$ parent, true " : $ parent );
812- }
813-
814805 /**
815806 * Compile a mode-aware path access expression for the given base and parts.
816807 * @param string[] $parts
0 commit comments