PowerShell 3 – Finally on the DLR!

For those of you living in a cave: PowerShell 3 will be released in Windows 8, and we got a CTP at roughly the same time as the Windows 8 Developer Preview was released (at Microsoft’s new //Build/ conference in September 2011). A second CTP was released just in time for Christmas.

I’ve been playing with PowerShell 3 for a few months now, and I guess it’s long past time I started blogging about it.

There are a lot of new things coming in this release, but for me, the biggest change is the fact that PowerShell is now based on the Dynamic Language Runtime, a runtime environment that adds a set of services for dynamic languages to the Common Language Runtime (CLR), which is the core of the .NET Framework. The DLR makes it easier to develop dynamic languages to run on the .NET Framework. Of course, PowerShell is a dynamic language that runs on the .NET framework, but it was originally begun before the DLR had been released, so it’s only now that it’s finally been adapted to the DLR.

However, although PowerShell 3 is implemented using the DLR, it’s not a DLR language in every way that IronPython or IronRuby are. Let me borrow a couple of graphics from the DLR overview documentation.

The DLR Overview

DLR Overview

You can see there’s three major sections to the DLR as it’s available on CodePlex: hosting, runtime, and language. However, not all of the DLR actually shipped in the .NET Framework 4.0 CLR.

The DLR shipped in CLR 4

DLR Shipped in CLR4

PowerShell 3 takes advantage of all (or most) of what shipped in the CLR, but since the PowerShell team wasn’t willing to be responsible for shipping the rest of the DLR in the operating system, they didn’t implement the rest of it. Which is to say, PowerShell 3 is using the DLR Language Implementation code, with Shared AST and Expression trees, as well as the DynamicObject and Call Site Caching portions of the runtime, but none of the Common Hosting pieces like ScriptRuntime, ScriptScope, ScriptSource, or CompiledCode …

This means that you cannot use the same hosting APIs for PowerShell that you use for IronPython or IronRuby. However, even though you’re stuck using the same hosting APIs that you used with PowerShell 2 … you do get to use dynamic instead of PSObject when you’re working with the output in C#.

This really is a big deal

I wouldn’t care to speculate how many of the changes you’ll see in PowerShell 3 are directly due to the conversion to the DLR, but there are a few changes that you ought to be aware of. The first thing that you’ll probably notice is the difference in execution and performance. Anything you’ve learned about the relative performance of Scripts vs. Functions vs. Cmdlets and the load time of binary vs. script modules is going to go right out the window with PowerShell 3, as scripts and functions are now no longer (re)interpreted each time they’re run, but are compiled, executed, and (sometimes) cached. The result is that initial runs of scripts and imports of script modules are sometimes slower than they used to be, but subsequent runs of the same script or execution of functions from script modules run much faster, and this applies in particular to actual scripts in files and pre-defined functions in modules. Running a function repeatedly is now much faster than pasting the same code repeatedly into the console.

A more subtle, but significant difference is the change to PSObject.

In PowerShell 3, PSObject is a true dynamic object, and thus the output of cmdlets or scripts called in C# can be used with the dynamic keyword in C# instead of with the pseduo-reflection methods which are required for working with PSObject. However, this is just the tip of the iceberg, so to speak.

In PowerShell 2, all of PowerShell’s Extended Type System (ETS) was based on PSObject. New members were always added to a PSObject which is wrapped around the actual “BaseObject” — regardless of whether they came from a types.ps1xml file, or from calling Add-Member on an object. If you use Add-Member on strongly objects that are not already wrapped in a PSObject, you have to specify the -Passthru parameter and capture the output in order to have your object wrapped into a PSObject that the new member can be added to. In addition, when you cast an object to a specific type, those ETS members are mostly lost. Take this script for example:


$psObject = Get-ChildItem
$psObject.Count
$Count1 = ($psObject | where { $_.PSIsContainer }).Count

[IO.FileSystemInfo[]]$ioObject = Get-ChildItem
$ioObject.Count
$Count2 = ($ioObject | where { $_.PSIsContainer }).Count

$Count3 = ($ioObject | where { $_ -is [IO.DirectoryInfo] }).Count
 

In PowerShell 2, $Count1 and $Count3 will be the number of folders in the current directory, but $Count2 will always be ZERO, because the PSIsContainer property is actually an ETS property that’s lost when you cast the object to FileSystemInfo (and therefore it always evaluates as null and

However, in PowerShell 3 that’s no longer true. PowerShell now works with everything as dynamic objects, and Add-Member no longer needs the PSObject to keep track of these ETS members. This script will now get $Count1, $Count2, and $Count3 equal, as expected. Obviously the -Passthru switch on Add-Member is only needed when you’re trying to pipeline things, and not for simple assignments. However, there may also be other implications on when things get wrapped into a PSObject, and when it matters.

I think you’ll agree that having PowerShell on the DLR is awesome! But be aware that there are a few inconsequential breaking changes hiding in this kind of stuff. For example, after running that script above, try these three lines on PowerShell 2 and PowerShell 3 CTP2:


$Count1 -eq $Count2
$e = $ioObject[0] | Add-Member NoteProperty Note "This is a note" -Passthru
$f = $ioObject[0] | Add-Member NoteProperty Note "This is a note" -Passthru
 

In PowerShell 2, you’ll get False, and then the next two lines will work fine. In PowerShell 3 the first line will return True, and since Add-Member actually affects the underlying object even when it’s not wrapped in a PSObject, the third line will actually cause an error, because “Add-Member : Cannot add a member with the name “Note” because a member with that name already exists.”

Anyway, I’m sure I’ll have more to write about the DLR and the changes it’s bringing to PowerShell, but for now, I hope that’s enough to get you thinking ;-)

Similar Posts:

15 thoughts on “PowerShell 3 – Finally on the DLR!”

  1. “Running a function repeatedly is now MUCH slower than pasting the same code repeatedly into the console”

    Don’t you mean faster?

  2. Unfortunately, it would appear that PowerShell still isn’t able to natively deal with dynamic dispatch objects returned by CLR methods. When I attempt to use an ExpandoObject instance in PowerShell, it just sees the Keys and Values property of it’s underlying IDictionary implementation.

  3. Sure:

    Add-Type -TypeDefinition @"
    using System;
    using System.Collections.Generic;
    using System.Dynamic;
    public static class DynamicMaker {
        public static dynamic MakeDynamic(string propertyName, object value) {
            IDictionary obj = new ExpandoObject();
            obj[propertyName] = value;
            return obj;
        }
    }
    "
    @

    $Obj = [DynamicMaker]::MakeDynamic("Color", [ConsoleColor]::Blue)

    @($Obj) | Format-List
     

    I would expect the result to be:

    Color: Blue

    Instead it is:

    Key: Color
    Value: Blue

    Treating ExpandoObject as a dictionary and not a dynamic object with a single Color property.

    1. Ok, yeah, I came up with my own example, and … I guess this deserves it’s own blog post, but … it does work, mostly. It needs some help for Get-Member and maybe a format file though.

      Add-Type -ReferencedAssemblies System.Core, Microsoft.CSharp @"
      namespace Expando {
          using System;
          using System.Dynamic;
          using System.Management.Automation;
         
          [Cmdlet("
      Get", "Expando")]
          public class Test : Cmdlet {

              public static dynamic GetThing() {
                  dynamic expando = new ExpandoObject();
                  // Adding New Members
                  // properties, methods (and events?) can be added to instances of the ExpandoObject class.
                  expando.FirstName = "Joel";
                  expando.LastName = "
      Bennett";
                  expando.GetFullName = (Func<String>)(() => {
                      return String.Format("
      {0}, {1}",  expando.LastName, expando.FirstName);
                  });

                  return expando;
              }

              protected override void ProcessRecord() {
                  WriteObject( GetThing() );
              }
          }
      }
      "@

      $thing = [Expando.Test]::GetThing()

      # Thinks that $thing is a KeyValuePair<String,Object>
      $thing | Get-Member

      # Shows that $thing is really an ExpandoObject:
      Get-Member -InputObject $thing  
       

      But even though it’s an ExpandoObject, you can’t figure out what properties are on it without looking at the key/value stuff. Once you know, you can access them, although the methods need .Invoke()ing:

      <#C:\PS #>⁠⁠⁠ $thing["FirstName"]
      Joel

      <#C:\PS #>⁠⁠⁠ $thing.FirstName
      Joel

      <#C:\PS #>⁠⁠⁠ ⁠⁠$thing.FirstName = "Katrina"

      <#C:\PS #>⁠⁠⁠ $thing.GetFullName.Invoke()
      Bennett, Katrina
       

  4. Yeah! I have seen the presentation Video from Jeffrey Snover and Refaat Issa at the Build conference.
    The say execution of code is up to 6* faster.

    As a .NET Developer and Admin, I was crying all the time, about the One-way affair, between PowerShell and the .Net Framework. Because PowerShell takes all advantages of the Framework, but there is no backflow from PowerShell to the Framework (PS-Modules/Snapins as Classes into the Framework, to use it in C# or VB natively).
    I am Happy to go a step forward in this relationship.

    My credo is: PowerShell everywhere! (As a replacement for Kixtart, DOS, VBscript..)
    If you want to use PowerShell for Logon scripts there is a Gap. One of the Arguments against PowerShell for this purpose is that PowerShell starts up to slow. How fast is that with V3 and a script?

    So I see a need for precompiled scripts, to add more speed! What’s with that?

    My next question which comes into mind is; which role plays the .NET Code Access Security in that game?
    (I did not take knowledge of the new .NET 4 CAS)

    1. So, yeah … PowerShell still starts like a .Net app starts, so it’s slower to start than most scripting languages. And of course, since you can’t store the script pre-compiled, it has to be compiled on first run — which is kind-of a pain for startup scripts. My impression is that in some cases they’ll automatically cache scripts’ pre-compiled versions, but I’m not sure that’s true.

      I’m really not sure how CAS applies. I do know that .Net 4 had better defaults for UNC paths, which is a relief, but I’m not a CAS expert.

  5. Ahh thanks, yeah seems like they might want to build in some special case logic for anything imeplementing IDynamicMetaObjectProvider so that it calls the GetDynamicMemberNames method.

    In the meantime I used this as a workaround.

        function ConvertFrom-DictionaryObject {
            process {
                $Obj = New-Object PSObject
                foreach ($Key in $_.Keys) {
                    Add-Member -InputObject $Obj NoteProperty $Key $_[$Key]
                }
                $Obj
            }
        }
     

    But then again doing this is basically the same situation we had in v2.

  6. Okay I have no idea what I am doing with this comment system. :) I meant to reply, and I tried both HTML and Markdown and can’t seem to format my code like you have.

    1. I should really stick in a toolbar … it’s textile, but to trigger syntax highlighting I use geshi, which means you have to tell it what language it is with a tag: <code lang="posh">

Comments are closed.