@@ -96,6 +96,10 @@ const {
9696 globalThis,
9797} = primordials ;
9898
99+ const {
100+ isProxy,
101+ } = require ( 'internal/util/types' ) ;
102+
99103const { BuiltinModule } = require ( 'internal/bootstrap/realm' ) ;
100104const {
101105 makeRequireFunction,
@@ -1328,8 +1332,10 @@ function completeFSFunctions(match) {
13281332// -> [['util.print', 'util.debug', 'util.log', 'util.inspect'],
13291333// 'util.' ]
13301334//
1331- // Warning: This eval's code like "foo.bar.baz", so it will run property
1332- // getter code.
1335+ // Warning: This evals code like "foo.bar.baz", so it could run property
1336+ // getter code. To avoid potential triggering side-effects with getters the completion
1337+ // logic is skipped when getters or proxies are involved in the expression.
1338+ // (see: https://github.com/nodejs/node/issues/57829).
13331339function complete ( line , callback ) {
13341340 // List of completion lists, one for each inheritance "level"
13351341 let completionGroups = [ ] ;
@@ -1525,50 +1531,61 @@ function complete(line, callback) {
15251531 return ;
15261532 }
15271533
1528- let chaining = '.' ;
1529- if ( StringPrototypeEndsWith ( expr , '?' ) ) {
1530- expr = StringPrototypeSlice ( expr , 0 , - 1 ) ;
1531- chaining = '?.' ;
1532- }
1533-
1534- const memberGroups = [ ] ;
1535- const evalExpr = `try { ${ expr } } catch {}` ;
1536- this . eval ( evalExpr , this . context , getREPLResourceName ( ) , ( e , obj ) => {
1537- try {
1538- let p ;
1539- if ( ( typeof obj === 'object' && obj !== null ) ||
1540- typeof obj === 'function' ) {
1541- ArrayPrototypePush ( memberGroups , filteredOwnPropertyNames ( obj ) ) ;
1542- p = ObjectGetPrototypeOf ( obj ) ;
1543- } else {
1544- p = obj . constructor ? obj . constructor . prototype : null ;
1534+ return includesProxiesOrGetters (
1535+ StringPrototypeSplit ( match , '.' ) ,
1536+ this . eval ,
1537+ this . context ,
1538+ ( includes ) => {
1539+ if ( includes ) {
1540+ // The expression involves proxies or getters, meaning that it
1541+ // can trigger side-effectful behaviors, so bail out
1542+ return completionGroupsLoaded ( ) ;
15451543 }
1546- // Circular refs possible? Let's guard against that.
1547- let sentinel = 5 ;
1548- while ( p !== null && sentinel -- !== 0 ) {
1549- ArrayPrototypePush ( memberGroups , filteredOwnPropertyNames ( p ) ) ;
1550- p = ObjectGetPrototypeOf ( p ) ;
1544+
1545+ let chaining = '.' ;
1546+ if ( StringPrototypeEndsWith ( expr , '?' ) ) {
1547+ expr = StringPrototypeSlice ( expr , 0 , - 1 ) ;
1548+ chaining = '?.' ;
15511549 }
1552- } catch {
1553- // Maybe a Proxy object without `getOwnPropertyNames` trap.
1554- // We simply ignore it here, as we don't want to break the
1555- // autocompletion. Fixes the bug
1556- // https://github.com/nodejs/node/issues/2119
1557- }
15581550
1559- if ( memberGroups . length ) {
1560- expr += chaining ;
1561- ArrayPrototypeForEach ( memberGroups , ( group ) => {
1562- ArrayPrototypePush ( completionGroups ,
1563- ArrayPrototypeMap ( group ,
1564- ( member ) => `${ expr } ${ member } ` ) ) ;
1565- } ) ;
1566- filter &&= `${ expr } ${ filter } ` ;
1567- }
1551+ const memberGroups = [ ] ;
1552+ const evalExpr = `try { ${ expr } } catch {}` ;
1553+ this . eval ( evalExpr , this . context , getREPLResourceName ( ) , ( e , obj ) => {
1554+ try {
1555+ let p ;
1556+ if ( ( typeof obj === 'object' && obj !== null ) ||
1557+ typeof obj === 'function' ) {
1558+ ArrayPrototypePush ( memberGroups , filteredOwnPropertyNames ( obj ) ) ;
1559+ p = ObjectGetPrototypeOf ( obj ) ;
1560+ } else {
1561+ p = obj . constructor ? obj . constructor . prototype : null ;
1562+ }
1563+ // Circular refs possible? Let's guard against that.
1564+ let sentinel = 5 ;
1565+ while ( p !== null && sentinel -- !== 0 ) {
1566+ ArrayPrototypePush ( memberGroups , filteredOwnPropertyNames ( p ) ) ;
1567+ p = ObjectGetPrototypeOf ( p ) ;
1568+ }
1569+ } catch {
1570+ // Maybe a Proxy object without `getOwnPropertyNames` trap.
1571+ // We simply ignore it here, as we don't want to break the
1572+ // autocompletion. Fixes the bug
1573+ // https://github.com/nodejs/node/issues/2119
1574+ }
15681575
1569- completionGroupsLoaded ( ) ;
1570- } ) ;
1571- return ;
1576+ if ( memberGroups . length ) {
1577+ expr += chaining ;
1578+ ArrayPrototypeForEach ( memberGroups , ( group ) => {
1579+ ArrayPrototypePush ( completionGroups ,
1580+ ArrayPrototypeMap ( group ,
1581+ ( member ) => `${ expr } ${ member } ` ) ) ;
1582+ } ) ;
1583+ filter &&= `${ expr } ${ filter } ` ;
1584+ }
1585+
1586+ completionGroupsLoaded ( ) ;
1587+ } ) ;
1588+ } ) ;
15721589 }
15731590
15741591 return completionGroupsLoaded ( ) ;
@@ -1626,6 +1643,34 @@ function complete(line, callback) {
16261643 }
16271644}
16281645
1646+ function includesProxiesOrGetters ( exprSegments , evalFn , context , callback , currentExpr = '' , idx = 0 ) {
1647+ const currentSegment = exprSegments [ idx ] ;
1648+ currentExpr += `${ currentExpr . length === 0 ? '' : '.' } ${ currentSegment } ` ;
1649+ evalFn ( `try { ${ currentExpr } } catch { }` , context , getREPLResourceName ( ) , ( _ , currentObj ) => {
1650+ if ( typeof currentObj !== 'object' || currentObj === null ) {
1651+ return callback ( false ) ;
1652+ }
1653+
1654+ if ( isProxy ( currentObj ) ) {
1655+ return callback ( true ) ;
1656+ }
1657+
1658+ const nextIdx = idx + 1 ;
1659+
1660+ if ( nextIdx >= exprSegments . length ) {
1661+ return callback ( false ) ;
1662+ }
1663+
1664+ const nextSegmentProp = ObjectGetOwnPropertyDescriptor ( currentObj , exprSegments [ nextIdx ] ) ;
1665+ const nextSegmentPropHasGetter = typeof nextSegmentProp ?. get === 'function' ;
1666+ if ( nextSegmentPropHasGetter ) {
1667+ return callback ( true ) ;
1668+ }
1669+
1670+ return includesProxiesOrGetters ( exprSegments , evalFn , context , callback , currentExpr , nextIdx ) ;
1671+ } ) ;
1672+ }
1673+
16291674REPLServer . prototype . completeOnEditorMode = ( callback ) => ( err , results ) => {
16301675 if ( err ) return callback ( err ) ;
16311676
0 commit comments