Beispiel #1
0
        /// <summary>
        /// Compiles <paramref name="code"/> and creates script.
        /// </summary>
        /// <param name="options">Compilation options.</param>
        /// <param name="code">Code to be compiled.</param>
        /// <param name="builder">Assembly builder.</param>
        /// <param name="previousSubmissions">Enumeration of scripts that were evaluated within current context. New submission may reference them.</param>
        /// <returns>New script reepresenting the compiled code.</returns>
        public static Script Create(Context.ScriptOptions options, string code, PhpCompilationFactory builder, IEnumerable <Script> previousSubmissions)
        {
            var tree = PhpSyntaxTree.ParseCode(code,
                                               new PhpParseOptions(kind: options.IsSubmission ? SourceCodeKind.Script : SourceCodeKind.Regular),
                                               PhpParseOptions.Default,
                                               options.Location.Path);


            var diagnostics = tree.Diagnostics;

            if (!HasErrors(diagnostics))
            {
                // unique in-memory assembly name
                var name = builder.GetNewSubmissionName();

                // list of scripts that were eval'ed in the context already,
                // our compilation may depend on them
                var dependingSubmissions = previousSubmissions.Where(s => !s.Image.IsDefaultOrEmpty);
                IEnumerable <MetadataReference> metadatareferences
                    = dependingSubmissions.Select(s => MetadataReference.CreateFromImage(s.Image));

                if (options.AdditionalReferences != null)
                {
                    // add additional script references
                    metadatareferences
                        = metadatareferences.Concat(options.AdditionalReferences.Select(r => MetadataReference.CreateFromFile(r)));
                }

                // create the compilation object
                // TODO: add conditionally declared types into the compilation tables
                var compilation = (PhpCompilation)builder.CoreCompilation
                                  .WithAssemblyName(name.Name)
                                  .AddSyntaxTrees(tree)
                                  .AddReferences(metadatareferences);

                if (options.EmitDebugInformation)
                {
                    compilation = compilation.WithPhpOptions(compilation.Options.WithOptimizationLevel(OptimizationLevel.Debug).WithDebugPlusMode(true));
                }

                diagnostics = compilation.GetDeclarationDiagnostics();
                if (!HasErrors(diagnostics))
                {
                    var peStream  = new MemoryStream();
                    var pdbStream = options.EmitDebugInformation ? new MemoryStream() : null;
                    var result    = compilation.Emit(peStream, pdbStream);
                    if (result.Success)
                    {
                        return(new Script(name, peStream, pdbStream, builder, previousSubmissions));
                    }
                    else
                    {
                        diagnostics = result.Diagnostics;
                    }
                }
            }

            //
            return(CreateInvalid(diagnostics));
        }
        Context.IScript Context.IScriptingProvider.CreateScript(Context.ScriptOptions options, string code)
        {
            var context = ScriptingContext.EnsureContext(options.Context);
            var script  = TryGetOrCreateScript(code, options, context);

            Debug.Assert(script != null);

            //
            context.Submissions.Add(script);

            //
            return(script);
        }
        Script CacheLookupNoLock(List <Script> candidates, Context.ScriptOptions options, string code, ScriptingContext context)
        {
            foreach (var c in candidates)
            {
                // candidate requires that all its dependencies were loaded into context
                // TODO: resolve the compiled code dependencies - referenced types and declared functions - instead of "DependingSubmissions"
                if (c.DependingSubmissions.All(context.Submissions.Contains))
                {
                    return(c);
                }
            }

            return(null);
        }
Beispiel #4
0
        Script CacheLookup(Context.ScriptOptions options, string code, ScriptingContext data)
        {
            if (_scripts.TryGetValue(code, out List <Script> candidates))
            {
                foreach (var c in candidates)
                {
                    // candidate requires that all its dependencies were loaded into context
                    if (c.DependingSubmissions.All(data.Submissions.Contains))
                    {
                        return(c);
                    }
                }
            }

            return(null);
        }
        Script /*!*/ TryGetOrCreateScript(string code, Context.ScriptOptions options, ScriptingContext context)
        {
            var script = default(Script);

            _scriptsLock.EnterUpgradeableReadLock();
            try
            {
                if (!_scripts.TryGetValue(code, out var subsmissions))
                {
                    _scriptsLock.EnterWriteLock();
                    try
                    {
                        if (!_scripts.TryGetValue(code, out subsmissions))
                        {
                            _scripts[code] = subsmissions = new List <Script>(1);
                        }
                    }
                    finally
                    {
                        _scriptsLock.ExitWriteLock();
                    }
                }

                if ((script = CacheLookupNoLock(subsmissions, options, code, context)) == null)
                {
                    _scriptsLock.EnterWriteLock();
                    try
                    {
                        if ((script = CacheLookupNoLock(subsmissions, options, code, context)) == null)
                        {
                            subsmissions.Add((script = Script.Create(options, code, _builder, context.Submissions)));
                        }
                    }
                    finally
                    {
                        _scriptsLock.ExitWriteLock();
                    }
                }
            }
            finally
            {
                _scriptsLock.ExitUpgradeableReadLock();
            }

            return(script);
        }
Beispiel #6
0
        Context.IScript Context.IScriptingProvider.CreateScript(Context.ScriptOptions options, string code)
        {
            var data   = ScriptingContext.EnsureContext(options.Context);
            var script = CacheLookup(options, code, data);

            if (script == null)
            {
                // TODO: rwlock cache[code]
                script = Script.Create(options, code, _builder, data.Submissions);
                EnsureCache(code).Add(script);
            }

            Debug.Assert(script != null);

            //
            data.Submissions.Add(script);

            //
            return(script);
        }
        /// <summary>
        /// Compiles <paramref name="code"/> and creates script.
        /// </summary>
        /// <param name="options">Compilation options.</param>
        /// <param name="code">Code to be compiled.</param>
        /// <param name="builder">Assembly builder.</param>
        /// <param name="previousSubmissions">Enumeration of scripts that were evaluated within current context. New submission may reference them.</param>
        /// <returns>New script reepresenting the compiled code.</returns>
        public static Script Create(Context.ScriptOptions options, string code, PhpCompilationFactory builder, IEnumerable <Script> previousSubmissions)
        {
            // use the language version of the requesting context
            Version languageVersion = null;
            bool    shortOpenTags   = false;

            var language = options.Context.TargetPhpLanguage;

            if (language != null)
            {
                shortOpenTags = language.ShortOpenTag;
                Version.TryParse(language.LanguageVersion, out languageVersion);
            }

            // parse the source code
            var tree = PhpSyntaxTree.ParseCode(
                SourceText.From(code, Encoding.UTF8),
                new PhpParseOptions(
                    kind: options.IsSubmission ? SourceCodeKind.Script : SourceCodeKind.Regular,
                    languageVersion: languageVersion,
                    shortOpenTags: shortOpenTags),
                PhpParseOptions.Default,
                options.Location.Path);

            var diagnostics = tree.Diagnostics;

            if (!HasErrors(diagnostics))
            {
                // TODO: collect required types from {tree}, remember as a script dependencies
                // TODO: perform class autoload (now before compilation, and then always before invocation)

                // unique in-memory assembly name
                var name = builder.GetNewSubmissionName();

                // list of scripts that were eval'ed in the context already,
                // our compilation may depend on them
                var dependingSubmissions = previousSubmissions.Where(s => !s.Image.IsDefaultOrEmpty);
                IEnumerable <MetadataReference> metadatareferences
                    = dependingSubmissions.Select(s => MetadataReference.CreateFromImage(s.Image));

                if (options.AdditionalReferences != null)
                {
                    // add additional script references
                    metadatareferences
                        = metadatareferences.Concat(options.AdditionalReferences.Select(r => MetadataReference.CreateFromFile(r)));
                }

                // create the compilation object
                // TODO: add conditionally declared types into the compilation tables
                var compilation = (PhpCompilation)builder.CoreCompilation
                                  .WithAssemblyName(name.Name)
                                  .AddSyntaxTrees(tree)
                                  .AddReferences(metadatareferences);

                var emitOptions   = new EmitOptions();
                var embeddedTexts = default(IEnumerable <EmbeddedText>);

                if (options.EmitDebugInformation)
                {
                    compilation = compilation.WithPhpOptions(compilation.Options.WithOptimizationLevel(OptimizationLevel.Debug).WithDebugPlusMode(true));
                    //emitOptions = emitOptions.WithDebugInformationFormat(DebugInformationFormat.PortablePdb);
                    //embeddedTexts = new[] { EmbeddedText.FromSource(tree.FilePath, tree.GetText()) };
                }
                else
                {
                    compilation = compilation.WithPhpOptions(compilation.Options.WithOptimizationLevel(OptimizationLevel.Release));
                }

                diagnostics = compilation.GetDeclarationDiagnostics();
                if (!HasErrors(diagnostics))
                {
                    var peStream  = new MemoryStream();
                    var pdbStream = options.EmitDebugInformation ? new MemoryStream() : null;

                    var result = compilation.Emit(peStream,
                                                  pdbStream: pdbStream,
                                                  options: emitOptions,
                                                  embeddedTexts: embeddedTexts
                                                  );

                    if (result.Success)
                    {
                        return(new Script(name, peStream, pdbStream, builder, previousSubmissions));
                    }
                    else
                    {
                        diagnostics = result.Diagnostics;
                    }
                }
            }

            //
            return(CreateInvalid(diagnostics));
        }
        /// <summary>
        /// Compiles <paramref name="code"/> and creates script.
        /// </summary>
        /// <param name="options">Compilation options.</param>
        /// <param name="code">Code to be compiled.</param>
        /// <param name="builder">Assembly builder.</param>
        /// <param name="previousSubmissions">Enumeration of scripts that were evaluated within current context. New submission may reference them.</param>
        /// <returns>New script reepresenting the compiled code.</returns>
        public static Script Create(Context.ScriptOptions options, string code, PhpCompilationFactory builder, IEnumerable <Script> previousSubmissions)
        {
            // use the language version of the requesting context
            var  languageVersion = options.LanguageVersion;
            bool shortOpenTags   = false;

            var language = options.Context.TargetPhpLanguage;

            if (language != null)
            {
                shortOpenTags = language.ShortOpenTag;
                languageVersion ??= language.LanguageVersion;
            }

            // unique in-memory assembly name
            var name = builder.GetNewSubmissionName();

            // submission do not have the opening "<?php" script tag:
            var kind = options.IsSubmission ? SourceCodeKind.Script : SourceCodeKind.Regular;

            if (kind == SourceCodeKind.Script && options.EmitDebugInformation)
            {
                // since submission do not have the opening "<?php" tag,
                // add a comment with the opening tag, so source code editors don't get confused and colorize the code properly:
                code = $"#<?php\n{code}";
            }

            // parse the source code
            var tree = PhpSyntaxTree.ParseCode(
                SourceText.From(code, Encoding.UTF8, SourceHashAlgorithm.Sha256),
                new PhpParseOptions(
                    kind: kind,
                    languageVersion: languageVersion,
                    shortOpenTags: shortOpenTags),
                PhpParseOptions.Default,
                options.IsSubmission ? BuildSubmissionFileName(options.Location.Path, name.Name) : options.Location.Path
                );

            var diagnostics = tree.Diagnostics;

            if (!HasErrors(diagnostics))
            {
                // TODO: collect required types from {tree}, remember as a script dependencies
                // TODO: perform class autoload (now before compilation, and then always before invocation)

                // list of scripts that were eval'ed in the context already,
                // our compilation may depend on them
                var dependingSubmissions = previousSubmissions.Where(s => !s.Image.IsDefaultOrEmpty);
                IEnumerable <MetadataReference> metadatareferences
                    = dependingSubmissions.Select(s => MetadataReference.CreateFromImage(s.Image));

                if (options.AdditionalReferences != null)
                {
                    // add additional script references
                    metadatareferences
                        = metadatareferences.Concat(options.AdditionalReferences.Select(r => MetadataReference.CreateFromFile(r)));
                }

                // create the compilation object
                // TODO: add conditionally declared types into the compilation tables
                var compilation = (PhpCompilation)builder.CoreCompilation
                                  .WithLangVersion(languageVersion)
                                  .WithAssemblyName(name.Name)
                                  .AddSyntaxTrees(tree)
                                  .AddReferences(metadatareferences);

                var emitOptions   = new EmitOptions();
                var embeddedTexts = default(IEnumerable <EmbeddedText>);

                if (options.EmitDebugInformation)
                {
                    compilation = compilation.WithPhpOptions(compilation.Options.WithOptimizationLevel(OptimizationLevel.Debug).WithDebugPlusMode(true));

                    if (options.IsSubmission)
                    {
                        emitOptions   = emitOptions.WithDebugInformationFormat(DebugInformationFormat.PortablePdb);
                        embeddedTexts = new[] { EmbeddedText.FromSource(tree.FilePath, tree.GetText()) };
                    }
                }
                else
                {
                    compilation = compilation.WithPhpOptions(compilation.Options.WithOptimizationLevel(OptimizationLevel.Release));
                }

                diagnostics = compilation.GetDeclarationDiagnostics();
                if (!HasErrors(diagnostics))
                {
                    var peStream  = new MemoryStream();
                    var pdbStream = options.EmitDebugInformation ? new MemoryStream() : null;

                    var result = compilation.Emit(peStream,
                                                  pdbStream: pdbStream,
                                                  options: emitOptions,
                                                  embeddedTexts: embeddedTexts
                                                  );

                    if (result.Success)
                    {
                        //if (pdbStream != null)
                        //{
                        //    // DEBUG DUMP
                        //    var fname = @"C:\Users\jmise\OneDrive\Desktop\" + Path.GetFileNameWithoutExtension(tree.FilePath);
                        //    File.WriteAllBytes(fname + ".dll", peStream.ToArray());
                        //    File.WriteAllBytes(fname + ".pdb", pdbStream.ToArray());
                        //}

                        return(new Script(name, peStream, pdbStream, builder, previousSubmissions));
                    }
                    else
                    {
                        diagnostics = result.Diagnostics;
                    }
                }
            }

            //
            return(CreateInvalid(diagnostics));
        }