Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds a Test-Path check when loading modules; Fixes a stack overflow exception when using: scoped variables are remapped #1300

Merged
merged 3 commits into from
May 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/Private/AutoImport.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ function Import-PodeModulesIntoRunspaceState {

# import the module
$path = Find-PodeModuleFile -Module $module
if ([string]::IsNullOrEmpty($path) -or !(Test-Path $path)) {
continue
}

if (($module.ModuleType -ieq 'Manifest') -or ($path.EndsWith('.ps1'))) {
$PodeContext.RunspaceState.ImportPSModule($path)
Expand Down
4 changes: 3 additions & 1 deletion src/Private/Helpers.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -937,7 +937,9 @@ function Add-PodePSDrives {
function Import-PodeModules {
# import other modules in the session
foreach ($path in $PodeContext.Server.Modules.Values) {
$null = Import-Module $path -DisableNameChecking -Scope Global -ErrorAction Stop
if (Test-Path $path) {
$null = Import-Module $path -DisableNameChecking -Scope Global -ErrorAction Stop
}
}
}

Expand Down
125 changes: 92 additions & 33 deletions src/Private/ScopedVariables.ps1
Original file line number Diff line number Diff line change
@@ -1,3 +1,52 @@
function Add-PodeScopedVariableInternal {
[CmdletBinding(DefaultParameterSetName = 'Replace')]
param(
[Parameter(Mandatory = $true)]
[string]
$Name,

[Parameter(Mandatory = $true, ParameterSetName = 'Replace')]
[string]
$GetReplace,

[Parameter(ParameterSetName = 'Replace')]
[string]
$SetReplace = $null,

[Parameter(Mandatory = $true, ParameterSetName = 'ScriptBlock')]
[scriptblock]
$ScriptBlock,

[Parameter(ParameterSetName = 'Internal')]
[switch]
$InternalFunction
)

# lowercase the name
$Name = $Name.ToLowerInvariant()

# check if var already defined
if (Test-PodeScopedVariable -Name $Name) {
throw "Scoped Variable already defined: $($Name)"
}

# add scoped var definition
$PodeContext.Server.ScopedVariables[$Name] = @{
Name = $Name
Type = $PSCmdlet.ParameterSetName.ToLowerInvariant()
ScriptBlock = $ScriptBlock
Get = @{
Pattern = "(?<full>\`$$($Name)\:(?<name>[a-z0-9_\?]+))"
Replace = $GetReplace
}
Set = @{
Pattern = "(?<full>\`$$($Name)\:(?<name>[a-z0-9_\?]+)\s*=)"
Replace = $SetReplace
}
InternalFunction = $InternalFunction.IsPresent
}
}

function Add-PodeScopedVariablesInbuilt {
Add-PodeScopedVariableInbuiltUsing
Add-PodeScopedVariableInbuiltCache
Expand Down Expand Up @@ -31,48 +80,58 @@ function Add-PodeScopedVariableInbuiltState {
}

function Add-PodeScopedVariableInbuiltUsing {
Add-PodeScopedVariable -Name 'using' -ScriptBlock {
param($ScriptBlock, $PSSession)

# do nothing if no script or session
if (($null -eq $ScriptBlock) -or ($null -eq $PSSession)) {
return $ScriptBlock, $null
}
Add-PodeScopedVariableInternal -Name 'using' -InternalFunction
}

# rename any __using_ vars for inner timers, etcs
$strScriptBlock = "$($ScriptBlock)"
$foundInnerUsing = $false
function Convert-PodeScopedVariableInbuiltUsing {
param(
[Parameter(ValueFromPipeline = $true)]
[scriptblock]
$ScriptBlock,

while ($strScriptBlock -imatch '(?<full>\$__using_(?<name>[a-z0-9_\?]+))') {
$foundInnerUsing = $true
$strScriptBlock = $strScriptBlock.Replace($Matches['full'], "`$using:$($Matches['name'])")
}
[Parameter()]
[System.Management.Automation.SessionState]
$PSSession
)

# just return if there are no $using:
if ($strScriptBlock -inotmatch '\$using:') {
return $ScriptBlock, $null
}
# do nothing if no script or session
if (($null -eq $ScriptBlock) -or ($null -eq $PSSession)) {
return $ScriptBlock, $null
}

# if we found any inner usings, recreate the scriptblock
if ($foundInnerUsing) {
$ScriptBlock = [scriptblock]::Create($strScriptBlock)
}
# rename any __using_ vars for inner timers, etcs
$strScriptBlock = "$($ScriptBlock)"
$foundInnerUsing = $false

# get any using variables
$usingVars = Get-PodeScopedVariableUsingVariables -ScriptBlock $ScriptBlock
if (($null -eq $usingVars) -or ($usingVars.Count -eq 0)) {
return $ScriptBlock, $null
}
while ($strScriptBlock -imatch '(?<full>\$__using_(?<name>[a-z0-9_\?]+))') {
$foundInnerUsing = $true
$strScriptBlock = $strScriptBlock.Replace($Matches['full'], "`$using:$($Matches['name'])")
}

# convert any using vars to use new names
$usingVars = Find-PodeScopedVariableUsingVariableValues -UsingVariables $usingVars -PSSession $PSSession
# just return if there are no $using:
if ($strScriptBlock -inotmatch '\$using:') {
return $ScriptBlock, $null
}

# now convert the script
$newScriptBlock = Convert-PodeScopedVariableUsingVariables -ScriptBlock $ScriptBlock -UsingVariables $usingVars
# if we found any inner usings, recreate the scriptblock
if ($foundInnerUsing) {
$ScriptBlock = [scriptblock]::Create($strScriptBlock)
}

# return converted script
return $newScriptBlock, $usingVars
# get any using variables
$usingVars = Get-PodeScopedVariableUsingVariables -ScriptBlock $ScriptBlock
if (($null -eq $usingVars) -or ($usingVars.Count -eq 0)) {
return $ScriptBlock, $null
}

# convert any using vars to use new names
$usingVars = Find-PodeScopedVariableUsingVariableValues -UsingVariables $usingVars -PSSession $PSSession

# now convert the script
$newScriptBlock = Convert-PodeScopedVariableUsingVariables -ScriptBlock $ScriptBlock -UsingVariables $usingVars

# return converted script
return $newScriptBlock, $usingVars
}

function Get-PodeScopedVariableUsingVariables {
Expand Down
90 changes: 41 additions & 49 deletions src/Public/ScopedVariables.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -122,43 +122,52 @@ function Convert-PodeScopedVariable {
# get the scoped var metadata
$scopedVar = $PodeContext.Server.ScopedVariables[$Name]

# scriptblock or replace?
if ($null -ne $scopedVar.ScriptBlock) {
return Invoke-PodeScriptBlock `
-ScriptBlock $scopedVar.ScriptBlock `
-Arguments $ScriptBlock, $PSSession, $scopedVar.Get.Pattern, $scopedVar.Set.Pattern `
-Splat `
-Return `
-NoNewClosure
}

# replace style
else {
# convert scriptblock to string
$strScriptBlock = "$($ScriptBlock)"
# invoke the logic for the appropriate conversion type required - internal function map, custom scriptblock, or simple replace
switch ($scopedVar.Type) {
'internal' {
switch ($scopedVar.Name) {
'using' {
return Convert-PodeScopedVariableInbuiltUsing -ScriptBlock $ScriptBlock -PSSession $PSSession
}
}
}

# see if the script contains any form of the scoped variable, and if not just return
$found = $strScriptBlock -imatch "\`$$($Name)\:"
if (!$found) {
return $ScriptBlock
'scriptblock' {
return Invoke-PodeScriptBlock `
-ScriptBlock $scopedVar.ScriptBlock `
-Arguments $ScriptBlock, $PSSession, $scopedVar.Get.Pattern, $scopedVar.Set.Pattern `
-Splat `
-Return `
-NoNewClosure
}

# loop and replace "set" syntax if replace template supplied
if (![string]::IsNullOrEmpty($scopedVar.Set.Replace)) {
while ($strScriptBlock -imatch $scopedVar.Set.Pattern) {
$setReplace = $scopedVar.Set.Replace.Replace('{{name}}', $Matches['name'])
$strScriptBlock = $strScriptBlock.Replace($Matches['full'], $setReplace)
'replace' {
# convert scriptblock to string
$strScriptBlock = "$($ScriptBlock)"

# see if the script contains any form of the scoped variable, and if not just return
$found = $strScriptBlock -imatch "\`$$($Name)\:"
if (!$found) {
return $ScriptBlock
}
}

# loop and replace "get" syntax
while ($strScriptBlock -imatch $scopedVar.Get.Pattern) {
$getReplace = $scopedVar.Get.Replace.Replace('{{name}}', $Matches['name'])
$strScriptBlock = $strScriptBlock.Replace($Matches['full'], "($($getReplace))")
}
# loop and replace "set" syntax if replace template supplied
if (![string]::IsNullOrEmpty($scopedVar.Set.Replace)) {
while ($strScriptBlock -imatch $scopedVar.Set.Pattern) {
$setReplace = $scopedVar.Set.Replace.Replace('{{name}}', $Matches['name'])
$strScriptBlock = $strScriptBlock.Replace($Matches['full'], $setReplace)
}
}

# convert update scriptblock back
return [scriptblock]::Create($strScriptBlock)
# loop and replace "get" syntax
while ($strScriptBlock -imatch $scopedVar.Get.Pattern) {
$getReplace = $scopedVar.Get.Replace.Replace('{{name}}', $Matches['name'])
$strScriptBlock = $strScriptBlock.Replace($Matches['full'], "($($getReplace))")
}

# convert update scriptblock back
return [scriptblock]::Create($strScriptBlock)
}
}
}

Expand Down Expand Up @@ -226,24 +235,7 @@ function Add-PodeScopedVariable {
$ScriptBlock
)

# check if var already defined
if (Test-PodeScopedVariable -Name $Name) {
throw "Scoped Variable already defined: $($Name)"
}

# add scoped var definition
$PodeContext.Server.ScopedVariables[$Name] = @{
$Name = $Name
ScriptBlock = $ScriptBlock
Get = @{
Pattern = "(?<full>\`$$($Name)\:(?<name>[a-z0-9_\?]+))"
Replace = $GetReplace
}
Set = @{
Pattern = "(?<full>\`$$($Name)\:(?<name>[a-z0-9_\?]+)\s*=)"
Replace = $SetReplace
}
}
Add-PodeScopedVariableInternal @PSBoundParameters
}

<#
Expand Down
Loading