371 changes: 371 additions & 0 deletions src/Scripting/CSharp/CSharpObjectFormatter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,371 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Roslyn.Utilities;

namespace Roslyn.Scripting.CSharp
{
public sealed class CSharpObjectFormatter : ObjectFormatter
{
public static readonly CSharpObjectFormatter Instance = new CSharpObjectFormatter();

private CSharpObjectFormatter()
{
}

public override object VoidDisplayString
{
get { return "<void>"; }
}

public override string NullLiteral
{
get { return ObjectDisplay.NullLiteral; }
}

public override string FormatLiteral(bool value)
{
return ObjectDisplay.FormatLiteral(value);
}

public override string FormatLiteral(string value, bool quote, bool useHexadecimalNumbers = false)
{
var options = ObjectDisplayOptions.None;
if (quote)
{
options |= ObjectDisplayOptions.UseQuotes;
}
if (useHexadecimalNumbers)
{
options |= ObjectDisplayOptions.UseHexadecimalNumbers;
}
return ObjectDisplay.FormatLiteral(value, options);
}

public override string FormatLiteral(char c, bool quote, bool includeCodePoints = false, bool useHexadecimalNumbers = false)
{
var options = ObjectDisplayOptions.None;
if (quote)
{
options |= ObjectDisplayOptions.UseQuotes;
}
if (includeCodePoints)
{
options |= ObjectDisplayOptions.IncludeCodePoints;
}
if (useHexadecimalNumbers)
{
options |= ObjectDisplayOptions.UseHexadecimalNumbers;
}
return ObjectDisplay.FormatLiteral(c, options);
}

public override string FormatLiteral(sbyte value, bool useHexadecimalNumbers = false)
{
return ObjectDisplay.FormatLiteral(value, GetObjectDisplayOptions(useHexadecimalNumbers));
}

public override string FormatLiteral(byte value, bool useHexadecimalNumbers = false)
{
return ObjectDisplay.FormatLiteral(value, GetObjectDisplayOptions(useHexadecimalNumbers));
}

public override string FormatLiteral(short value, bool useHexadecimalNumbers = false)
{
return ObjectDisplay.FormatLiteral(value, GetObjectDisplayOptions(useHexadecimalNumbers));
}

public override string FormatLiteral(ushort value, bool useHexadecimalNumbers = false)
{
return ObjectDisplay.FormatLiteral(value, GetObjectDisplayOptions(useHexadecimalNumbers));
}

public override string FormatLiteral(int value, bool useHexadecimalNumbers = false)
{
return ObjectDisplay.FormatLiteral(value, GetObjectDisplayOptions(useHexadecimalNumbers));
}

public override string FormatLiteral(uint value, bool useHexadecimalNumbers = false)
{
return ObjectDisplay.FormatLiteral(value, GetObjectDisplayOptions(useHexadecimalNumbers));
}

public override string FormatLiteral(long value, bool useHexadecimalNumbers = false)
{
return ObjectDisplay.FormatLiteral(value, GetObjectDisplayOptions(useHexadecimalNumbers));
}

public override string FormatLiteral(ulong value, bool useHexadecimalNumbers = false)
{
return ObjectDisplay.FormatLiteral(value, GetObjectDisplayOptions(useHexadecimalNumbers));
}

public override string FormatLiteral(double value)
{
return ObjectDisplay.FormatLiteral(value, ObjectDisplayOptions.None);
}

public override string FormatLiteral(float value)
{
return ObjectDisplay.FormatLiteral(value, ObjectDisplayOptions.None);
}

public override string FormatLiteral(decimal value)
{
return ObjectDisplay.FormatLiteral(value, ObjectDisplayOptions.None);
}

public override string FormatTypeName(Type type, ObjectFormattingOptions options)
{
return GetPrimitiveTypeName(type) ?? AppendComplexTypeName(new StringBuilder(), type, options).ToString();
}

private static string GetPrimitiveTypeName(Type type)
{
switch (Type.GetTypeCode(type))
{
case TypeCode.Boolean: return "bool";
case TypeCode.Byte: return "byte";
case TypeCode.Char: return "char";
case TypeCode.Decimal: return "decimal";
case TypeCode.Double: return "double";
case TypeCode.Int16: return "short";
case TypeCode.Int32: return "int";
case TypeCode.Int64: return "long";
case TypeCode.SByte: return "sbyte";
case TypeCode.Single: return "float";
case TypeCode.String: return "string";
case TypeCode.UInt16: return "ushort";
case TypeCode.UInt32: return "uint";
case TypeCode.UInt64: return "ulong";

case TypeCode.Object:
case TypeCode.Empty:
case TypeCode.DBNull:
case TypeCode.DateTime:
default:
if (type == typeof(object))
{
return "object";
}
return null;
}
}

private StringBuilder AppendComplexTypeName(StringBuilder builder, Type type, ObjectFormattingOptions options)
{
if (type.IsArray)
{
builder.Append(FormatArrayTypeName(type, arrayOpt: null, options: options));
return builder;
}

// compiler generated (e.g. iterator/async)
string stateMachineName;
if (GeneratedNames.TryParseSourceMethodNameFromGeneratedName(type.Name, GeneratedNameKind.StateMachineType, out stateMachineName))
{
builder.Append(stateMachineName);
return builder;
}

if (type.IsGenericType)
{
// consolidated generic arguments (includes arguments of all declaring types):
Type[] genericArguments = type.GetGenericArguments();

if (type.DeclaringType != null)
{
List<Type> nestedTypes = new List<Type>();
do
{
nestedTypes.Add(type);
type = type.DeclaringType;
}
while (type != null);

int typeArgumentIndex = 0;
for (int i = nestedTypes.Count - 1; i >= 0; i--)
{
AppendTypeInstantiation(builder, nestedTypes[i], genericArguments, ref typeArgumentIndex, options);
if (i > 0)
{
builder.Append('.');
}
}
}
else
{
int typeArgumentIndex = 0;
return AppendTypeInstantiation(builder, type, genericArguments, ref typeArgumentIndex, options);
}
}
else if (type.DeclaringType != null)
{
builder.Append(type.Name.Replace('+', '.'));
}
else
{
builder.Append(type.Name);
}

return builder;
}

private StringBuilder AppendTypeInstantiation(StringBuilder builder, Type type, Type[] genericArguments, ref int genericArgIndex,
ObjectFormattingOptions options)
{
// generic arguments of all the outer types and the current type;
Type[] currentGenericArgs = type.GetGenericArguments();
int currentArgCount = currentGenericArgs.Length - genericArgIndex;

if (currentArgCount > 0)
{
int backtick = type.Name.IndexOf('`');
if (backtick > 0)
{
builder.Append(type.Name.Substring(0, backtick));
}
else
{
builder.Append(type.Name);
}

builder.Append('<');

for (int i = 0; i < currentArgCount; i++)
{
if (i > 0)
{
builder.Append(", ");
}
builder.Append(FormatTypeName(genericArguments[genericArgIndex++], options));
}

builder.Append('>');
}
else
{
builder.Append(type.Name);
}

return builder;
}

public override string FormatArrayTypeName(Array array, ObjectFormattingOptions options)
{
return FormatArrayTypeName(array.GetType(), array, options);
}

private string FormatArrayTypeName(Type arrayType, Array arrayOpt, ObjectFormattingOptions options)
{
StringBuilder sb = new StringBuilder();

// print the inner-most element type first:
Type elementType = arrayType.GetElementType();
while (elementType.IsArray)
{
elementType = elementType.GetElementType();
}

sb.Append(FormatTypeName(elementType, options));

// print all components of a jagged array:
Type type = arrayType;
do
{
if (arrayOpt != null)
{
sb.Append('[');

int rank = type.GetArrayRank();

bool anyNonzeroLowerBound = false;
for (int i = 0; i < rank; i++)
{
if (arrayOpt.GetLowerBound(i) > 0)
{
anyNonzeroLowerBound = true;
break;
}
}

for (int i = 0; i < rank; i++)
{
int lowerBound = arrayOpt.GetLowerBound(i);
long length = arrayOpt.GetLongLength(i);

if (i > 0)
{
sb.Append(", ");
}

if (anyNonzeroLowerBound)
{
AppendArrayBound(sb, lowerBound, options.UseHexadecimalNumbers);
sb.Append("..");
AppendArrayBound(sb, length + lowerBound, options.UseHexadecimalNumbers);
}
else
{
AppendArrayBound(sb, length, options.UseHexadecimalNumbers);
}
}

sb.Append(']');
arrayOpt = null;
}
else
{
AppendArrayRank(sb, type);
}

type = type.GetElementType();
}
while (type.IsArray);

return sb.ToString();
}

private void AppendArrayBound(StringBuilder sb, long bound, bool useHexadecimalNumbers)
{
if (bound <= Int32.MaxValue)
{
sb.Append(FormatLiteral((int)bound, useHexadecimalNumbers));
}
else
{
sb.Append(FormatLiteral(bound, useHexadecimalNumbers));
}
}

private static void AppendArrayRank(StringBuilder sb, Type arrayType)
{
sb.Append('[');
int rank = arrayType.GetArrayRank();
if (rank > 1)
{
sb.Append(',', rank - 1);
}
sb.Append(']');
}

public override string FormatMemberName(System.Reflection.MemberInfo member)
{
return member.Name;
}

public override bool IsHiddenMember(System.Reflection.MemberInfo member)
{
// Generated fields, e.g. "<property_name>k__BackingField"
return GeneratedNames.IsGeneratedMemberName(member.Name);
}
}
}
189 changes: 189 additions & 0 deletions src/Scripting/CSharp/CSharpScript.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Diagnostics;
using System.Globalization;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Text;

namespace Roslyn.Scripting.CSharp
{
/// <summary>
/// A factory for creating and running csharp scripts.
/// </summary>
public sealed class CSharpScript : Script
{
private CSharpScript(string code, string path, ScriptOptions options, Type globalsType, Type returnType, ScriptBuilder builder, Script previous)
: base(code, path, options, globalsType, returnType, builder, previous)
{
}

internal override Script Make(string code, string path, ScriptOptions options, Type globalsType, Type returnType, ScriptBuilder builder, Script previous)
{
return new CSharpScript(code, path, options, globalsType, returnType, builder, previous);
}

/// <summary>
/// Create a new C# script.
/// <param name="code">The source code of the script.</param>
/// <param name="options">The script options.</param>
/// </summary>
public static Script Create(string code, ScriptOptions options)
{
return new CSharpScript(code, null, options, null, typeof(object), null, null);
}

/// <summary>
/// Create a new C# script.
/// <param name="code">The source code of the script.</param>
/// </summary>
public static Script Create(string code)
{
return Create(code, null);
}

/// <summary>
/// Run a C# script.
/// </summary>
/// <param name="code">The source code of the script.</param>
/// <param name="options">The script options.</param>
/// <param name="globals">An object instance whose members can be accessed by the script as global variables,
/// or a <see cref="ScriptState"/> instance that was the output from a previously run script.</param>
public static ScriptState Run(string code, ScriptOptions options, object globals)
{
return Create(code, options).Run(globals);
}

/// <summary>
/// Run a C# script.
/// </summary>
/// <param name="code">The source code of the script.</param>
/// <param name="options">The script options.</param>
public static ScriptState Run(string code, ScriptOptions options)
{
return Run(code, options, globals: null);
}

/// <summary>
/// Run a C# script.
/// </summary>
/// <param name="code">The source code of the script.</param>
/// <param name="globals">An object instance whose members can be accessed by the script as global variables,
/// or a <see cref="ScriptState"/> instance that was the output from a previously run script.</param>
public static ScriptState Run(string code, object globals)
{
return Run(code, options: null, globals: globals);
}

/// <summary>
/// Run a C# script.
/// </summary>
/// <param name="code">The source code of the script.</param>
public static ScriptState Run(string code)
{
return Run(code, null, null);
}

/// <summary>
/// Run a C# script and return its resulting value.
/// </summary>
/// <param name="code">The source code of the script.</param>
/// <param name="options">The script options.</param>
/// <param name="globals">An object instance whose members can be accessed by the script as global variables,
/// or a <see cref="ScriptState"/> instance that was the output from a previously run script.</param>
/// <return>Returns the value returned by running the script.</return>
public static object Eval(string code, ScriptOptions options, object globals)
{
return Run(code, options, globals).ReturnValue;
}

/// <summary>
/// Run a C# script and return its resulting value.
/// </summary>
/// <param name="code">The source code of the script.</param>
/// <param name="options">The script options.</param>
/// <return>Returns the value returned by running the script.</return>
public static object Eval(string code, ScriptOptions options)
{
return Run(code, options).ReturnValue;
}

/// <summary>
/// Run a C# script and return its resulting value.
/// </summary>
/// <param name="code">The source code of the script.</param>
/// <param name="globals">An object instance whose members can be accessed by the script as global variables,
/// or a <see cref="ScriptState"/> instance that was the output from a previously run script.</param>
/// <return>Returns the value returned by running the script.</return>
public static object Eval(string code, object globals)
{
return Run(code, globals).ReturnValue;
}

/// <summary>
/// Run a C# script and return its resulting value.
/// </summary>
/// <param name="code">The source code of the script.</param>
/// <return>Returns the value returned by running the script.</return>
public static object Eval(string code)
{
return Run(code).ReturnValue;
}

#region Compilation
private static readonly CSharpParseOptions s_defaultInteractive = new CSharpParseOptions(languageVersion: LanguageVersion.CSharp6, kind: SourceCodeKind.Interactive);
private static readonly CSharpParseOptions s_defaultScript = new CSharpParseOptions(languageVersion: LanguageVersion.CSharp6, kind: SourceCodeKind.Script);

protected override Compilation CreateCompilation()
{
Compilation previousSubmission = null;
if (this.Previous != null)
{
previousSubmission = this.Previous.GetCompilation();
}

var references = this.GetReferencesForCompilation();

var parseOptions = this.Options.IsInteractive ? s_defaultInteractive : s_defaultScript;
var tree = SyntaxFactory.ParseSyntaxTree(this.Code, parseOptions, path: this.Path);

string assemblyName, submissionTypeName;
this.Builder.GenerateSubmissionId(out assemblyName, out submissionTypeName);

var compilation = CSharpCompilation.CreateSubmission(
assemblyName,
tree,
references,
new CSharpCompilationOptions(
outputKind: OutputKind.DynamicallyLinkedLibrary,
mainTypeName: null,
scriptClassName: submissionTypeName,
usings: this.Options.Namespaces,
optimizationLevel: OptimizationLevel.Debug, // TODO
checkOverflow: false, // TODO
allowUnsafe: true, // TODO
platform: Platform.AnyCpu,
warningLevel: 4,
xmlReferenceResolver: null, // don't support XML file references in interactive (permissions & doc comment includes)
sourceReferenceResolver: SourceFileResolver.Default, // TODO
metadataReferenceResolver: this.Options.ReferenceResolver,
assemblyIdentityComparer: DesktopAssemblyIdentityComparer.Default
),
previousSubmission,
this.ReturnType,
this.GlobalsType
);

return compilation;
}

protected override string FormatDiagnostic(Diagnostic diagnostic, CultureInfo culture)
{
return CSharpDiagnosticFormatter.Instance.Format(diagnostic, culture);
}
#endregion
}
}

83 changes: 83 additions & 0 deletions src/Scripting/CSharp/CSharpScripting.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ImportGroup Label="Settings">
<Import Project="..\..\Tools\Microsoft.CodeAnalysis.Toolset.Open\Targets\VSL.Settings.targets" />
<Import Project="..\..\..\build\VSL.Settings.Closed.targets" />
</ImportGroup>
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{066F0DBD-C46C-4C20-AFEC-99829A172625}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>Roslyn.Scripting.CSharp</RootNamespace>
<AssemblyName>Roslyn.Scripting.CSharp</AssemblyName>
<SolutionDir Condition="'$(SolutionDir)' == '' OR '$(SolutionDir)' == '*Undefined*'">..\..\..\</SolutionDir>
<RestorePackages>true</RestorePackages>
</PropertyGroup>
<ItemGroup Label="Project References">
<ProjectReference Include="..\..\Compilers\Core\Desktop\CodeAnalysis.Desktop.csproj">
<Project>{dfa21ca1-7f96-47ee-940c-069858e81727}</Project>
<Name>CodeAnalysis.Desktop</Name>
</ProjectReference>
<ProjectReference Include="..\..\Compilers\Core\Portable\CodeAnalysis.csproj">
<Project>{1EE8CAD3-55F9-4D91-96B2-084641DA9A6C}</Project>
<Name>CodeAnalysis</Name>
</ProjectReference>
<ProjectReference Include="..\..\Compilers\CSharp\Portable\CSharpCodeAnalysis.csproj">
<Project>{B501A547-C911-4A05-AC6E-274A50DFF30E}</Project>
<Name>CSharpCodeAnalysis</Name>
</ProjectReference>
<ProjectReference Include="..\Core\CommonScripting.csproj">
<Project>{12A68549-4E8C-42D6-8703-A09335F97997}</Project>
<Name>CommonScripting</Name>
</ProjectReference>
</ItemGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|Any CPU' ">
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|Any CPU' ">
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|ARM' ">
<PlatformTarget>ARM</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|ARM' ">
<PlatformTarget>ARM</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Collections.Immutable, Version=1.1.33.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\packages\System.Collections.Immutable.1.1.33-beta\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll</HintPath>
</Reference>
<Reference Include="System.Core" />
</ItemGroup>
<ItemGroup>
<Compile Include="CSharpObjectFormatter.cs" />
<Compile Include="CSharpScript.cs" />
<Compile Include="CSharpScriptingResources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>CSharpScriptingResources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<InternalsVisibleToTest Include="Roslyn.Compilers.CSharp.Emit.UnitTests" />
<InternalsVisibleToTest Include="Roslyn.Compilers.UnitTests" />
<InternalsVisibleToTest Include="Roslyn.Compilers.VisualBasic.Emit.UnitTests" />
<InternalsVisibleToTest Include="Roslyn.Scripting.Common.UnitTests" />
<InternalsVisibleToTest Include="Roslyn.Scripting.CSharp.UnitTests" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="CSharpScriptingResources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>CSharpScriptingResources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ImportGroup Label="Targets">
<Import Project="..\..\Tools\Microsoft.CodeAnalysis.Toolset.Open\Targets\VSL.Imports.targets" />
<Import Project="..\..\..\build\VSL.Imports.Closed.targets" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
</ImportGroup>
</Project>
63 changes: 63 additions & 0 deletions src/Scripting/CSharp/CSharpScriptingResources.Designer.cs
101 changes: 101 additions & 0 deletions src/Scripting/CSharp/CSharpScriptingResources.resx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 1.3
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">1.3</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1">this is my long string</data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
[base64 mime encoded serialized .NET Framework object]
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
[base64 mime encoded string representing a byte array form of the .NET Framework object]
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->

<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>1.3</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
4 changes: 4 additions & 0 deletions src/Scripting/CSharp/packages.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="System.Collections.Immutable" version="1.1.33-beta" targetFramework="net45" />
</packages>
24 changes: 24 additions & 0 deletions src/Scripting/CSharpTest/CSharpScriptEngine.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;

namespace Roslyn.Scripting.CSharp
{
/// <summary>
/// Represents a runtime execution context for C# scripts.
/// </summary>
internal sealed class CSharpScriptEngine : ScriptEngine
{
public CSharpScriptEngine(MetadataFileReferenceProvider metadataReferenceProvider = null, AssemblyLoader assemblyLoader = null)
: base(metadataReferenceProvider, assemblyLoader)
{
}

internal override Script Create(string code, ScriptOptions options, Type globalsType, Type returnType)
{
return CSharpScript.Create(code, options).WithGlobalsType(globalsType).WithReturnType(returnType).WithBuilder(this.Builder);
}
}
}
116 changes: 116 additions & 0 deletions src/Scripting/CSharpTest/CSharpScriptingTest.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ImportGroup Label="Settings">
<Import Project="..\..\Tools\Microsoft.CodeAnalysis.Toolset.Open\Targets\VSL.Settings.targets" />
<Import Project="..\..\..\build\VSL.Settings.Closed.targets" />
</ImportGroup>
<PropertyGroup>
<Nonshipping>true</Nonshipping>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{2DAE4406-7A89-4B5F-95C3-BC5422CE47CE}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Roslyn.Scripting.CSharpTest</RootNamespace>
<AssemblyName>Roslyn.Scripting.CSharp.UnitTests</AssemblyName>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<SolutionDir Condition="'$(SolutionDir)' == '' OR '$(SolutionDir)' == '*Undefined*'">..\..\..\</SolutionDir>
<RestorePackages>true</RestorePackages>
</PropertyGroup>
<ItemGroup Label="Project References">
<ProjectReference Include="..\..\Compilers\Core\Desktop\CodeAnalysis.Desktop.csproj">
<Project>{dfa21ca1-7f96-47ee-940c-069858e81727}</Project>
<Name>CodeAnalysis.Desktop</Name>
</ProjectReference>
<ProjectReference Include="..\..\Compilers\Core\Portable\CodeAnalysis.csproj">
<Project>{1EE8CAD3-55F9-4D91-96B2-084641DA9A6C}</Project>
<Name>CodeAnalysis</Name>
</ProjectReference>
<ProjectReference Include="..\..\Compilers\CSharp\Desktop\CSharpCodeAnalysis.Desktop.csproj">
<Project>{079af8ef-1058-48b6-943f-ab02d39e0641}</Project>
<Name>CSharpCodeAnalysis.Desktop</Name>
</ProjectReference>
<ProjectReference Include="..\..\Compilers\CSharp\Portable\CSharpCodeAnalysis.csproj">
<Project>{B501A547-C911-4A05-AC6E-274A50DFF30E}</Project>
<Name>CSharpCodeAnalysis</Name>
</ProjectReference>
<ProjectReference Include="..\..\Compilers\VisualBasic\Desktop\BasicCodeAnalysis.Desktop.vbproj">
<Project>{73f3e2c5-d742-452e-b9e1-20732ddbc75d}</Project>
<Name>BasicCodeAnalysis.Desktop</Name>
</ProjectReference>
<ProjectReference Include="..\..\Compilers\VisualBasic\Portable\BasicCodeAnalysis.vbproj">
<Project>{2523D0E6-DF32-4A3E-8AE0-A19BFFAE2EF6}</Project>
<Name>BasicCodeAnalysis</Name>
</ProjectReference>
<ProjectReference Include="..\..\Compilers\Test\Resources\Core\CompilerTestResources.vbproj">
<Project>{7FE6B002-89D8-4298-9B1B-0B5C247DD1FD}</Project>
<Name>CompilerTestResources</Name>
</ProjectReference>
<ProjectReference Include="..\..\Compilers\Test\Utilities\CSharp\CSharpCompilerTestUtilities.csproj">
<Project>{4371944A-D3BA-4B5B-8285-82E5FFC6D1F9}</Project>
<Name>CSharpCompilerTestUtilities</Name>
</ProjectReference>
<ProjectReference Include="..\Core\CommonScripting.csproj">
<Project>{12A68549-4E8C-42D6-8703-A09335F97997}</Project>
<Name>CommonScripting</Name>
</ProjectReference>
<ProjectReference Include="..\Test\CommonScriptingTest.csproj">
<Project>{2DAE4406-7A89-4B5F-95C3-BC5472CE47CE}</Project>
<Name>CommonScriptingTest</Name>
</ProjectReference>
<ProjectReference Include="..\CSharp\CSharpScripting.csproj">
<Project>{066F0DBD-C46C-4C20-AFEC-99829A172625}</Project>
<Name>CSharpScripting</Name>
</ProjectReference>
<ProjectReference Include="..\..\Test\Utilities\TestUtilities.csproj">
<Project>{76C6F005-C89D-4348-BB4A-391898DBEB52}</Project>
<Name>TestUtilities</Name>
</ProjectReference>
<ProjectReference Include="..\..\Compilers\CSharp\Test\Symbol\CSharpCompilerSymbolTest.csproj">
<Project>{28026D16-EB0C-40B0-BDA7-11CAA2B97CCC}</Project>
<Name>CSharpCompilerSymbolTest</Name>
</ProjectReference>
<ProjectReference Include="..\..\Compilers\Test\Utilities\Core2\CompilerTestUtilities2.csproj">
<Project>{F7712928-1175-47B3-8819-EE086753DEE2}</Project>
<Name>CompilerTestUtilities2</Name>
</ProjectReference>
</ItemGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
<Reference Include="System" />
<Reference Include="System.Collections.Immutable, Version=1.1.33.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\packages\System.Collections.Immutable.1.1.33-beta\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll</HintPath>
</Reference>
<Reference Include="xunit">
<HintPath>..\..\..\packages\xunit.1.9.2\lib\net20\xunit.dll</HintPath>
</Reference>
<Reference Include="System.Core" />
<Reference Include="System.Data" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" />
</ItemGroup>
<ItemGroup>
<Compile Include="CSharpScriptEngine.cs" />
<Compile Include="InteractiveSessionTests.cs" />
<Compile Include="ObjectFormatterTests.cs" />
<Compile Include="ScriptTests.cs" />
</ItemGroup>
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ImportGroup Label="Targets">
<Import Project="..\..\Tools\Microsoft.CodeAnalysis.Toolset.Open\Targets\VSL.Imports.targets" />
<Import Project="..\..\..\build\VSL.Imports.Closed.targets" />
<Import Project="..\..\..\build\Roslyn.Toolsets.Xunit.targets" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
</ImportGroup>
</Project>
3,843 changes: 3,843 additions & 0 deletions src/Scripting/CSharpTest/InteractiveSessionTests.cs

Large diffs are not rendered by default.

1,365 changes: 1,365 additions & 0 deletions src/Scripting/CSharpTest/ObjectFormatterTests.cs

Large diffs are not rendered by default.

177 changes: 177 additions & 0 deletions src/Scripting/CSharpTest/ScriptTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Roslyn.Utilities;
using Xunit;

namespace Roslyn.Scripting.CSharp.Test
{
public class ScriptTests : CSharpTestBase
{
[Fact]
public void TestCreateScript()
{
var script = CSharpScript.Create("1 + 2");
Assert.Equal("1 + 2", script.Code);
}

[Fact]
public void TestGetCompilation()
{
var script = CSharpScript.Create("1 + 2");
var compilation = script.GetCompilation();
Assert.Equal(script.Code, compilation.SyntaxTrees.First().GetText().ToString());
}

[Fact]
public void TestCreateScriptDelegate()
{
// create a delegate for the entire script
var script = CSharpScript.Create("1 + 2");
var fn = script.CreateDelegate();
var value = fn();
Assert.Equal(3, value);
}

[Fact]
public void TestRunScript()
{
var result = CSharpScript.Run("1 + 2");
Assert.Equal(3, result.ReturnValue);
}

[Fact]
public void TestCreateAndRunScript()
{
var script = CSharpScript.Create("1 + 2");
var result = script.Run();
Assert.Same(script, result.Script);
Assert.Equal(3, result.ReturnValue);
}

[Fact]
public void TestEvalScript()
{
var value = CSharpScript.Eval("1 + 2");
Assert.Equal(3, value);
}

[Fact]
public void TestRunScriptWithSpecifiedReturnType()
{
var result = CSharpScript.Create("1 + 2").WithReturnType(typeof(int)).Run();
Assert.Equal(3, result.ReturnValue);
}

[Fact]
public void TestRunVoidScript()
{
var result = CSharpScript.Run("Console.WriteLine(0);");
}

public class Globals
{
public int X;
public int Y;
}

[Fact]
public void TestRunScriptWithGlobals()
{
var result = CSharpScript.Run("X + Y", new Globals { X = 1, Y = 2 });
Assert.Equal(3, result.ReturnValue);
}

[Fact]
public void TestRunCreatedScriptWithExpectedGlobals()
{
var script = CSharpScript.Create("X + Y").WithGlobalsType(typeof(Globals));
var result = script.Run(new Globals { X = 1, Y = 2 });
Assert.Equal(3, result.ReturnValue);
Assert.Same(script, result.Script);
}

[Fact]
public void TestRunCreatedScriptWithUnexpectedGlobals()
{
var script = CSharpScript.Create("X + Y");
var result = script.Run(new Globals { X = 1, Y = 2 });
Assert.Equal(3, result.ReturnValue);

// the end state of running the script should be based on a different script instance because of the globals
// not matching the original script definition.
Assert.NotSame(script, result.Script);
}

[Fact]
public void TestRunScriptWithScriptState()
{
// run a script using another scripts end state as the starting state (globals)
var result = CSharpScript.Run("int X = 100;");
var result2 = CSharpScript.Run("X + X", result);
Assert.Equal(200, result2.ReturnValue);
}

[Fact]
public void TestRepl()
{
string[] submissions = new[]
{
"int x = 100;",
"int y = x * x;",
"x + y"
};

object input = null;
ScriptState result = null;
foreach (var submission in submissions)
{
result = CSharpScript.Run(submission, input);
input = result;
}

Assert.Equal(10100, result.ReturnValue);
}

[Fact]
public void TestCreateMethodDelegate()
{
// create a delegate to a method declared in the script
var state = CSharpScript.Run("int Times(int x) { return x * x; }");
var fn = state.CreateDelegate<Func<int, int>>("Times");
var result = fn(5);
Assert.Equal(25, result);
}

[Fact]
public void TestGetScriptVariableAfterRunningScript()
{
var result = CSharpScript.Run("int x = 100;");
var globals = result.Variables.Names.ToList();
Assert.Equal(1, globals.Count);
Assert.Equal(true, globals.Contains("x"));
Assert.Equal(true, result.Variables.ContainsVariable("x"));
Assert.Equal(100, (int)result.Variables["x"].Value);
}

[Fact]
public void TestBranchingSubscripts()
{
// run script to create declaration of M
var result1 = CSharpScript.Run("int M(int x) { return x + x; }");

// run second script starting from first script's end state
// this script's new declaration should hide the old declaration
var result2 = CSharpScript.Run("int M(int x) { return x * x; } M(5)", result1);
Assert.Equal(25, result2.ReturnValue);

// run third script also starting from first script's end state
// it should not see any declarations made by the second script.
var result3 = CSharpScript.Run("M(5)", result1);
Assert.Equal(10, result3.ReturnValue);
}
}
}
6 changes: 6 additions & 0 deletions src/Scripting/CSharpTest/packages.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="StyleCop.MSBuild" version="4.7.48.2" targetFramework="net45" developmentDependency="true" />
<package id="System.Collections.Immutable" version="1.1.33-beta" targetFramework="net45" />
<package id="xunit" version="1.9.2" targetFramework="net45" />
</packages>
58 changes: 58 additions & 0 deletions src/Scripting/Core/AssemblyLoadResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;

namespace Roslyn.Scripting
{
/// <summary>
/// The result of loading an assembly reference to the interactive session.
/// </summary>
[Serializable]
public struct AssemblyLoadResult
{
private readonly string _path;
private readonly string _originalPath;
private readonly bool _successful;

internal static AssemblyLoadResult CreateSuccessful(string path, string originalPath)
{
return new AssemblyLoadResult(path, originalPath, successful: true);
}

internal static AssemblyLoadResult CreateAlreadyLoaded(string path, string originalPath)
{
return new AssemblyLoadResult(path, originalPath, successful: false);
}

private AssemblyLoadResult(string path, string originalPath, bool successful)
{
_path = path;
_originalPath = originalPath;
_successful = successful;
}

/// <summary>
/// True if the assembly was loaded by the assembly loader, false if has been loaded before.
/// </summary>
public bool IsSuccessful
{
get { return _successful; }
}

/// <summary>
/// Full path to the physical assembly file (might be a shadow-copy of the original assembly file).
/// </summary>
public string Path
{
get { return _path; }
}

/// <summary>
/// Original assembly file path.
/// </summary>
public string OriginalPath
{
get { return _originalPath; }
}
}
}
31 changes: 31 additions & 0 deletions src/Scripting/Core/AssemblyLoader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using Microsoft.CodeAnalysis;
using System.Reflection;

namespace Roslyn.Scripting
{
/// <summary>
/// Loads assemblies for Reflection based APIs.
/// </summary>
public abstract class AssemblyLoader
{
private sealed class _Default : AssemblyLoader
{
public override Assembly Load(AssemblyIdentity identity, string location = null)
{
return Assembly.Load(identity.ToAssemblyName());
}
}

internal static readonly AssemblyLoader Default = new _Default();

/// <summary>
/// Loads an assembly given its full name.
/// </summary>
/// <param name="identity">The identity of the assembly to load.</param>
/// <param name="location">Location of the assembly.</param>
/// <returns>The loaded assembly.</returns>
public abstract Assembly Load(AssemblyIdentity identity, string location = null);
}
}
142 changes: 142 additions & 0 deletions src/Scripting/Core/CommonScripting.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ImportGroup Label="Settings">
<Import Project="..\..\Tools\Microsoft.CodeAnalysis.Toolset.Open\Targets\VSL.Settings.targets" />
<Import Project="..\..\..\build\VSL.Settings.Closed.targets" />
</ImportGroup>
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{12A68549-4E8C-42D6-8703-A09335F97997}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>Roslyn.Scripting</RootNamespace>
<AssemblyName>Roslyn.Scripting.Common</AssemblyName>
<SolutionDir Condition="'$(SolutionDir)' == '' OR '$(SolutionDir)' == '*Undefined*'">..\..\..\</SolutionDir>
<RestorePackages>true</RestorePackages>
</PropertyGroup>
<ItemGroup Label="Linked Files">
<Compile Include="..\..\Compilers\Helpers\GlobalAssemblyCacheHelpers\FusionAssemblyIdentity.cs">
<Link>FusionAssemblyIdentity.cs</Link>
</Compile>
<Compile Include="..\..\Compilers\Helpers\GlobalAssemblyCacheHelpers\GlobalAssemblyCache.cs">
<Link>GlobalAssemblyCache.cs</Link>
</Compile>
<Compile Include="..\..\Compilers\Helpers\GlobalAssemblyCacheHelpers\GacFileResolver.cs">
<Link>GacFileResolver.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup Label="Project References">
<ProjectReference Include="..\..\Compilers\Core\Desktop\CodeAnalysis.Desktop.csproj">
<Project>{dfa21ca1-7f96-47ee-940c-069858e81727}</Project>
<Name>CodeAnalysis.Desktop</Name>
</ProjectReference>
<ProjectReference Include="..\..\Compilers\Core\Portable\CodeAnalysis.csproj">
<Project>{1EE8CAD3-55F9-4D91-96B2-084641DA9A6C}</Project>
<Name>CodeAnalysis</Name>
</ProjectReference>
</ItemGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|Any CPU' ">
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|Any CPU' ">
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|ARM' ">
<PlatformTarget>ARM</PlatformTarget>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|ARM' ">
<PlatformTarget>ARM</PlatformTarget>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'DogfoodDebug|AnyCPU'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'DogfoodDebug|ARM'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'DogfoodRelease|AnyCPU'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'DogfoodRelease|ARM'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefineConstants>TRACE;DEBUG;SCRIPTING</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefineConstants>TRACE;SCRIPTING</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Collections.Immutable, Version=1.1.33.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\packages\System.Collections.Immutable.1.1.33-beta\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll</HintPath>
</Reference>
<Reference Include="System.Core" />
<Reference Include="System.Reflection.Metadata, Version=1.0.18.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\packages\System.Reflection.Metadata.1.0.18-beta\lib\portable-net45+win8\System.Reflection.Metadata.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="AssemblyLoader.cs" />
<Compile Include="AssemblyLoadResult.cs" />
<Compile Include="ObjectFormatter.cs" />
<Compile Include="ObjectFormatter.Formatter.cs" />
<Compile Include="Script.cs" />
<Compile Include="ScriptBuilder.cs" />
<Compile Include="CommonScriptingResources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>CommonScriptingResources.resx</DependentUpon>
</Compile>
<Compile Include="CompilationErrorException.cs" />
<Compile Include="Emit\CommonCompilationExtensions.cs" />
<Compile Include="Emit\ReflectionEmitResult.cs" />
<Compile Include="Emit\ReflectionEmitter.cs" />
<Compile Include="Emit\ReflectionEmitter.Refs.cs" />
<Compile Include="InteractiveAssemblyLoader.cs" />
<Compile Include="MemberDisplayFormat.cs" />
<Compile Include="MetadataShadowCopy.cs" />
<Compile Include="MetadataShadowCopyProvider.cs" />
<Compile Include="ObjectFormattingOptions.cs" />
<Compile Include="ScriptExecutionState.cs" />
<Compile Include="ScriptVariables.cs" />
<Compile Include="ScriptOptions.cs" />
<Compile Include="ScriptState.cs" />
<Compile Include="ScriptVariable.cs" />
<Compile Include="ShadowCopy.cs" />
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="Roslyn.Scripting.CSharp" />
<InternalsVisibleTo Include="Roslyn.Scripting.VisualBasic" />
<InternalsVisibleTo Include="Roslyn.InteractiveFeatures" />
<InternalsVisibleTo Include="Roslyn.InteractiveEditorFeatures" />
<InternalsVisibleTo Include="Roslyn.VisualStudio.InteractiveServices" />
<InternalsVisibleTo Include="csi" />
<InternalsVisibleTo Include="vbi" />
<!-- GAC helpers -->
<InternalsVisibleToTest Include="Roslyn.Compilers.UnitTests" />
<InternalsVisibleToTest Include="Roslyn.Scripting.Common.UnitTests" />
<InternalsVisibleToTest Include="Roslyn.Scripting.CSharp.UnitTests" />
<InternalsVisibleToTest Include="Roslyn.Scripting.VisualBasic.UnitTests" />
<InternalsVisibleToTest Include="Roslyn.Test.Utilities" />
<InternalsVisibleToTest Include="RoslynTaoActions" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="CommonScriptingResources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>CommonScriptingResources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ImportGroup Label="Targets">
<Import Project="..\..\Tools\Microsoft.CodeAnalysis.Toolset.Open\Targets\VSL.Imports.targets" />
<Import Project="..\..\..\build\VSL.Imports.Closed.targets" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
</ImportGroup>
</Project>
162 changes: 162 additions & 0 deletions src/Scripting/Core/CommonScriptingResources.Designer.cs
153 changes: 153 additions & 0 deletions src/Scripting/Core/CommonScriptingResources.resx
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="StackOverflowWhileEvaluat" xml:space="preserve">
<value>!&lt;Stack overflow while evaluating object&gt;</value>
</data>
<data name="CantAssignTo" xml:space="preserve">
<value>Can't assign '{0}' to '{1}'.</value>
</data>
<data name="ExpectedAnAssemblyReference" xml:space="preserve">
<value>Expected an assembly reference.</value>
</data>
<data name="DisplayNameOrPathCannotBe" xml:space="preserve">
<value>Display name or path cannot be empty.</value>
</data>
<data name="AssemblyNotFound" xml:space="preserve">
<value>Assembly not found.</value>
</data>
<data name="AbsolutePathExpected" xml:space="preserve">
<value>Absolute path expected</value>
</data>
<data name="ExecutionStateFrozen" xml:space="preserve">
<value>Execution state is frozen and cannot be modified.</value>
</data>
<data name="GlobalsNotAssignable" xml:space="preserve">
<value>The globals of type '{0}' is not assignable to '{1}'</value>
</data>
<data name="StartingStateIncompatible" xml:space="preserve">
<value>Starting state was incompatible with script.</value>
</data>
<data name="InvalidAssemblyName" xml:space="preserve">
<value>Invalid assembly name</value>
</data>
<data name="InvalidCharactersInAssemblyName" xml:space="preserve">
<value>Invalid characters in assemblyName</value>
</data>
</root>
30 changes: 30 additions & 0 deletions src/Scripting/Core/CompilationErrorException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;

namespace Roslyn.Scripting
{
/// <summary>
/// An exception thrown when the compilation stage of interactive execution produces compilation errors.
/// </summary>
public class CompilationErrorException : Exception
{
private readonly ImmutableArray<Diagnostic> _diagnostics;

internal CompilationErrorException(string message, ImmutableArray<Diagnostic> diagnostics)
: base(message)
{
_diagnostics = diagnostics;
}

/// <summary>
/// The list of diagnostics produced by compilation.
/// </summary>
public ImmutableArray<Diagnostic> Diagnostics
{
get { return _diagnostics; }
}
}
}
174 changes: 174 additions & 0 deletions src/Scripting/Core/Emit/CommonCompilationExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using Microsoft.CodeAnalysis;
using System;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;
using Roslyn.Utilities;
using Cci = Microsoft.Cci;
using Microsoft.CodeAnalysis.Emit;

namespace Roslyn.Scripting.Emit
{
internal static class CommonCompilationExtensions
{
/// <summary>
/// Emits the compilation into given <see cref="ModuleBuilder"/> using Reflection.Emit APIs.
/// </summary>
/// <param name="compilation">Compilation.</param>
/// <param name="moduleBuilder">
/// The module builder to add the types into. Can be reused for multiple compilation units.
/// </param>
/// <param name="assemblyLoader">
/// Loads an assembly given an <see cref="AssemblyIdentity"/>.
/// This callback is used for loading assemblies referenced by the compilation.
/// <see cref="System.Reflection.Assembly.Load(AssemblyName)"/> is used if not specified.
/// </param>
/// <param name="assemblySymbolMapper">
/// Applied when converting assembly symbols to assembly references.
/// <see cref="IAssemblySymbol"/> is mapped to its <see cref="IAssemblySymbol.Identity"/> by default.
/// </param>
/// <param name="cancellationToken">Can be used to cancel the emit process.</param>
/// <param name="recoverOnError">If false the method returns an unsuccessful result instead of falling back to CCI writer.</param>
/// <param name="compiledAssemblyImage">Assembly image, returned only if we fallback to CCI writer.</param>
/// <param name="entryPoint">An entry point or null if not applicable or on failure.</param>
/// <param name="diagnostics">Diagnostics.</param>
/// <returns>True on success, false if a compilation error occurred or the compilation doesn't contain any code or declarations.</returns>
/// <remarks>
/// Reflection.Emit doesn't support all metadata constructs. If an unsupported construct is
/// encountered a metadata writer that procudes uncollectible code is used instead. This is
/// indicated by
/// <see cref="ReflectionEmitResult.IsUncollectible"/> flag on the result.
///
/// Reusing <see cref="System.Reflection.Emit.ModuleBuilder"/> may be beneficial in certain
/// scenarios. For example, when emitting a sequence of code snippets one at a time (like in
/// REPL). All the snippets can be compiled into a single module as long as the types being
/// emitted have unique names. Reusing a single module/assembly reduces memory overhead. On
/// the other hand, collectible assemblies are units of collection. Defining too many
/// unrelated types in a single assemly might prevent the unused types to be collected.
///
/// No need to provide a name override when using Reflection.Emit, since the assembly already
/// exists.
/// </remarks>
/// <exception cref="InvalidOperationException">Referenced assembly can't be resolved.</exception>
internal static bool Emit(
this Compilation compilation,
ModuleBuilder moduleBuilder,
AssemblyLoader assemblyLoader,
Func<IAssemblySymbol, AssemblyIdentity> assemblySymbolMapper,
bool recoverOnError,
DiagnosticBag diagnostics,
CancellationToken cancellationToken,
out MethodInfo entryPoint,
out byte[] compiledAssemblyImage)
{
compiledAssemblyImage = default(byte[]);

var moduleBeingBuilt = compilation.CreateModuleBuilder(
emitOptions: EmitOptions.Default,
manifestResources: null,
assemblySymbolMapper: assemblySymbolMapper,
testData: null,
diagnostics: diagnostics,
cancellationToken: cancellationToken);

if (moduleBeingBuilt == null)
{
entryPoint = null;
return false;
}

if (!compilation.Compile(
moduleBeingBuilt,
win32Resources: null,
xmlDocStream: null,
generateDebugInfo: false,
diagnostics: diagnostics,
filterOpt: null,
cancellationToken: cancellationToken))
{
entryPoint = null;
return false;
}

Cci.IMethodReference cciEntryPoint = moduleBeingBuilt.EntryPoint;

cancellationToken.ThrowIfCancellationRequested();

DiagnosticBag metadataDiagnostics = DiagnosticBag.GetInstance();

var context = new EmitContext((Cci.IModule)moduleBeingBuilt, null, metadataDiagnostics);

// try emit via Reflection.Emit
try
{
var referencedAssemblies = from referencedAssembly in compilation.GetBoundReferenceManager().GetReferencedAssemblies()
let peReference = referencedAssembly.Key as PortableExecutableReference
select KeyValuePair.Create(
moduleBeingBuilt.Translate(referencedAssembly.Value, metadataDiagnostics),
(peReference != null) ? peReference.FilePath : null);

entryPoint = ReflectionEmitter.Emit(
context,
referencedAssemblies,
moduleBuilder,
assemblyLoader ?? AssemblyLoader.Default,
cciEntryPoint,
cancellationToken);

// translate metadata errors.
return compilation.FilterAndAppendAndFreeDiagnostics(diagnostics, ref metadataDiagnostics);
}
catch (TypeLoadException)
{
// attempted to emit reference to a type that can't be loaded (has invalid metadata)
}
catch (NotSupportedException)
{
// nop
}

// TODO (tomat):
//
// Another possible approach would be to just return an error, that we can't emit via
// Ref.Emit and let the user choose another method of emitting. For that we would want
// to preserve the state of the Emit.Assembly object with all the compiled methods so
// that the subsequent emit doesn't need to compile method bodies again.

// TODO (tomat):
//
// If Ref.Emit fails to emit the code the type builders already created will stay
// defined on the module builder. Ideally we would clean them up but Ref.Emit doesn't
// provide any API to do so. In fact it also keeps baked TypeBuilders alive as well.

if (!recoverOnError)
{
metadataDiagnostics.Free();
entryPoint = null;
return false;
}

using (var stream = new System.IO.MemoryStream())
{
Cci.PeWriter.WritePeToStream(
context,
compilation.MessageProvider,
stream,
pdbWriterOpt: null,
allowMissingMethodBodies: false,
deterministic: false,
cancellationToken: cancellationToken);

compiledAssemblyImage = stream.ToArray();
}

var compiledAssembly = Assembly.Load(compiledAssemblyImage);
entryPoint = (cciEntryPoint != null) ? ReflectionEmitter.ResolveEntryPoint(compiledAssembly, cciEntryPoint, context) : null;

// translate metadata errors.
return compilation.FilterAndAppendAndFreeDiagnostics(diagnostics, ref metadataDiagnostics);
}
}
}
36 changes: 36 additions & 0 deletions src/Scripting/Core/Emit/ReflectionEmitResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections.Immutable;
using System.Reflection;

namespace Microsoft.CodeAnalysis.Emit
{
public class ReflectionEmitResult : EmitResult
{
private readonly MethodInfo _entryPoint;
private readonly bool _isUncollectible;

internal ReflectionEmitResult(MethodInfo entryPoint, bool success, bool isUncollectible, ImmutableArray<Diagnostic> diagnostics)
: base(success, diagnostics)
{
_entryPoint = entryPoint;
_isUncollectible = isUncollectible;
}

/// <summary>
/// Gets method information about the entrypoint of the emitted assembly.
/// </summary>
public MethodInfo EntryPoint
{
get { return _entryPoint; }
}

/// <summary>
/// Indicates whether the emitted assembly can be garbage collected.
/// </summary>
public bool IsUncollectible
{
get { return _isUncollectible; }
}
}
}
1,013 changes: 1,013 additions & 0 deletions src/Scripting/Core/Emit/ReflectionEmitter.Refs.cs

Large diffs are not rendered by default.

2,799 changes: 2,799 additions & 0 deletions src/Scripting/Core/Emit/ReflectionEmitter.cs

Large diffs are not rendered by default.

490 changes: 490 additions & 0 deletions src/Scripting/Core/InteractiveAssemblyLoader.cs

Large diffs are not rendered by default.

38 changes: 38 additions & 0 deletions src/Scripting/Core/MemberDisplayFormat.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

namespace Roslyn.Scripting
{
public enum MemberDisplayFormat
{
/// <summary>
/// Display just a simple description of the object, like type name or ToString(). Don't
/// display any members or items of the object.
/// </summary>
NoMembers,

/// <summary>
/// Display structure of the object on a single line.
/// </summary>
Inline,

/// <summary>
/// Display structure of the object on a single line, where the object is displayed as a value of its container's member.
/// E.g. { a = ... }
/// </summary>
InlineValue,

/// <summary>
/// Displays a siple description of the object followed by list of members. Each member is
/// displayed on a separate line.
/// </summary>
List,
}

internal static partial class EnumBounds
{
internal static bool IsValid(this MemberDisplayFormat value)
{
return value >= MemberDisplayFormat.NoMembers && value <= MemberDisplayFormat.List;
}
}
}
63 changes: 63 additions & 0 deletions src/Scripting/Core/MetadataShadowCopy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Diagnostics;
using Microsoft.CodeAnalysis.Text;
using Microsoft.Win32.SafeHandles;

namespace Microsoft.CodeAnalysis
{
/// <summary>
/// Represents a shadow copy of an assembly or a standalone module.
/// </summary>
public sealed class MetadataShadowCopy
{
private readonly ShadowCopy _primaryModule;
private readonly ShadowCopy _documentationFile;

// this instance doesn't own the image
private readonly Metadata _metadataCopy;

internal MetadataShadowCopy(ShadowCopy primaryModule, ShadowCopy documentationFile, Metadata metadataCopy)
{
Debug.Assert(primaryModule != null);
Debug.Assert(metadataCopy != null);
////Debug.Assert(!metadataCopy.IsImageOwner); property is now internal

_primaryModule = primaryModule;
_documentationFile = documentationFile;
_metadataCopy = metadataCopy;
}

/// <summary>
/// Assembly manifest module copy or a standalone module copy.
/// </summary>
public ShadowCopy PrimaryModule
{
get { return _primaryModule; }
}

/// <summary>
/// Documentation file copy or null if there is none.
/// </summary>
public ShadowCopy DocumentationFile
{
get { return _documentationFile; }
}

public Metadata Metadata
{
get { return _metadataCopy; }
}

// keep this internal so that users can't delete files that the provider manages
internal void DisposeFileHandles()
{
_primaryModule.DisposeFileStream();

if (_documentationFile != null)
{
_documentationFile.DisposeFileStream();
}
}
}
}
693 changes: 693 additions & 0 deletions src/Scripting/Core/MetadataShadowCopyProvider.cs

Large diffs are not rendered by default.

895 changes: 895 additions & 0 deletions src/Scripting/Core/ObjectFormatter.Formatter.cs

Large diffs are not rendered by default.

580 changes: 580 additions & 0 deletions src/Scripting/Core/ObjectFormatter.cs

Large diffs are not rendered by default.

72 changes: 72 additions & 0 deletions src/Scripting/Core/ObjectFormattingOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;

namespace Roslyn.Scripting
{
[Serializable]
public sealed class ObjectFormattingOptions
{
public static readonly ObjectFormattingOptions Default = new ObjectFormattingOptions();

public bool QuoteStrings { get; private set; }
public MemberDisplayFormat MemberFormat { get; private set; }
public int MaxLineLength { get; private set; }
public int MaxOutputLength { get; private set; }
public bool UseHexadecimalNumbers { get; private set; }
public string MemberIndentation { get; private set; }
public string Ellipsis { get; private set; }
public string NewLine { get; private set; }
public bool IncludeCodePoints { get; private set; }

public ObjectFormattingOptions(
MemberDisplayFormat memberFormat = MemberDisplayFormat.NoMembers,
bool quoteStrings = true,
bool useHexadecimalNumbers = false,
bool includeCodePoints = false,
int maxLineLength = Int32.MaxValue,
int maxOutputLength = Int32.MaxValue,
string memberIndentation = null,
string ellipsis = null,
string lineBreak = null)
{
if (!memberFormat.IsValid())
{
throw new ArgumentOutOfRangeException("memberFormat");
}

this.MemberFormat = memberFormat;
this.QuoteStrings = quoteStrings;
this.IncludeCodePoints = includeCodePoints;
this.MaxOutputLength = (maxOutputLength >= 0) ? maxOutputLength : int.MaxValue;
this.MaxLineLength = (maxLineLength >= 0) ? maxLineLength : int.MaxValue;
this.UseHexadecimalNumbers = useHexadecimalNumbers;
this.MemberIndentation = memberIndentation ?? " ";
this.Ellipsis = ellipsis ?? "...";
this.NewLine = lineBreak ?? Environment.NewLine;
}

public ObjectFormattingOptions Copy(
MemberDisplayFormat? memberFormat = null,
bool? quoteStrings = null,
bool? useHexadecimalNumbers = null,
bool? includeCodePoints = null,
int? maxLineLength = null,
int? maxOutputLength = null,
string memberIndentation = null,
string ellipsis = null,
string newLine = null)
{
return new ObjectFormattingOptions(
memberFormat ?? this.MemberFormat,
quoteStrings ?? this.QuoteStrings,
useHexadecimalNumbers ?? this.UseHexadecimalNumbers,
includeCodePoints ?? this.IncludeCodePoints,
maxLineLength ?? this.MaxLineLength,
maxOutputLength ?? this.MaxOutputLength,
memberIndentation ?? this.MemberIndentation,
ellipsis ?? this.Ellipsis,
newLine ?? this.NewLine);
}
}
}