IronRuby on Your Phone

Windows Phone 7 application platform was finally revealed at MIX10. It generated a lot of excitement among .NET developers presumably due to the fact that it’s based on Silverlight. No need to learn a completely new programming model or libraries. Since the .NET Framework supports many languages you might wonder if you have the same choice on the Phone. In particular, could you script the Phone using languages built on Dynamic Language Runtime? I’m excited to announce that IronRuby 1.0 RC4 works on Windows Phone 7! Although there are some limitations the basic scripting just works as you’d expect. But keep in mind that the platform is still a technology preview (CTP) so not all features need to work seamlessly. Let’s dig into details.

Limitations

First of all, why are there any limitations at all if the Phone’s platform is Silverlight and IronRuby works on Silverlight since its inception? Although the UI and Media APIs are pretty much the same the Phone differs from the browser plug-in in the CLR execution engine and the Base Class Library. The Phone uses Compact Framework (CF) which provides only a subset of functionality available in the browser. The two platforms should converge in the future and the limitations will hopefully go away.

The main feature missing from the CF is Reflection.Emit. There are other differences but this is the most important one concerning dynamic languages. As you can imagine we do emit some code at runtime to implement dynamic features of our languages. However we also have an interpreter around. Each DLR based language can choose when to use the interpreter and when the compiler. Both IronRuby and IronPython interpret user code that is executed only a few times. It’s not efficient to compile such code since the cost for doing so is high and the execution throughput is not important. On the other hand code that’s running many times should be compiled and optimized. If the interpreter finds out that a function or a loop body is being executed more than N times it spawns its asynchronous background compilation. When the compilation is done the instructions that call the function or enter the loop are replaced by new ones that jump to the compiled code. The threshold N is configurable.

It should be clear now how to work around the lack of Reflection.Emit on the Phone. Let’s just interpret all the time! And indeed it mostly works. You won’t get a great throughput performance out of it but it works just fine for common scripts. Since the CLR interop is still available you can write performance critical code in C# and call it from the scripts. There are only a few CLR features that you need to avoid since the current interpreter doesn’t handle them without resorting to Reflection.Emit. The most significant are calls to methods with out or ref parameters. You also won’t be able to inherit a Ruby class from a CLR class or implement a CLR interface since that requires us to emit a proper CLR type. We are going to address both of these limitations in future versions.

So, IronRuby runs on the Phone. Why IronPython doesn’t yet? There is no technical reason why it couldn’t. Some parts of the IronPython runtime were just written before we implemented the interpreter and thus need some refactoring to enable full interpretation. You can motivate us to do the work faster by voting on CodePlex.

Sample App

Let’s write an app that hosts IronRuby on the Phone. It’s really simple! You just use the DLR Hosting APIs as you would do when embedding a DLR scripting language in a Silverlight app. I’ll show only the interesting parts of the app. You can download the full source code here. You’ll need Visual Studio 2010 for Windows Phone CTP and IronRuby 1.0 RC4 to try it out. Install them both and then open PhoneScripter.sln, build the solution and deploy to the emulator. Make sure that references to IronRuby and DLR assemblies in the project are correct.

The few lines that you need to run a Ruby script in the Phone emulator (and hopefully on the real device when available) are:

  • _engine = Ruby.CreateEngine((setup) => { setup.Options["CompilationThreshold"] = Int32.MaxValue; });

    Creates a Ruby engine that never compiles the interpreted code. I guess IronRuby could max out the compilation threshold by default when running on the Compact Framework. For now you need to do this manually.

  • _engine.Runtime.LoadAssembly(typeof(Color).Assembly);

    Loads System.Windows assembly into the dynamic runtime so that we can script the UI. System and mscorlib assemblies are loaded by default.

  • RubyContext context = (RubyContext)HostingHelpers.GetLanguageContext(_engine);
    context.ObjectClass.SetConstant("Phone", this);

    Makes the MainPage class instance (this) available to the script via a global constant Phone. We’ll use it to access UI elements on the page. This is a hack! You should use ScriptScope Hosting API to expose host objects to the script in your .NET apps. However, the scope dynamic object internally uses methods with out parameters and we can’t interpret calls to it from Ruby. We’ll enable this in future. For now, we work it around by directly accessing IronRuby’s RubyContext class. Be aware that this class is not a part of the Hosting APIs, we might change it in future versions and break your code. So do not use it in any production code.

  • MemoryStream stream = new MemoryStream();
    _engine.Runtime.IO.SetOutput(stream, Encoding.UTF8);
    
    try {
        try {
            _engine.Execute(Input.Text);
        } finally {
            byte[] bytes = stream.ToArray();
            Output.Text += Encoding.UTF8.GetString(bytes, 0, bytes.Length);
        }
    } catch (Exception ex) {
        Output.Text += ex.Message;
    }

    Executes a script entered in Input TextBox, captures its output and appends it to the content of Output TextBox.

We can now enter and run a Ruby script:

PhoneScripter

Forwarding meta-object

The Dynamic Language Runtime (DLR) allows us to add dynamic behavior to .NET classes. Writing your own dynamic class, i.e. a class implementing IDynamicMetaObjectProvider interface, might be as easy as inheriting from System.Dynamic.DynamicObject. Or you might need more control over the dynamic operations and implement the IDynamicMetaObjectProvider interface manually. Or maybe you already have an existing class and need to add dynamic behavior while preserving its parent class. And perhaps you already have a class that implements IDynamicMetaObjectProvider, find this implementation useful and want to reuse it. What code do we need to write to achieve that?

The ForwardingMetaObject class defined below makes it easy to forward dynamic operations from one dynamic class (forwarder) to another (forwardee).

public sealed class ForwardingMetaObject : DynamicMetaObject {
    private readonly DynamicMetaObject _metaForwardee;

    public ForwardingMetaObject(Expression expression, BindingRestrictions restrictions, object forwarder, 
        IDynamicMetaObjectProvider forwardee, Func<Expression, Expression> forwardeeGetter)
        : base(expression, restrictions, forwarder) { 
       
        // We'll use forwardee's meta-object to bind dynamic operations.
        _metaForwardee = forwardee.GetMetaObject(
            forwardeeGetter(
                Expression.Convert(expression, forwarder.GetType())   // [1]
            )
        );      
    }

    // Restricts the target object's type to TForwarder. 
    // The meta-object we are forwarding to assumes that it gets an instance of TForwarder (see [1]). 
    // We need to ensure that the assumption holds.
    private DynamicMetaObject AddRestrictions(DynamicMetaObject result) {
        return new DynamicMetaObject(
           result.Expression,
           BindingRestrictions.GetTypeRestriction(Expression, Value.GetType()).Merge(result.Restrictions),
           _metaForwardee.Value
       );
    }

    // Forward all dynamic operations or some of them as needed //

    public override DynamicMetaObject BindGetMember(GetMemberBinder binder) {
        return AddRestrictions(_metaForwardee.BindGetMember(binder));
    }

    public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args) {
        return AddRestrictions(_metaForwardee.BindInvokeMember(binder, args));
    }

    public override DynamicMetaObject BindConvert(ConvertBinder binder) {
        return AddRestrictions(_metaForwardee.BindConvert(binder));
    }

    // ... //
}

Let’s use this class in an example. Let’s define some class B simply as a subclass of DynamicObject that supports GetMember, InvokeMember and Convert to String dynamic operations:

public class B : DynamicObject {
    private readonly string _name;

    public B(string name) {
        _name = name;
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result) {
        result = "got member " + _name + "." + binder.Name;
        return true;
    }

    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) {
        result = "invoked member " + _name + "." + binder.Name;
        return true;
    }

    public override bool TryConvert(ConvertBinder binder, out object result) {
        if (binder.Type == typeof(string)) {
            result = _name;
            return true;
        } else {
            result = null;
            return false;
        }
    }
}

And now let’s define a class A that forwards these operations to B via ForwardingMetaObject:

public class A : IDynamicMetaObjectProvider {
    private readonly B _b;
    public B B { get { return _b; } }

    public A(B b) {
        _b = b;
    }

    DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(Expression parameter) {
        return new ForwardingMetaObject(parameter, BindingRestrictions.Empty, this, _b, 
            // B's meta-object needs to know where to find the instance of B it is operating on.
            // Assuming that an instance of A is passed to the 'parameter' expression
            // we get the corresponding instance of B by reading the "B" property.
            exprA => Expression.Property(exprA, "B")
        );
    }
}

Finally, let’s run some C# 4.0 code that shows how it all works together:

class Program {
    static void Main(string[] args) {
        B b1 = new B("b1");
        B b2 = new B("b2");

        dynamic a1 = new A(b1);
        dynamic a2 = new A(b2);

        Console.WriteLine(a1.SomeMember);
        Console.WriteLine(a2.SomeMember);
        Console.WriteLine(a1.SomeMethodCall());
        Console.WriteLine(a1.OtherMethodCall());
        Console.WriteLine((string)a1);
    }
}

Output:

got member b1.SomeMember
got member b2.SomeMember
invoked member b1.SomeMethodCall
invoked member b1.OtherMethodCall
b1

The really great thing about DLR is that your dynamic objects will also work in IronRuby and IronPython even though you haven’t thought about it at all when authoring them. Assuming that we compiled the above code into MyDynamicLibrary.dll we can run IronRuby REPL and check it out:

IronRuby 0.9.1.0 on .NET 4.0.20915.0
Copyright (c) Microsoft Corporation. All rights reserved.

>>> require 'MyDynamicLibrary.dll'
=> true
>>> b1, b2 = B.new('[b1 in Ruby]'), B.new('[b2 in Ruby]')
=> [B, B]
>>> a1, a2 = A.new(b1), A.new(b2)
=> [B, B]
>>> a1.SomeMethodCall
=> 'invoked member [b1 in Ruby].SomeMethodCall'
>>> a2.OtherMethodCall
=> 'invoked member [b2 in Ruby].OtherMethodCall'
>>> a1.to_s                             # invokes ToString
=> "B"                                   
>>> a1.to_str                           # invokes dynamic to string conversion
=> "[b1 in Ruby]"

Multilingual REPL

DLR Hosting API is a common API for all DLR languages: IronRuby, IronPython, and others. It abstracts away language specific details and provides API that could be used in a language independent manner. The API is primarily designed to ease usage of dynamic languages from strongly typed .NET languages and to make it easier to write applications with a scriptable object model. Nonetheless we can take advantage of the API in a scripting language as well. For that purpose the most interesting features are management of isolated scripting environments (script runtimes) and cross language code execution. I presented the former in my last post. Let’s check out the latter today.

To run examples below you’ll need to build the latest IronRuby, IronPython and DLR from sources. You can do so using the latest sources from IronRuby GIT repository or DLR CodePlex site:

  • Building from IronRuby GIT repo (mapped locally to C:\Git\ironruby):

    C:\Git\ironruby\Merlin\Main > msbuild Languages\Ruby\Ruby.sln
    C:\Git\ironruby\Merlin\Main > msbuild Languages\IronPython\IronPython.sln

    We’ve recently included IronPython projects to the repository. If you already have the repo set up just pull the latest commit. You can also use rake to build IronRuby, we don’t provide rake file for IronPython though.

  • Building from DLR CodePlex (downloaded and extracted to C:\CodePlex\DLR_Main):

    C:\Codeplex\DLR_Main > msbuild Codeplex-DLR.sln

Both builds build unsigned debug assemblies and place the binaries into bin\Debug directory.

Available Languages

The DLR runtime can host multiple languages at once. Listing all languages that are available is easy:

# github: languages.rb
# load IronRuby library that provides Ruby programs an easy access to the Hosting API:
require 'IronRuby'

# create a new DLR runtime:
runtime = IronRuby.create_runtime
  
# list all available languages:
runtime.setup.language_setups.each { |ls| puts "#{ls.display_name}: #{ls.names.inspect}" }

The last line enumerates all instances of LanguageSetup class (defined by Hosting API) that are stored in the setup of the runtime. For each such language setup its display name and a list of short names are printed. The short names uniquely identify languages within the runtime. A language can be identified by multiple short names.

C:\Codeplex\DLR_Main\Bin\Debug>ir languages.rb
IronPython 2.6 Alpha: ["IronPython", "Python", "py"]
IronRuby 1.0 Alpha: ["IronRuby", "Ruby", "rb"]
ToyScript: ["ToyScript", "ts"]

Where does this list come from? We have said nothing about IronPython in our script. Does IronRuby know about IronPython? Making IronRuby dependent on all languages it could possibly interact with wouldn’t be a good design. Instead, Hosting API creates and sets up a script runtime according to the current .NET application’s configuration. It is told to do so in IronRuby#create_runtime. The .NET application is ir.exe hence the configuration is loaded from ir.exe.config file that lives next to the ir.exe. Indeed, the list of languages can be found in this file:

<languages>
  <language names="IronPython;Python;py" extensions=".py" displayName="IronPython 2.6 Alpha"
    type="IronPython.Runtime.PythonContext, IronPython, Version=2.6.0.1, Culture=neutral, PublicKeyToken=null" />
  <language names="IronRuby;Ruby;rb" extensions=".rb" displayName="IronRuby 1.0 Alpha"     
    type="IronRuby.Runtime.RubyContext, IronRuby, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
  <language names="ToyScript;ts" extensions=".ts"    
    type="ToyScript.ToyLanguageContext, ToyScript, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</languages>

Whenever you find a DLR language you want to use for scripting your app and don’t have yet you can just add it to the list and you’re all set.

Hosting IronPython in IronRuby

Now that we know how IronPython identifies itself to the runtime, we can load its engine and execute some Python code. Let’s take the simple REPL implementation from the previous post and change it to execute Python code instead of Ruby:

# github: python_repl.rb
require 'IronRuby'
runtime = IronRuby.create_runtime

# ask the runtime for Python engine using one of its simple names:
engine = runtime.get_engine("IronPython")

scope = engine.create_scope

while true  
  print "> "
  code = gets
  break if code.nil?
  begin
    result = engine.execute(code, scope)

    # print the Ruby's view of the result:
    p result

    # print the result as Python would display it:
    puts engine.operations.format(result)
  rescue Exception
    puts $!
  end
end

Let’s play now:

C:\Codeplex\DLR_Main\Bin\Debug>ir python_repl.rb
> def add(a,b): return a + b
nil
None
> add
#<IronPython::Runtime::PythonFunction:0x000005c>
<function add at 0x000000000000002B>
> add(1,1)
2
2
> add((1, 2), (3, 4))
#<IronPython::Runtime::PythonTuple:0x000005e>
(1, 2, 3, 4)
> ^Z

As you can see, each language might have a different view on the same object.

To make the REPL really useful we need to support multi-line statements (i.e. wait with code execution until a complete statement is entered). Let’s keep that and more for the next post.

Isolated Code Execution

Every Ruby programmer is familiar with irban interactive Ruby shell, aka REPL (Read-Eval-Print Loop). It is a handy tool for discovering how Ruby language features or libraries work. It has some limitations though. It is entirely written in Ruby and so you might bump into a problem like this:

C:\Program Files\Ruby\bin> irb
irb(main):001:0> class Fixnum
irb(main):002:1>   remove_method :+
irb(main):003:1* end
=> Fixnum
irb(main):004:0> 1
C:/M1/Merlin/External/Languages/Ruby/ruby-1.8.6/lib/ruby/1.8/irb/input-method.rb:99:in `gets': undefined method `+' for
3:Fixnum (NoMethodError)
        from C:/M1/Merlin/External/Languages/Ruby/ruby-1.8.6/lib/ruby/1.8/irb.rb:132:in `eval_input'
        from C:/M1/Merlin/External/Languages/Ruby/ruby-1.8.6/lib/ruby/1.8/irb.rb:259:in `signal_status'
        from C:/M1/Merlin/External/Languages/Ruby/ruby-1.8.6/lib/ruby/1.8/irb.rb:131:in `eval_input'
        from C:/M1/Merlin/External/Languages/Ruby/ruby-1.8.6/lib/ruby/1.8/irb/ruby-lex.rb:189:in `call'
        from C:/M1/Merlin/External/Languages/Ruby/ruby-1.8.6/lib/ruby/1.8/irb/ruby-lex.rb:189:in `buf_input'
        from C:/M1/Merlin/External/Languages/Ruby/ruby-1.8.6/lib/ruby/1.8/irb/ruby-lex.rb:104:in `getc'
        from C:/M1/Merlin/External/Languages/Ruby/ruby-1.8.6/lib/ruby/1.8/irb/slex.rb:206:in `match_io'
        from C:/M1/Merlin/External/Languages/Ruby/ruby-1.8.6/lib/ruby/1.8/irb/slex.rb:76:in `match'
         ... 8 levels...
        from C:/M1/Merlin/External/Languages/Ruby/ruby-1.8.6/lib/ruby/1.8/irb.rb:70:in `start'
        from C:/M1/Merlin/External/Languages/Ruby/ruby-1.8.6/lib/ruby/1.8/irb.rb:69:in `catch'
        from C:/M1/Merlin/External/Languages/Ruby/ruby-1.8.6/lib/ruby/1.8/irb.rb:69:in `start'
        from C:/M1/Merlin/External/Languages/Ruby/ruby-1.8.6/bin/irb:13 
C:\Program Files\Ruby\bin> 

Oops. What happened? We removed method “+” from Fixnum class and, accidentally, irb itself uses that method somewhere in the REPL implementation. The solution is obvious. Run any user code typed in an isolated environment (or “virtual machine”) different from the one that drives the REPL. Let’s take a look how to do this in IronRuby.

Hosting API

The Dynamic Language Runtime (DLR), which IronRuby is built on top of, provides API for hosting dynamic languages in an arbitrary .NET application (see Hosting API specification). You only need to know a few concepts to use this API in a powerful way. The first is script runtime. Each instance of ScriptRuntime class provides an environment for script execution. The runtime hosts scripting language engines. A single runtime can host engines of multiple DLR languages (IronRuby, IronPython, etc.), at most one engine for each language. Each engine is represented by an instance of ScriptEngine class. It holds on a global state that the particular language defines. IronRuby engine, for example, maintains a dictionary of global variables, objects that represent Ruby modules and classes, the list of loaded Ruby files, etc. An application can create any number of ScriptRuntimes and thus any number of IronRuby or IronPython “virtual machines” in a single process.

Hosting API is implemented in C# in Microsoft.Scripting.dll assembly distributed with IronRuby. IronRuby has a rich support for .NET interop, so we could load this assembly and use the Hosting API classes directly from IronRuby scripts. Although it would be simple we made it even easier: IronRuby comes with IronRuby module that provides basic hosting API specialized for Ruby.

Ruby REPL Based on Hosting API

A very simple REPL in Ruby might read as follows.

# load IronRuby library
require 'IronRuby'

# create a new IronRuby engine and a new runtime:
engine = IronRuby.create_engine

while true  
  print "> "
  code = gets
  break if code.nil?
  begin
    p engine.execute(code)
  rescue Exception
    puts $!
  end
end

The REPL script itself runs in a script runtime created by ir.exe host. The code typed in the loop is evaluated in the runtime created by IronRuby#create_engine. The result of ScriptEngine#execute is sent to the calling runtime and printed out via Kernel#p method.

C:\IronRuby\Merlin\Main\Bin\Debug\ir.exe repl.rb
> 1+1
2
> Fixnum.send :remove_method, :+
Fixnum@2
> 1+1
undefined method `+' for 1:Fixnum
> 2.times { |i| puts i }
0
1
2
> ^Z

Note that there are as many class objects for each class as there are runtimes in which the class is used. IronRuby appends “@n” to the name of a class that comes from a different runtime, whose id is n, to differentiate it from the class of the same name in the current runtime. Hence Module#remove_method, which returns the class the method has been removed from, returns “Fixnum@2”.

There are many ways in which we can make our REPL better. The most serious deficiency can be easily demonstrated:

> x = 1
1
> puts x
undefined method `x' for main:Object

Why we got an exception? We defined a local variable x in the first line, so why is it not available in the second line? We are missing some kind of variable dictionary shared among multiple snippet executions. If we used Kernel#eval in Ruby we would pass an instance of Binding to it. The Hosting API uses a similar concept: script scope. I’m going to explain DLR scopes in a separate blog post in detail. Let’s just say for now that IronRuby associates a top-level Ruby binding with each ScriptScope class instance against which it executes code. We need to make a very simple change in our REPL script to use DLR scopes:

# github: repl.rb
require 'IronRuby'
engine = IronRuby.create_engine
scope = engine.create_scope

while true  
  print "> "
  code = gets
  break if code.nil?
  begin
    p engine.execute(code, scope) 
  rescue Exception
    puts $!
  end
end

Local variables work as expected now (actually, the fix in IronRuby source code that makes this work is not yet committed to the public repo, it’s coming soon though):

> x = 1
1
> puts x
1
nil

Another feature our simple REPL lacks is handling of multi-line expressions and statements. Let’s keep that and more for the next blog post.