Powershell: Modules produced by repeat calls to GetNewClosure() are related. But how, exactly?

Created on 6 Mar 2019  路  8Comments  路  Source: PowerShell/PowerShell

The docs for ScriptBlock.GetNewClosure() state the following:

Returns a new scriptblock bound to a module. Any local variables in the callers context will be copied into the module.

I have been assuming that repeat invocations of GetNewClosure() would result in scriptblocks bound to different modules. However, this does not seem to be the case. The following code

$m = New-Module {}

foreach ($sb in @(
    { $v = 'normal assignment'                        }
    { Set-Variable v 'Set-Variable'                   }
    { Set-Variable v 'Set-Variable -Scope 1' -Scope 1 }
))
{
    . ( $sb  | . $m { process { $_.GetNewClosure() } } ) # set value
    . ( {$v} | . $m { process { $_.GetNewClosure() } } ) # output value
}

outputs

Set-Variable -Scope 1

In other words, a variable set by $sb.GetNewClosure() is visible from {$v}.GetNewClosure(). The modules bound to each scriptblock seem to be somehow related, but not identical.

I'm hoping someone can shed some light on the nature of the module to which each of the scriptblocks output by repeat calls to GetNewClosure() is bound and how those modules are related to one another.

Environment Data

Written as of :

PowerShell Core 6.1.3 release
Issue-Question Resolution-Answered

Most helpful comment

@mklement0 It seems like that might be a bug. It reminds me of the inconsistent behavior in #6378. That turned out to be a bug.

All 8 comments

@alx9r When you're dotsourcing a module bound scriptblock, the current scope will be the ModuleScope (same as psm1) while it's being invoked. So "Scope 1" there is global.

Also, @alx9r, there's only _one_ module instance in your code, $m, and all your . invocations happen in its top-level scope (where, as @SeeminglyScience points out, -Scope 1 refers to the _global_ scope).

So "Scope 1" there is global.

Oh I see. Thanks @SeeminglyScience.

(For posterity, that link to "global" is more nuanced than I remembered: See "One last detail..." in #6139.)

there's only one module instance in your code, $m, and all your . invocations happen in its top-level scope

I count 7 module instances: $m plus the 6 produced by the 6 invocations of .GetNewClosure(). @mklement0 are you saying that those are all actually the same module?

Invoking

$m = New-Module {}
$m.Name

foreach ($sb in @(
    { $v = 'normal assignment'                        }
    { Set-Variable v 'Set-Variable'                   }
    { Set-Variable v 'Set-Variable -Scope 1' -Scope 1 }
))
{
    $sb  | . $m { process { $_.GetNewClosure() } } | % {$_.Module.Name}
    {$v} | . $m { process { $_.GetNewClosure() } } | % {$_.Module.Name}
}

shows 7 different module names on my computer.

Thanks, @alx9r - clearly I had a misconception there.

I missed that .GetNewClosure() _creates_ a new module, correct?

I missed that .GetNewClosure() creates a new module, correct?

@mklement0 That's my current understanding of what happens.

As for the link to the global scope: it is possible - though I've never seen it in the wild - to create a module that _doesn't_ have that link - or, rather, it _does and it doesn't_. A bug?

@PetSerAl pointed me to this PSModuleInfo constructor that, when passed $false, purports to create a module that opts out of the link:

# WITH link to global scope.
PS> $global:var = 42; . ([psmoduleinfo]::new($true)) { $global:var }
42

# WITHOUT link to global scope.
PS> $global:var = 42; . ([psmoduleinfo]::new($false)) { $global:var }
  # NO OUTPUT. Get-Variable -Scope Global var would fail.

However, _without a scope qualifier_ the global variable _is_ seen:

# WITHOUT link to global scope, but REFERENCE WITHOUT SCOPE QUALIFIER
PS> $global:var = 42; . ([psmoduleinfo]::new($false)) { $var }
42 # !! Unexpectedly still seen; ditto with `Get-Variable var` (without -Scope)

@mklement0 It seems like that might be a bug. It reminds me of the inconsistent behavior in #6378. That turned out to be a bug.

Thanks, @alx9r - I've created #9080.

Was this page helpful?
0 / 5 - 0 ratings