Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/monodevelop.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatt Ward <matt.ward@xamarin.com>2015-11-24 14:35:58 +0300
committerMatt Ward <matt.ward@xamarin.com>2015-11-24 15:15:11 +0300
commit174f1335283a16918b1cfcac1a5f5594122d3464 (patch)
tree8cb9028820f973fc591a6bdfeb5f735627fec67d /main/src/addins/TextTemplating
parentc06c4c2a5e30aede8c115e094a7f93d18ed88672 (diff)
[T4] Fix null ref when using parameterized T4 templates.
This also fixes bug #36093 - [ASP.NET Project Wizard] Global.asax could not be written. https://bugzilla.xamarin.com/show_bug.cgi?id=36093 The simplest way to reproduce the exception is to create a T4 template with the content shown below and then try to save the T4 template: <#@ template language="C#" #> <#@ parameter type="System.Int32" name="TimesToRepeat" #> The TemplatingEngine.GenerateIndentedClassCode uses reflection when running on Mono to initialize internal members of the CodeGenerator class since the implementation of the CSharpCodeProvider is incomplete on Mono so the CodeDomProvider CreateCodeFromMember method cannot be called. The CodeGenerator in Mono 4.2 has changed and no longer has an InitOutput method that the TemplatingEngine was using to initialize the internal members. This missing method was causing a null reference exception. Now if the InitOutput method is missing the TemplatingEngine will instead directly initialize the internal fields of the CodeGenerator class. The CSharpCodeProvider's GenerateCodeFromMember is still not implemented with Mono 4.2 so reflection is still required. Also added a unit test for the GenerateIndentedClassCode method.
Diffstat (limited to 'main/src/addins/TextTemplating')
-rw-r--r--main/src/addins/TextTemplating/Mono.TextTemplating.Tests/GenerateIndentedClassCodeTests.cs108
-rw-r--r--main/src/addins/TextTemplating/Mono.TextTemplating.Tests/Mono.TextTemplating.Tests.csproj1
-rw-r--r--main/src/addins/TextTemplating/Mono.TextTemplating/Mono.TextTemplating/TemplatingEngine.cs29
3 files changed, 135 insertions, 3 deletions
diff --git a/main/src/addins/TextTemplating/Mono.TextTemplating.Tests/GenerateIndentedClassCodeTests.cs b/main/src/addins/TextTemplating/Mono.TextTemplating.Tests/GenerateIndentedClassCodeTests.cs
new file mode 100644
index 0000000000..0810e2e783
--- /dev/null
+++ b/main/src/addins/TextTemplating/Mono.TextTemplating.Tests/GenerateIndentedClassCodeTests.cs
@@ -0,0 +1,108 @@
+//
+// GenerateIndentedClassCodeTests.cs
+//
+// Author:
+// Matt Ward <matt.ward@xamarin.com>
+//
+// Copyright (c) 2015 Xamarin Inc. (http://xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.CodeDom;
+using System.CodeDom.Compiler;
+using NUnit.Framework;
+using System.IO;
+
+namespace Mono.TextTemplating.Tests
+{
+ [TestFixture]
+ public class GenerateIndentedClassCodeTests
+ {
+ [Test]
+ public void FieldAndPropertyGenerated ()
+ {
+ var provider = CodeDomProvider.CreateProvider ("C#");
+ var field = CreateBoolField ();
+ var property = CreateBoolProperty ();
+
+ string output = TemplatingEngine.GenerateIndentedClassCode (provider, field, property);
+ output = FixOutput (output);
+ string expectedOutput = FixOutput (MethodAndFieldGeneratedOutput);
+
+ Assert.AreEqual (expectedOutput, output);
+ }
+
+ static CodeTypeMember CreateVoidMethod ()
+ {
+ var meth = new CodeMemberMethod { Name = "MyMethod" };
+ meth.ReturnType = new CodeTypeReference (typeof(void));
+ return meth;
+ }
+
+ static CodeTypeMember CreateBoolField ()
+ {
+ var type = new CodeTypeReference (typeof(bool));
+ return new CodeMemberField { Name = "myField", Type = type };
+ }
+
+ static CodeTypeMember CreateBoolProperty ()
+ {
+ var type = new CodeTypeReference (typeof(bool));
+ var prop = new CodeMemberProperty { Name = "MyProperty", Type = type };
+ prop.GetStatements.Add (
+ new CodeMethodReturnStatement (
+ new CodePrimitiveExpression (true)
+ )
+ );
+ return prop;
+ }
+
+ /// <summary>
+ /// Remove empty lines which are not generated on Mono.
+ /// </summary>
+ static string FixOutput (string output, string newLine = "\n")
+ {
+ using (var writer = new StringWriter ()) {
+ using (var reader = new StringReader (output)) {
+
+ string line;
+ while ((line = reader.ReadLine ()) != null) {
+ if (!String.IsNullOrWhiteSpace (line)) {
+ writer.Write (line);
+ writer.Write (newLine);
+ }
+ }
+ }
+ return writer.ToString ();
+ }
+ }
+
+ public static string MethodAndFieldGeneratedOutput =
+@"
+ private bool myField;
+
+ private bool MyProperty {
+ get {
+ return true;
+ }
+ }
+";
+ }
+}
diff --git a/main/src/addins/TextTemplating/Mono.TextTemplating.Tests/Mono.TextTemplating.Tests.csproj b/main/src/addins/TextTemplating/Mono.TextTemplating.Tests/Mono.TextTemplating.Tests.csproj
index 3b67d5045d..fec16c1aac 100644
--- a/main/src/addins/TextTemplating/Mono.TextTemplating.Tests/Mono.TextTemplating.Tests.csproj
+++ b/main/src/addins/TextTemplating/Mono.TextTemplating.Tests/Mono.TextTemplating.Tests.csproj
@@ -41,6 +41,7 @@
<Compile Include="GenerationTests.cs" />
<Compile Include="TemplatingEngineHelper.cs" />
<Compile Include="TemplateEnginePreprocessTemplateTests.cs" />
+ <Compile Include="GenerateIndentedClassCodeTests.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Mono.TextTemplating\Mono.TextTemplating.csproj">
diff --git a/main/src/addins/TextTemplating/Mono.TextTemplating/Mono.TextTemplating/TemplatingEngine.cs b/main/src/addins/TextTemplating/Mono.TextTemplating/Mono.TextTemplating/TemplatingEngine.cs
index 8a312101d2..9425f6dae8 100644
--- a/main/src/addins/TextTemplating/Mono.TextTemplating/Mono.TextTemplating/TemplatingEngine.cs
+++ b/main/src/addins/TextTemplating/Mono.TextTemplating/Mono.TextTemplating/TemplatingEngine.cs
@@ -1091,7 +1091,7 @@ namespace Mono.TextTemplating
}
var cgType = typeof (CodeGenerator);
- var cgInit = cgType.GetMethod ("InitOutput", BindingFlags.NonPublic | BindingFlags.Instance);
+ var initializeCodeGenerator = GetInitializeCodeGeneratorAction (cgType);
var cgFieldGen = cgType.GetMethod ("GenerateField", BindingFlags.NonPublic | BindingFlags.Instance);
var cgPropGen = cgType.GetMethod ("GenerateProperty", BindingFlags.NonPublic | BindingFlags.Instance);
@@ -1103,19 +1103,42 @@ namespace Mono.TextTemplating
foreach (CodeTypeMember member in members) {
var f = member as CodeMemberField;
if (f != null) {
- cgInit.Invoke (generator, new object[] { sw, options });
+ initializeCodeGenerator (generator, sw, options);
cgFieldGen.Invoke (generator, new object[] { f });
continue;
}
var p = member as CodeMemberProperty;
if (p != null) {
- cgInit.Invoke (generator, new object[] { sw, options });
+ initializeCodeGenerator (generator, sw, options);
cgPropGen.Invoke (generator, new object[] { p, dummy });
continue;
}
}
}
+ static Action<CodeGenerator, StringWriter, CodeGeneratorOptions> GetInitializeCodeGeneratorAction (Type cgType)
+ {
+ var cgInit = cgType.GetMethod ("InitOutput", BindingFlags.NonPublic | BindingFlags.Instance);
+ if (cgInit != null) {
+ return new Action<CodeGenerator, StringWriter, CodeGeneratorOptions> ((generator, sw, options) => {
+ cgInit.Invoke (generator, new object[] { sw, options });
+ });
+ }
+
+ var cgOptions = cgType.GetField ("options", BindingFlags.NonPublic | BindingFlags.Instance);
+ var cgOutput = cgType.GetField ("output", BindingFlags.NonPublic | BindingFlags.Instance);
+
+ if (cgOptions != null && cgOutput != null) {
+ return new Action<CodeGenerator, StringWriter, CodeGeneratorOptions> ((generator, sw, options) => {
+ var output = new IndentedTextWriter (sw);
+ cgOptions.SetValue (generator, options);
+ cgOutput.SetValue (generator, output);
+ });
+ }
+
+ throw new InvalidOperationException ("Unable to initialize CodeGenerator.");
+ }
+
public static string GenerateIndentedClassCode (CodeDomProvider provider, params CodeTypeMember[] members)
{
return GenerateIndentedClassCode (provider, (IEnumerable<CodeTypeMember>)members);