/// <summary> /// Runs the emitted assembly. /// </summary> private CompilationEditor[] GetAssemblyChildren(Assembly assembly) { // Find the SelfEdit attribute on assembly string attrFullName = typeof(EditSelfAttribute).FullName; CustomAttributeData attrData = assembly.CustomAttributes.FirstOrDefault(x => x.AttributeType.FullName == attrFullName); if (attrData == null) { Report(Diagnostic.Create(LoadError, Location.None, "Could not find EditSelf attribute on compiled assembly.")); return(null); } // Construct its args var editorTypes = (IReadOnlyList <CustomAttributeTypedArgument>)attrData.ConstructorArguments[0].Value; CompilationEditor[] editors = new CompilationEditor[editorTypes.Count]; bool failed = false; for (int i = 0; i < editors.Length; i++) { if (Activator.CreateInstance(editorTypes[i].Value as Type, true) is CompilationEditor editor) { editors[i] = editor; continue; } Report(Diagnostic.Create(LoadError, Location.None, $"Could not create editor of type '{editorTypes[i].Value}'.")); failed = true; } return(failed ? null : editors); }
/// <summary> /// Registers a new <paramref name="edit"/> that is to be applied to any <see cref="CSharpSyntaxTree"/> /// found on the <see cref="CSharpCompilation"/>. /// </summary> /// <param name="editor"></param> /// <param name="edit"></param> public static void EditSyntaxTree(this CompilationEditor editor, SyntaxTreeEdit edit) { IList <SyntaxTreeEdit> GetDefaultValue() { editor.CompilationPipeline += EditCompilation; return(new LightList <SyntaxTreeEdit>()); } CSharpCompilation EditCompilation(CSharpCompilation compilation, CancellationToken cancellationToken) { var edits = editor.Storage.Get <IList <SyntaxTreeEdit> >(SyntaxTreeKey); for (int i = 0; i < compilation.SyntaxTrees.Length; i++) { CSharpSyntaxTree tree = compilation.SyntaxTrees[i] as CSharpSyntaxTree; CSharpSyntaxTree newTree = tree; if (tree == null) { continue; } foreach (var treeEdit in edits) { newTree = treeEdit(newTree, compilation, cancellationToken); if (newTree != null) { continue; } i--; compilation = compilation.RemoveSyntaxTrees(tree); goto NextTree; } compilation = compilation.ReplaceSyntaxTree(tree, newTree); NextTree :; } return(compilation); } editor.Storage.GetOrAdd(SyntaxTreeKey, GetDefaultValue).Add(edit); }
/// <summary> /// Adds the given <paramref name="component"/> to the IL emission pipeline. /// </summary> /// <param name="editor"></param> /// <param name="component"></param> public static void EditIL(this CompilationEditor editor, PipelineComponent <EmitDelegate> component) { Pipeline <EmitDelegate> GetDefaultValue() { EmitDelegate Emit(EmitDelegate next) { var pipeline = editor.Storage.Get <Pipeline <EmitDelegate> >(Key); var del = pipeline.MakeDelegate(next); return(del); } CodeGeneratorContext.EmitPipeline += Emit; //editor.EmissionStart += ce => CodeGeneratorContext.EmitPipeline += Emit; //editor.EmissionEnd += ce => CodeGeneratorContext.EmitPipeline -= Emit; return(new Pipeline <EmitDelegate>()); } editor.Storage.GetOrAdd(Key, GetDefaultValue).Add(component); }
/// <summary> /// Defines a feature on which the calling <see cref="CompilationEditor"/> is dependant. /// </summary> public static void DefineFeatureDependency(this CompilationEditor editor, string feature, string value = "True") { IDictionary <string, string> GetDefaultValue() { editor.GetRecomputationPipeline().Add(next => { return(opts => { if (editor.SharedStorage.TryGet(Key, out IDictionary <string, string> features)) { opts = opts.WithFeatures(opts.Features.Concat(features)); } return next(opts); }); }); return(new Dictionary <string, string>()); } editor.SharedStorage.GetOrAdd(Key, GetDefaultValue)[feature] = value; }
/// <summary> /// Defines a constant to be used as a preprocessor symbol name when parsing syntax trees. /// </summary> public static void DefineConstant(this CompilationEditor editor, string constant) { IList <string> GetDefaultValue() { editor.GetRecomputationPipeline().Add(next => { return(opts => { if (editor.SharedStorage.TryGet(Key, out IList <string> preprocessorSymbolNames)) { opts = opts.WithPreprocessorSymbols(opts.PreprocessorSymbolNames.Concat(preprocessorSymbolNames)); } return next(opts); }); }); return(new LightList <string>()); } editor.SharedStorage.GetOrAdd(Key, GetDefaultValue).Add(constant); }
/// <summary> /// Registers a new <paramref name="edit"/> that is to be applied to any <see cref="SyntaxNode"/> /// found on the <see cref="CSharpCompilation"/>. /// </summary> /// <param name="editor"></param> /// <param name="edit"></param> public static void EditSyntax(this CompilationEditor editor, SyntaxEdit edit) { IList <SyntaxEdit> GetDefaultValue() { editor.CompilationPipeline += EditCompilation; return(new LightList <SyntaxEdit>()); } CSharpCompilation EditCompilation(CSharpCompilation compilation, CancellationToken cancellationToken) { IList <SyntaxEdit> edits = editor.Storage.Get <IList <SyntaxEdit> >(SyntaxKey); SyntaxTreeRewriter syntaxRewriter = new SyntaxTreeRewriter(compilation, edits, cancellationToken); for (int i = 0; i < compilation.SyntaxTrees.Length; i++) { CSharpSyntaxTree tree = compilation.SyntaxTrees[i] as CSharpSyntaxTree; if (tree == null) { continue; } CSharpSyntaxNode root = tree.GetRoot(cancellationToken); SyntaxNode newRoot = syntaxRewriter.Visit(root); if (root != newRoot) { compilation = compilation.ReplaceSyntaxTree(tree, tree.WithRootAndOptions(newRoot, tree.Options)); } } return(compilation); } editor.Storage.GetOrAdd(SyntaxKey, GetDefaultValue).Add(edit); }
/// <summary> /// Returns a copy of the given <see cref="SyntaxTree"/>, with its options compatible /// with Cometary's required options. /// </summary> public static SyntaxTree WithCometaryOptions(this CSharpSyntaxTree syntaxTree, CompilationEditor editor) { return(syntaxTree.WithOptions(editor.GetRecomputationPipeline().MakeDelegate(opts => opts)(syntaxTree.Options))); }
/// <summary> /// Adds the given <paramref name="component"/> to the IL emission pipeline. /// </summary> /// <param name="editor"></param> /// <param name="component"></param> public static void EditIL(this CompilationEditor editor, AlternateEmitDelegate component) { editor.EditIL(CodeGeneratorContext.ToComponent(component)); }
/// <summary> /// Registers a new <paramref name="edit"/> that is to be applied to all operations /// that are about to be emitted. /// </summary> /// <param name="editor"></param> /// <param name="edit"></param> public static void EditOperation(this CompilationEditor editor, Edit <IOperation> edit) { editor.EditIL(next => (context, operation, used) => next(context, edit(operation, default(CancellationToken)), used)); }
internal static Pipeline <Func <CSharpParseOptions, CSharpParseOptions> > GetRecomputationPipeline(this CompilationEditor editor) { return(editor.SharedStorage.GetOrAdd(RecomputeKey, () => new Pipeline <Func <CSharpParseOptions, CSharpParseOptions> >())); }