PageRenderTime 45ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/Microsoft.Scripting/Runtime/ScriptCode.cs

https://bitbucket.org/stefanrusek/xronos
C# | 235 lines | 148 code | 41 blank | 46 comment | 9 complexity | 12189a2a58ef79c7762a1462fb4de13c MD5 | raw file
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Microsoft Public License. A
  6. * copy of the license can be found in the License.html file at the root of this distribution. If
  7. * you cannot locate the Microsoft Public License, please send an email to
  8. * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  9. * by the terms of the Microsoft Public License.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. #if CODEPLEX_40
  16. using System;
  17. #else
  18. using System; using Microsoft;
  19. #endif
  20. using System.Collections.Generic;
  21. using System.IO;
  22. #if CODEPLEX_40
  23. using System.Linq.Expressions;
  24. #else
  25. using Microsoft.Linq.Expressions;
  26. #endif
  27. using System.Reflection;
  28. using System.Reflection.Emit;
  29. using System.Threading;
  30. using Microsoft.Contracts;
  31. using Microsoft.Scripting.Generation;
  32. using Microsoft.Scripting.Runtime;
  33. using Microsoft.Scripting.Utils;
  34. using System.Diagnostics;
  35. namespace Microsoft.Scripting {
  36. /// <summary>
  37. /// ScriptCode is an instance of compiled code that is bound to a specific LanguageContext
  38. /// but not a specific ScriptScope. The code can be re-executed multiple times in different
  39. /// scopes. Hosting API counterpart for this class is <c>CompiledCode</c>.
  40. /// </summary>
  41. public abstract class ScriptCode {
  42. private readonly SourceUnit _sourceUnit;
  43. public ScriptCode(SourceUnit sourceUnit) {
  44. ContractUtils.RequiresNotNull(sourceUnit, "sourceUnit");
  45. _sourceUnit = sourceUnit;
  46. }
  47. public LanguageContext LanguageContext {
  48. get { return _sourceUnit.LanguageContext; }
  49. }
  50. public SourceUnit SourceUnit {
  51. get { return _sourceUnit; }
  52. }
  53. public virtual Scope CreateScope() {
  54. return new Scope();
  55. }
  56. public abstract object Run(Scope scope);
  57. public abstract object Run();
  58. class CodeInfo {
  59. public readonly MethodBuilder Builder;
  60. public readonly ScriptCode Code;
  61. public readonly Type DelegateType;
  62. public CodeInfo(MethodBuilder builder, ScriptCode code, Type delegateType) {
  63. Builder = builder;
  64. Code = code;
  65. DelegateType = delegateType;
  66. }
  67. }
  68. /// <summary>
  69. /// This takes an assembly name including extension and saves the provided ScriptCode objects into the assembly.
  70. ///
  71. /// The provided script codes can constitute code from multiple languages. The assemblyName can be either a fully qualified
  72. /// or a relative path. The DLR will simply save the assembly to the desired location. The assembly is created by the DLR and
  73. /// if a file already exists than an exception is raised.
  74. ///
  75. /// The DLR determines the internal format of the ScriptCode and the DLR can feel free to rev this as appropriate.
  76. /// </summary>
  77. public static void SaveToAssembly(string assemblyName, params ScriptCode[] codes) {
  78. ContractUtils.RequiresNotNull(assemblyName, "assemblyName");
  79. ContractUtils.RequiresNotNullItems(codes, "codes");
  80. // break the assemblyName into it's dir/name/extension
  81. string dir = Path.GetDirectoryName(assemblyName);
  82. if (String.IsNullOrEmpty(dir)) {
  83. dir = Environment.CurrentDirectory;
  84. }
  85. string name = Path.GetFileNameWithoutExtension(assemblyName);
  86. string ext = Path.GetExtension(assemblyName);
  87. // build the assembly & type gen that all the script codes will live in...
  88. AssemblyGen ag = new AssemblyGen(new AssemblyName(name), dir, ext, /*emitSymbols*/false);
  89. TypeBuilder tb = ag.DefinePublicType("DLRCachedCode", typeof(object), true);
  90. TypeGen tg = new TypeGen(ag, tb);
  91. var symbolDict = new Dictionary<SymbolId, FieldBuilder>();
  92. // then compile all of the code
  93. Dictionary<Type, List<CodeInfo>> langCtxBuilders = new Dictionary<Type, List<CodeInfo>>();
  94. foreach (ScriptCode sc in codes) {
  95. List<CodeInfo> builders;
  96. if (!langCtxBuilders.TryGetValue(sc.LanguageContext.GetType(), out builders)) {
  97. langCtxBuilders[sc.LanguageContext.GetType()] = builders = new List<CodeInfo>();
  98. }
  99. KeyValuePair<MethodBuilder, Type> compInfo = sc.CompileForSave(tg, symbolDict);
  100. builders.Add(new CodeInfo(compInfo.Key, sc, compInfo.Value));
  101. }
  102. MethodBuilder mb = tb.DefineMethod(
  103. "GetScriptCodeInfo",
  104. MethodAttributes.SpecialName | MethodAttributes.Public | MethodAttributes.Static,
  105. typeof(Tuple<Type[], Delegate[][], string[][], object>),
  106. Type.EmptyTypes);
  107. ILGen ilgen = new ILGen(mb.GetILGenerator());
  108. var langsWithBuilders = langCtxBuilders.ToArray();
  109. // lang ctx array
  110. ilgen.EmitArray(typeof(Type), langsWithBuilders.Length, (index) => {
  111. ilgen.Emit(OpCodes.Ldtoken, langsWithBuilders[index].Key);
  112. ilgen.EmitCall(typeof(Type).GetMethod("GetTypeFromHandle", new[] { typeof(RuntimeTypeHandle) }));
  113. });
  114. // builders array of array
  115. ilgen.EmitArray(typeof(Delegate[]), langsWithBuilders.Length, (index) => {
  116. List<CodeInfo> builders = langsWithBuilders[index].Value;
  117. ilgen.EmitArray(typeof(Delegate), builders.Count, (innerIndex) => {
  118. ilgen.EmitNull();
  119. ilgen.Emit(OpCodes.Ldftn, builders[innerIndex].Builder);
  120. ilgen.EmitNew(
  121. builders[innerIndex].DelegateType,
  122. new[] { typeof(object), typeof(IntPtr) }
  123. );
  124. });
  125. });
  126. // paths array of array
  127. ilgen.EmitArray(typeof(string[]), langsWithBuilders.Length, (index) => {
  128. List<CodeInfo> builders = langsWithBuilders[index].Value;
  129. ilgen.EmitArray(typeof(string), builders.Count, (innerIndex) => {
  130. ilgen.EmitString(builders[innerIndex].Code._sourceUnit.Path);
  131. });
  132. });
  133. // 4th element in tuple - always null...
  134. ilgen.EmitNull();
  135. ilgen.EmitNew(
  136. typeof(Tuple<Type[], Delegate[][], string[][], object>),
  137. new[] { typeof(Type[]), typeof(Delegate[][]), typeof(string[][]), typeof(object) }
  138. );
  139. ilgen.Emit(OpCodes.Ret);
  140. mb.SetCustomAttribute(new CustomAttributeBuilder(
  141. typeof(DlrCachedCodeAttribute).GetConstructor(Type.EmptyTypes),
  142. ArrayUtils.EmptyObjects
  143. ));
  144. tg.FinishType();
  145. ag.SaveAssembly();
  146. }
  147. /// <summary>
  148. /// This will take an assembly object which the user has loaded and return a new set of ScriptCode?s which have
  149. /// been loaded into the provided ScriptDomainManager.
  150. ///
  151. /// If the language associated with the ScriptCode?s has not already been loaded the DLR will load the
  152. /// LanguageContext into the ScriptDomainManager based upon the saved LanguageContext type.
  153. ///
  154. /// If the LanguageContext or the version of the DLR the language was compiled against is unavailable a
  155. /// TypeLoadException will be raised unless policy has been applied by the administrator to redirect bindings.
  156. /// </summary>
  157. public static ScriptCode[] LoadFromAssembly(ScriptDomainManager runtime, Assembly assembly) {
  158. ContractUtils.RequiresNotNull(runtime, "runtime");
  159. ContractUtils.RequiresNotNull(assembly, "assembly");
  160. // get the type which has our cached code...
  161. Type t = assembly.GetType("DLRCachedCode");
  162. if (t == null) {
  163. return new ScriptCode[0];
  164. }
  165. List<ScriptCode> codes = new List<ScriptCode>();
  166. MethodInfo mi = t.GetMethod("GetScriptCodeInfo");
  167. if (mi.IsSpecialName && mi.IsDefined(typeof(DlrCachedCodeAttribute), false)) {
  168. var infos = (Tuple<Type[], Delegate[][], string[][], object>)mi.Invoke(null, ArrayUtils.EmptyObjects);
  169. for (int i = 0; i < infos.Item000.Length; i++) {
  170. Type curType = infos.Item000[i];
  171. LanguageContext lc = runtime.GetLanguage(curType);
  172. Debug.Assert(infos.Item001[i].Length == infos.Item002[i].Length);
  173. Delegate[] methods = infos.Item001[i];
  174. string[] names = infos.Item002[i];
  175. for (int j = 0; j < methods.Length; j++) {
  176. codes.Add(lc.LoadCompiledCode(methods[j], names[j]));
  177. }
  178. }
  179. }
  180. return codes.ToArray();
  181. }
  182. protected LambdaExpression RewriteForSave(TypeGen typeGen, LambdaExpression code) {
  183. var diskRewriter = new ToDiskRewriter(typeGen);
  184. return diskRewriter.RewriteLambda(code);
  185. }
  186. protected virtual KeyValuePair<MethodBuilder, Type> CompileForSave(TypeGen typeGen, Dictionary<SymbolId, FieldBuilder> symbolDict) {
  187. throw new NotSupportedException();
  188. }
  189. [Confined]
  190. public override string ToString() {
  191. return String.Format("ScriptCode '{0}' from {1}", SourceUnit.Path, LanguageContext.GetType().Name);
  192. }
  193. }
  194. }