/// <summary> /// Creates a compilation context. /// </summary> /// <param name="applicationContext">Application context.</param> /// <param name="manager">Manager.</param> /// <param name="config">Configuration.</param> /// <param name="errorSink">Error sink.</param> /// <param name="workingDirectory">Working directory.</param> internal CompilationContext(ApplicationContext /*!*/ applicationContext, ICompilerManager manager, CompilerConfiguration /*!*/ config, ErrorSink /*!*/ errorSink, string /*!*/ workingDirectory) { Debug.Assert(applicationContext != null); Debug.Assert(config != null && workingDirectory != null); this.applicationContext = applicationContext; this.manager = manager; this.config = config; this.errors = errorSink; this.workingDirectory = workingDirectory; }
/// <summary> /// Creates a compilation context. /// </summary> /// <param name="applicationContext">Application context.</param> /// <param name="manager">Manager.</param> /// <param name="config">Configuration.</param> /// <param name="errorSink">Error sink.</param> /// <param name="workingDirectory">Working directory.</param> internal CompilationContext(ApplicationContext/*!*/ applicationContext, ICompilerManager manager, CompilerConfiguration/*!*/ config, ErrorSink/*!*/ errorSink, string/*!*/ workingDirectory) { Debug.Assert(applicationContext != null); Debug.Assert(config != null && workingDirectory != null); this.applicationContext = applicationContext; this.manager = manager; this.config = config; this.errors = errorSink; this.workingDirectory = workingDirectory; }
/// <summary> /// Translates a source path to an assembly coded name. /// </summary> /// <param name="sourceFile">Source file.</param> /// <param name="config">The compiler configuration.</param> /// <returns> /// The code name consisting of significant configuration hashcode and source /// path relative to the application source root. /// Format of the name: <code>{relativized path}(~{level_count})?#{config hash}#</code> /// Backslashes and colons are replaced with underscores, underscores are doubled. /// </returns> public static string GetAssemblyCodedName(PhpSourceFile/*!*/ sourceFile, CompilerConfiguration/*!*/ config) { RelativePath rp = sourceFile.RelativePath; StringBuilder sb = new StringBuilder(rp.Path); if (rp.Level >= 0) { sb.Append('~'); sb.Append(rp.Level); } sb.Append('#'); sb.Append(config.Compiler.HashCode.ToString("x")); sb.Append('#'); return sb.Replace("_", "__").Replace('/', '_').Replace('\\', '_').Replace(':', '_').ToString(); }
public void ApplyToConfiguration(CompilerConfiguration/*!*/ compilerConfig) { if (compilerConfig == null) throw new ArgumentNullException("compilerConfig"); if (languageFeatures.HasValue) { compilerConfig.Compiler.LanguageFeatures = languageFeatures.Value; } else { // sets the default language features for pure mode if not set yet: if (pure && !compilerConfig.Compiler.LanguageFeaturesSet) compilerConfig.Compiler.LanguageFeatures = Core.LanguageFeatures.PureModeDefault; } // cmd line: if (debuggable.HasValue) compilerConfig.Compiler.Debug = (bool)debuggable; if (staticInclusions.HasValue) compilerConfig.Compiler.EnableStaticInclusions = staticInclusions; // paths skipped during compilation are also forced dynamic inclusion paths (otherwise static inclusion could force compilation of these sources files) foreach (string path in skipPaths) { compilerConfig.Compiler.ForcedDynamicInclusionPaths.Add(path); } // static inclusion will be enabled for non-web applications if not set: if (!compilerConfig.Compiler.EnableStaticInclusions.HasValue && target != Targets.Web) compilerConfig.Compiler.EnableStaticInclusions = true; if (encoding != null) compilerConfig.Globalization.PageEncoding = encoding; // enable all warnings in pure mode by default: if (pure) compilerConfig.Compiler.DisabledWarnings = WarningGroups.None; // disableWarnings and enableWarnings sets are disjoint: compilerConfig.Compiler.DisabledWarnings |= disableWarnings; compilerConfig.Compiler.DisabledWarnings &= ~enableWarnings; compilerConfig.Compiler.DisabledWarningNumbers = compilerConfig.Compiler.DisabledWarningNumbers.Concat(disableWarningNumbers).Distinct().ToArray(); // Treat Warnings as Errors compilerConfig.Compiler.TreatWarningsAsErrors = this.TreatWarningsAsErrors; // sets source root (overrides any config setting): compilerConfig.Compiler.SourceRoot = new FullPath(sourceRoot); }
/// <summary> /// Loads configuration from Machine.config, phpc.exe.config, from files specified by command line arguments, /// and from command line arguments themselves. /// </summary> /// <exception cref="ConfigurationErrorsException">An error occured while loading the configuration.</exception> public static CompilerConfiguration/*!*/ LoadConfiguration( ApplicationContext/*!*/ appContext, List<FullPath>/*!*/ paths, TextWriter output) { Configuration.IsBuildTime = true; Configuration.Reload(appContext, true); // Machine.config, phpc.exe.config: CompilerConfiguration result = new CompilerConfiguration(Configuration.Application); // explicitly specified or default configs: foreach (FullPath path in paths) { if (output != null) output.WriteLine(path); result.LoadFromFile(appContext, path); } // load libraries lazily result.LoadLibraries(appContext); // return result; }
/// <summary> /// Compiles an application. /// </summary> /// <param name="applicationContext">Application context.</param> /// <param name="config">Compiler configuration record.</param> /// <param name="errorSink">Error sink.</param> /// <param name="ps">Parameters.</param> /// <exception cref="InvalidSourceException">Cannot read a source file/directory. See the inner exception for details.</exception> public void Compile( ApplicationContext/*!*/ applicationContext, CompilerConfiguration/*!*/ config, ErrorSink/*!*/ errorSink, CompilationParameters/*!*/ ps) { if (applicationContext == null) throw new ArgumentNullException("applicationContext"); if (config == null) throw new ArgumentNullException("config"); if (errorSink == null) throw new ArgumentNullException("errorSink"); ps.Validate(); PhpSourceFile entry_point_file = (ps.StartupFile != null) ? new PhpSourceFile(config.Compiler.SourceRoot, ps.StartupFile) : null; List<ResourceFileReference> resource_files = ResourceFileReference.FromFiles(ps.Resources); // creates directory if not exists: try { Directory.CreateDirectory(Path.GetDirectoryName(ps.OutPath)); } catch (Exception ex) { errorSink.Add(FatalErrors.ErrorCreatingFile, null, ErrorPosition.Invalid, ps.OutPath, ex.Message); } AssemblyKinds kind; switch (ps.Target) { case Targets.Dll: kind = AssemblyKinds.Library; entry_point_file = null; break; case Targets.Console: kind = AssemblyKinds.ConsoleApplication; break; case Targets.WinApp: kind = AssemblyKinds.WindowApplication; break; case Targets.Web: kind = AssemblyKinds.WebPage; entry_point_file = null; break; default: throw new ArgumentException(); } PhpAssemblyBuilder assembly_builder = PhpAssemblyBuilder.Create(applicationContext, kind, ps.Pure, ps.OutPath, ps.DocPath, entry_point_file, ps.Version, ps.Key, ps.Icon, resource_files, config.Compiler.Debug, ps.Force32Bit); assembly_builder.IsMTA = ps.IsMTA; Statistics.CompilationStarted(); ICompilerManager manager = (!ps.Pure) ? new ApplicationCompilerManager(applicationContext, assembly_builder) : null; try { CompilationContext context = new CompilationContext(applicationContext, manager, config, errorSink, config.Compiler.SourceRoot); assembly_builder.Build(EnumerateScripts(ps.SourcePaths, ps.SourceDirs, ps.FileExtensions, context), context); if (!context.Errors.AnyError && (ps.Target == Targets.Console || ps.Target == Targets.WinApp)) CopyApplicationConfigFile(config.Compiler.SourceRoot, ps.OutPath); } catch (CompilerException e) { errorSink.Add(e.ErrorInfo, null, ErrorPosition.Invalid, e.ErrorParams); } catch (InvalidSourceException e) { e.Report(errorSink); } catch (Exception e) { #if DEBUG //Console.WriteLine("Unexpected error: {0}", e.ToString());// removed, exception added into the error sink, so it's displayed in the VS Integration too #endif errorSink.AddInternalError(e); // compilation will fail, error will be displayed in Errors by VS Integration } finally { #if DEBUG Console.WriteLine(); Console.WriteLine("Statistics:"); Statistics.Dump(Console.Out, Path.GetDirectoryName(ps.OutPath)); Console.WriteLine(); #endif } }
/// <summary> /// Tries to load script from ASP.NET Temporary files - this is useful when /// web is not precompiled (so it is compiled into SSAs) and appdomain is reloaded /// (which means that we loose the cache) /// </summary> private bool TryLoadTemporaryCompiledNoLock(string ns, PhpSourceFile/*!*/ sourceFile, out CacheEntry cache_entry) { CompilerConfiguration config = new CompilerConfiguration(Configuration.Application); string name = WebCompilationContext.GetAssemblyCodedName(sourceFile, config); string sourcePath = sourceFile.FullPath.ToString(); bool sourceExists = File.Exists(sourcePath); DateTime sourceTime = sourceExists ? File.GetLastWriteTime(sourcePath) : DateTime.UtcNow.AddYears(1); // If file does not exist, fake the sourceTime to NOT load any SSA DLL. Delete them instead. DateTime configTime = Configuration.LastConfigurationModificationTime; // here find the max modification of all dependant files (configuration, script itself, other DLLs): long sourceStamp = Math.Max(Math.Max(sourceTime.Ticks, configTime.Ticks), appCodeAssemblyCreated.Ticks); // Find specified file in temporary files if (Directory.Exists(outDir)) foreach (string file in Directory.GetFiles(outDir, name + "*.dll")) { Match match = reFileStamp.Match(file); if (!match.Success) continue; long fileStamp; if (!Int64.TryParse((string)match.Groups["Stamp"].Value, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out fileStamp)) continue; // File is up-to-date if (sourceStamp < fileStamp) { Debug.WriteLine("WSSM", "Loading from ASP.NET Temporary files."); return LoadSSA(ns, file, out cache_entry); } else { // do "some" cleanup: try { File.Delete(file); File.Delete(Path.ChangeExtension(file, ".pdb")); } catch{ /*nop*/ } } } cache_entry = default(CacheEntry); return false; }
public WebCompilationContext(ApplicationContext applicationContext, ICompilerManager/*!*/ manager, CompilerConfiguration/*!*/ config, string/*!*/ workingDirectory, DateTime requestTimestamp) : base(applicationContext, manager, config, new WebErrorSink(config.Compiler.DisabledWarnings, config.Compiler.DisabledWarningNumbers), workingDirectory) { this.requestTimestamp = requestTimestamp; }
/// <summary> /// Compiles a script. /// Called when the script cannot be loaded from pre-compiled assembly and it should be compiled. /// </summary> /// <returns>The compiled script type.</returns> private ScriptInfo CompileScriptNoLock(string ns, PhpSourceFile/*!*/ sourceFile, RequestContext requestContext) { Debug.Assert(sourceFile != null); CompilerConfiguration config = new CompilerConfiguration(Configuration.Application); WebCompilationContext context = new WebCompilationContext(applicationContext, this, config, sourceFile.Directory, (requestContext != null) ? requestContext.HttpContext.Timestamp : DateTime.UtcNow); try { CacheEntry cache_entry; if (ScriptAssemblyBuilder.CompileScripts(new PhpSourceFile[] { sourceFile }, context)) { // assembly should be already added into the cache by Persist() method if (TryGetCachedEntry(ns, out cache_entry)) return cache_entry.ScriptInfo; } return null; } catch (CompilerException) { return null; } catch (Exception) { // record stack info to the message if the manager resides in a dedicated domain: throw; } }
/// <summary> /// Implements PHP <c>eval</c> construct with given code prefix and suffix. /// A result of concatanation prefix + code + suffix is compiled. /// Prefix should contain no new line characters. /// </summary> internal static object EvalInternal( string prefix, string code, string suffix, EvalKinds kind, ScriptContext/*!*/ scriptContext, Dictionary<string, object> localVariables, DObject self, DTypeDesc referringType, SourceCodeDescriptor descriptor, bool entireFile, NamingContext namingContext) { Debug.Assert(prefix != null && suffix != null); // composes code to be compiled: code = String.Concat(prefix, code, suffix); TransientAssemblyBuilder assembly_builder = scriptContext.ApplicationContext.TransientAssemblyBuilder; // looks up the cache: TransientModule module = assembly_builder.TransientAssembly.GetModule(scriptContext, referringType, code, descriptor); if (module == null) // double checked lock, // if module != null, it is definitely completed // since module is added into TransientAssembly at the end // of assembly_builder.Build lock (assembly_builder.TransientAssembly) { // lookup again, since it could be added into TransientAssembly while lock module = assembly_builder.TransientAssembly.GetModule(scriptContext, referringType, code, descriptor); if (module == null) { if (kind == EvalKinds.SyntheticEval) Debug.WriteLine("SYN EVAL", "Eval cache missed: '{0}'", code.Substring(0, Math.Max(code.IndexOf('{'), 0)).TrimEnd()); else Debug.WriteLine("EVAL", "Eval cache missed: '{0}'({1},{2})", descriptor.ContainingSourcePath, descriptor.Line, descriptor.Column); CompilerConfiguration config = new CompilerConfiguration(Configuration.Application); CompilationContext context = new CompilationContext(scriptContext.ApplicationContext, null, config, new EvalErrorSink(-prefix.Length, config.Compiler.DisabledWarnings, config.Compiler.DisabledWarningNumbers), scriptContext.WorkingDirectory); TransientCompilationUnit unit = assembly_builder.Build(code, descriptor, kind, context, scriptContext, referringType, namingContext, entireFile); // compilation failed: if (unit == null) return false; module = unit.TransientModule; } } // activates unconditionally declared types, functions and constants: module.TransientCompilationUnit.Declare(scriptContext); return module.Main(scriptContext, localVariables, self, referringType, true); }
/// <summary> /// Implements PHP <c>eval</c> construct with given code prefix and suffix. /// A result of concatanation prefix + code + suffix is compiled. /// Prefix should contain no new line characters. /// </summary> internal static object EvalInternal( string prefix, string code, string suffix, EvalKinds kind, ScriptContext /*!*/ scriptContext, Dictionary <string, object> localVariables, DObject self, DTypeDesc referringType, SourceCodeDescriptor descriptor, bool entireFile, NamingContext namingContext) { Debug.Assert(prefix != null && suffix != null); // composes code to be compiled: code = String.Concat(prefix, code, suffix); TransientAssemblyBuilder assembly_builder = scriptContext.ApplicationContext.TransientAssemblyBuilder; // looks up the cache: TransientModule module = assembly_builder.TransientAssembly.GetModule(scriptContext, referringType, code, descriptor); if (module == null) { // double checked lock, // if module != null, it is definitely completed // since module is added into TransientAssembly at the end // of assembly_builder.Build lock (assembly_builder.TransientAssembly) { // lookup again, since it could be added into TransientAssembly while lock module = assembly_builder.TransientAssembly.GetModule(scriptContext, referringType, code, descriptor); if (module == null) { if (kind == EvalKinds.SyntheticEval) { Debug.WriteLine("SYN EVAL", "Eval cache missed: '{0}'", code.Substring(0, Math.Max(code.IndexOf('{'), 0)).TrimEnd()); } else { Debug.WriteLine("EVAL", "Eval cache missed: '{0}'({1},{2})", descriptor.ContainingSourcePath, descriptor.Line, descriptor.Column); } CompilerConfiguration config = new CompilerConfiguration(Configuration.Application); CompilationContext context = new CompilationContext(scriptContext.ApplicationContext, null, config, new EvalErrorSink(-prefix.Length, config.Compiler.DisabledWarnings, config.Compiler.DisabledWarningNumbers), scriptContext.WorkingDirectory); TransientCompilationUnit unit = assembly_builder.Build(code, descriptor, kind, context, scriptContext, referringType, namingContext, entireFile); // compilation failed: if (unit == null) { return(false); } module = unit.TransientModule; } } } // activates unconditionally declared types, functions and constants: module.TransientCompilationUnit.Declare(scriptContext); return(module.Main(scriptContext, localVariables, self, referringType, true)); }