コード例 #1
0
ファイル: ScriptCompiler.cs プロジェクト: her123/ApsimX
        /// <summary>Compile a c# script.</summary>
        /// <param name="code">The c# code to compile.</param>
        /// <param name="model">The model owning the script.</param>
        /// <param name="referencedAssemblies">Optional referenced assemblies.</param>
        /// <returns>Compile errors or null if no errors.</returns>
        public Results Compile(string code, IModel model, IEnumerable <string> referencedAssemblies = null)
        {
            string errors = null;

            if (code != null)
            {
                // See if we have compiled the code already. If so then no need to compile again.
                var compilation = previousCompilations.Find(c => c.Code == code);

                bool newlyCompiled;
                if (compilation == null || compilation.Code != code)
                {
                    newlyCompiled = true;

                    var assemblies = GetReferenceAssemblies(referencedAssemblies, model.Name);

                    // We haven't compiled the code so do it now.
                    var result = CompileTextToAssembly(code, assemblies);
                    if (result.Errors.Count > 0)
                    {
                        // Errors were found. Add then to the return error string.
                        errors = null;
                        foreach (CompilerError err in result.Errors)
                        {
                            errors += $"Line {err.Line}: {err.ErrorText}{Environment.NewLine}";
                        }

                        // Because we have errors, remove the previous compilation if there is one.
                        if (compilation != null)
                        {
                            previousCompilations.Remove(compilation);
                        }
                        compilation = null;
                    }
                    else
                    {
                        // No errors.
                        // If we don't have a previous compilation, create one.
                        if (compilation == null)
                        {
                            compilation = new PreviousCompilation()
                            {
                                ModelFullPath = model.FullPath
                            };
                            previousCompilations.Add(compilation);
                        }

                        // Set the compilation properties.
                        compilation.Code             = code;
                        compilation.CompiledAssembly = result.CompiledAssembly;
                    }
                }
                else
                {
                    newlyCompiled = false;
                }

                if (compilation != null)
                {
                    // We have a compiled assembly so get the class name.
                    var regEx = new Regex(@"class\s+(\w+)\s");
                    var match = regEx.Match(code);
                    if (!match.Success)
                    {
                        throw new Exception($"Cannot find a class declaration in script:{Environment.NewLine}{code}");
                    }
                    var className = match.Groups[1].Value;

                    // Create an instance of the class and give it to the model.
                    var instanceType = compilation.CompiledAssembly.GetTypes().ToList().Find(t => t.Name == className);
                    return(new Results(compilation.CompiledAssembly, instanceType.FullName, newlyCompiled));
                }
                else
                {
                    return(new Results(errors));
                }
            }

            return(null);
        }
コード例 #2
0
ファイル: ScriptCompiler.cs プロジェクト: her123/ApsimX
        /// <summary>Compile a c# script.</summary>
        /// <param name="code">The c# code to compile.</param>
        /// <param name="model">The model owning the script.</param>
        /// <param name="referencedAssemblies">Optional referenced assemblies.</param>
        /// <returns>Compile errors or null if no errors.</returns>
        public Results Compile(string code, IModel model, IEnumerable <MetadataReference> referencedAssemblies = null)
        {
            string errors = null;

            if (code != null)
            {
                // See if we have compiled the code already. If so then no need to compile again.
                PreviousCompilation compilation = previousCompilations.Find(c => c.Code == code);

                bool newlyCompiled;
                if (compilation == null || compilation.Code != code)
                {
                    newlyCompiled = true;
                    bool withDebug = System.Diagnostics.Debugger.IsAttached;

                    IEnumerable <MetadataReference> assemblies = GetReferenceAssemblies(referencedAssemblies, model.Name);

                    // We haven't compiled the code so do it now.
                    string      sourceName;
                    Compilation compiled = CompileTextToAssembly(code, assemblies, out sourceName);

                    List <EmbeddedText> embeddedTexts = null;
                    if (withDebug)
                    {
                        System.Text.Encoding encoding = System.Text.Encoding.UTF8;

                        byte[]     buffer     = encoding.GetBytes(code);
                        SourceText sourceText = SourceText.From(buffer, buffer.Length, encoding, canBeEmbedded: true);
                        embeddedTexts = new List <EmbeddedText>
                        {
                            EmbeddedText.FromSource(sourceName, sourceText),
                        };
                    }

                    MemoryStream ms        = new MemoryStream();
                    MemoryStream pdbStream = new MemoryStream();
                    {
                        EmitResult emitResult = compiled.Emit(
                            peStream: ms,
                            pdbStream: withDebug ? pdbStream : null,
                            embeddedTexts: embeddedTexts
                            );
                        if (!emitResult.Success)
                        {
                            // Errors were found. Add then to the return error string.
                            errors = null;
                            foreach (Diagnostic diag in emitResult.Diagnostics)
                            {
                                if (diag.Severity == DiagnosticSeverity.Error)
                                {
                                    errors += $"{diag.ToString()}{Environment.NewLine}";
                                }
                            }

                            // Because we have errors, remove the previous compilation if there is one.
                            if (compilation != null)
                            {
                                previousCompilations.Remove(compilation);
                            }
                            compilation = null;
                        }
                        else
                        {
                            // No errors.
                            // If we don't have a previous compilation, create one.
                            if (compilation == null)
                            {
                                compilation = new PreviousCompilation()
                                {
                                    ModelFullPath = model.FullPath
                                };
                                previousCompilations.Add(compilation);
                            }

                            // Write the assembly to disk
                            ms.Seek(0, SeekOrigin.Begin);
                            string fileName = Path.Combine(Path.GetTempPath(), compiled.AssemblyName + ".dll");
                            using (FileStream file = new FileStream(fileName, FileMode.Create, FileAccess.Write))
                                ms.WriteTo(file);

                            // Set the compilation properties.
                            ms.Seek(0, SeekOrigin.Begin);
                            pdbStream.Seek(0, SeekOrigin.Begin);
                            compilation.Code             = code;
                            compilation.Reference        = compiled.ToMetadataReference();
                            compilation.CompiledAssembly = System.Runtime.Loader.AssemblyLoadContext.Default.LoadFromStream(ms, pdbStream);
                        }
                    }
                }
                else
                {
                    newlyCompiled = false;
                }

                if (compilation != null)
                {
                    // We have a compiled assembly so get the class name.
                    var regEx = new Regex(@"class\s+(\w+)\s");
                    var match = regEx.Match(code);
                    if (!match.Success)
                    {
                        throw new Exception($"Cannot find a class declaration in script:{Environment.NewLine}{code}");
                    }
                    var className = match.Groups[1].Value;

                    // Create an instance of the class and give it to the model.
                    var instanceType = compilation.CompiledAssembly.GetTypes().ToList().Find(t => t.Name == className);
                    return(new Results(compilation.CompiledAssembly, instanceType.FullName, newlyCompiled));
                }
                else
                {
                    return(new Results(errors));
                }
            }

            return(null);
        }