/// <summary> /// Returns compiled module if exists. /// </summary> /// <param name="sourceFile">Source file.</param> /// <param name="ctx">A compilation context.</param> /// <returns>The <see cref="PhpModule"/> or a <B>null</B> reference.</returns> /// <remarks> /// Checks whether a module associated with a <paramref name="sourceFile"/> has already been compiled. /// If so returns the respective <see cref="PhpModule"/>. Otherwise a <B>null</B> reference is returned. /// Does no locking since application compiler is thread unsafe. /// </remarks> public PhpModule LockForCompiling(PhpSourceFile/*!*/ sourceFile, CompilationContext/*!*/ ctx) { // take a look into script library first if (applicationContext.ScriptLibraryDatabase.ContainsScript(sourceFile.FullPath)) { return applicationContext.ScriptLibraryDatabase.GetScriptModule(sourceFile.FullPath); } return assemblyBuilder.Assembly.GetModule(sourceFile); }
/// <summary> /// Initializes a new instance of the <see cref="PHP.Core.CodeGenerator"/> class. /// </summary> public CodeGenerator(CompilationContext/*!*/ context) { ScriptContextPlace = new IndexedPlace(PlaceHolder.Argument, ScriptBuilder.ArgContext); TypeContextPlace = new IndexedPlace(PlaceHolder.Argument, ScriptBuilder.ArgIncluder); this.context = context; this.il = null; this.currentVariablesTable = null; this.currentLabels = null; this.locationStack = new CompilerLocationStack(); this.branchingStack = new BranchingStack(this); this.chainBuilder = new ChainBuilder(this); }
/// <summary> /// Called by compiler when information about compiling progress is available. /// </summary> /// <remarks>Ignored by this manager.</remarks> public void Info(PhpSourceFile/*!*/ sourceFile, CompilationContext ctx) { // nop // }
/// <summary> /// Wakes up threads waiting for a script compilation finish. /// </summary> /// <param name="sourceFile">The compiled script's source file.</param> /// <param name="successful">Whether compilation has been successful.</param> /// <param name="ctx">A compilation context.</param> /// <remarks>Should be called after <see cref="Persist"/>.</remarks> public void UnlockForCompiling(PhpSourceFile/*!*/ sourceFile, bool successful, CompilationContext ctx) { Debug.Assert(sourceFile != null && ctx is WebCompilationContext); ManualResetEvent compilation_finished; lock (eventsMutex) { if (events.TryGetValue(sourceFile, out compilation_finished)) events.Remove(sourceFile); } // any waiting thread can access the compiled assembly now: if (compilation_finished != null) compilation_finished.Set(); }
public InclusionGraphBuilder(CompilationContext/*!*/ context) { this.context = context; Statistics.Inclusions.InitializeGraph(); }
public override bool Build(IEnumerable<PhpSourceFile>/*!!*/ sourceFiles, CompilationContext/*!*/ context) { return CompileScripts(sourceFiles, context); }
public abstract bool Build(IEnumerable<PhpSourceFile>/*!!*/ sourceFiles, CompilationContext/*!*/ context);
/// <summary> /// Compiles a specified collection of scripts within the given compilation context. /// </summary> private static IEnumerable<PhpSourceFile>/*!*/ EnumerateScriptsInDirectory(IEnumerable<FullPath>/*!*/ sourcePaths, ICollection<string>/*!*/ fileExtensions, CompilationContext/*!*/ context) { Debug.Assert(sourcePaths != null && fileExtensions != null && context != null); FullPath source_root = context.Config.Compiler.SourceRoot; foreach (FullPath path in sourcePaths) { if (fileExtensions.Count != 0 && !path.HasAnyExtension(fileExtensions)) continue; string pathString = path; bool skip = false; foreach (string skipPath in context.Config.Compiler.ForcedDynamicInclusionTranslatedFullPaths) if (pathString.StartsWith(skipPath)) { skip = true; break; } if (skip) continue; yield return new PhpSourceFile(source_root, path); } }
/// <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> /// Adds a new module to application's assembly. /// </summary> /// <param name="compiledUnit">Unit being compiled.</param> /// <param name="ctx">A compilation context.</param> /// <returns>The builder where compiler should emit the resulting code.</returns> public IPhpModuleBuilder/*!*/ DefineModuleBuilder(CompilationUnitBase/*!*/ compiledUnit, CompilationContext ctx) { return assemblyBuilder.DefineModule(compiledUnit); }
/// <summary> /// Triggered by the compiler on events such are start of compilation, end of compilation etc. /// </summary> /// <param name="sourceFile">A source path to a script being processed.</param> /// <param name="ctx">A compilation context.</param> public void Info(PhpSourceFile/*!*/ sourceFile, CompilationContext/*!*/ ctx) { Console.WriteLine(sourceFile.RelativePath); }
/// <summary> /// Remembers whether compilation has been successful. /// </summary> /// <param name="sourceFile">Source file.</param> /// <param name="successful">Whether compilation was successful.</param> /// <param name="ctx">A compilation context.</param> public void UnlockForCompiling(PhpSourceFile/*!*/ sourceFile, bool successful, CompilationContext/*!*/ ctx) { this.successful &= successful; }
/// <summary> /// Ignored. All modules are stored in a single assembly which is persisted in <see cref="Finish"/>. /// </summary> public void Persist(CompilationUnitBase/*!*/ compiledUnit, CompilationContext/*!*/ ctx) { // nop // }
public void CleanAllUnits(CompilationContext/*!*/ context, bool successful) { foreach (CompilationUnit unit in nodes.Values) unit.CleanUp(context, successful); }
/// <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 } }
internal Analyzer(CompilationContext/*!*/ context) { this.context = context; condLevel = 0; loopNestingLevel = 0; state = States.Initial; }
private static IEnumerable<PhpSourceFile>/*!*/ EnumerateScripts( ICollection<FullPath>/*!*/ sourcePaths, ICollection<FullPath>/*!*/ sourceDirs, ICollection<string>/*!*/ fileExtensions, CompilationContext/*!*/ context) { Debug.Assert(sourcePaths != null && sourceDirs != null && fileExtensions != null && context != null); // enumerate listed source files: foreach (PhpSourceFile file in EnumerateScriptsInDirectory(sourcePaths, ArrayUtils.EmptyStrings, context)) yield return file; // enumerate source files recursively located in specified directories: foreach (FullPath dir in sourceDirs) { foreach (PhpSourceFile file in EnumerateScriptsRecursive(dir, fileExtensions, context)) yield return file; } }
/// <summary> /// Checks whether a specified source file needs to be (re)compiled and if so locks /// it so that any other compiler from the current app domain will wait until this compilation finishes. /// </summary> /// <param name="sourceFile">Source file.</param> /// <param name="ctx">Compilation context.</param> /// <returns> /// A compiled module associated with the <paramref name="sourceFile"/> or a <B>null</B> reference /// if a compilation of that file is needed. /// </returns> public PhpModule LockForCompiling(PhpSourceFile/*!*/ sourceFile, CompilationContext/*!*/ ctx) { Debug.Assert(ctx is WebCompilationContext); WebCompilationContext context = (WebCompilationContext)ctx; // take a look into script library first if (applicationContext.ScriptLibraryDatabase.ContainsScript(sourceFile.FullPath)) { return applicationContext.ScriptLibraryDatabase.GetScriptModule(sourceFile.FullPath); } for (int i = 0; i < AttemptsToGetCompiledAssembly; i++) { string ns = ScriptModule.GetSubnamespace(sourceFile.RelativePath, false); CacheEntry cache_entry; // TODO: Single script assemblies can be loaded and reflected // but this still have to be done for MSAs if (TryLoadCachedEntry(ns, sourceFile, out cache_entry) && !cache_entry.ScriptAssembly.IsMultiScript) return cache_entry.ScriptAssembly.GetModule(sourceFile); // compilation is in progress or not started yet // ManualResetEvent compilation_finished; lock (eventsMutex) { // if compilation of the target file has not started yet: if (!events.TryGetValue(sourceFile, out compilation_finished)) { // adds event which others wait on: events.Add(sourceFile, new ManualResetEvent(false)); return null; } } // waits until compilation is finished and assembly has been persisted: compilation_finished.WaitOne(CompilationTimeout, false); } return null; }
/// <summary> /// Recursively searches a directory for all files matching the web script file pattern. /// </summary> /// <exception cref="InvalidSourceException">Error reading the directory.</exception> private static IEnumerable<PhpSourceFile>/*!*/ EnumerateScriptsRecursive(FullPath directory, ICollection<string>/*!*/ fileExtensions, CompilationContext/*!*/ context) { Debug.Assert(fileExtensions != null && context != null); Debug.Assert(!directory.IsEmpty); string pathString = directory; foreach (string skipPath in context.Config.Compiler.ForcedDynamicInclusionTranslatedFullPaths) if (pathString.StartsWith(skipPath)) yield break; FullPath[] files, directories; try { files = directory.GetFiles(); directories = directory.GetDirectories(); } catch (Exception e) { throw new InvalidSourceException(directory, e); } // compiles scripts in the current directory: foreach (PhpSourceFile file in EnumerateScriptsInDirectory(files, fileExtensions, context)) yield return file; // processes subdirectories: foreach (FullPath dir in directories) { foreach (PhpSourceFile file in EnumerateScriptsRecursive(dir, fileExtensions, context)) yield return file; } }
/// <summary> /// Creates a new instance of <see cref="ScriptBuilder"/> to be used for compilation of the script's assembly. /// </summary> /// <param name="compiledUnit">Unit being compiled.</param> /// <param name="ctx">The current compilation context.</param> /// <returns>The script builder.</returns> public IPhpModuleBuilder DefineModuleBuilder(CompilationUnitBase/*!*/ compiledUnit, CompilationContext/*!*/ ctx) { Debug.Assert(compiledUnit is ScriptCompilationUnit && ctx is WebCompilationContext); WebCompilationContext context = (WebCompilationContext)ctx; ScriptCompilationUnit unit = (ScriptCompilationUnit)compiledUnit; // creates an assembly name: AssemblyName name = context.GetAssemblyFullName(unit.SourceUnit.SourceFile); // creates a script assembly builder: SingleScriptAssemblyBuilder builder = new SingleScriptAssemblyBuilder(applicationContext, name, outDir, name.Name + AssemblyExt, AssemblyKinds.WebPage, context.Config.Compiler.Debug, false, context.SaveOnlyAssembly, null); return builder.DefineScript(unit); }
public override bool Build(IEnumerable<PhpSourceFile>/*!*/ sourceFiles, CompilationContext/*!*/ context) { PureCompilationUnit unit = new PureCompilationUnit(false, false); bool success = unit.Compile(sourceFiles, this, context, context.Config.Globalization.PageEncoding); if (success) Save(); return success; }
/// <summary> /// Persists a built script to a file. /// </summary> /// <param name="compilationUnit">The unit being compiled.</param> /// <param name="ctx">Compilation context.</param> public void Persist(CompilationUnitBase/*!*/ compilationUnit, CompilationContext/*!*/ ctx) { Debug.Assert(compilationUnit is ScriptCompilationUnit && ctx is WebCompilationContext); WebCompilationContext context = (WebCompilationContext)ctx; ScriptCompilationUnit unit = (ScriptCompilationUnit)compilationUnit; SingleScriptAssemblyBuilder assembly_builder = (SingleScriptAssemblyBuilder)unit.ModuleBuilder.AssemblyBuilder; assembly_builder.Save(); string ns = ScriptModule.GetSubnamespace(unit.SourceUnit.SourceFile.RelativePath, false); if (SaveOnlyAssembly) { // assembly not loaded into memory yet (we need to load from fs to not break debugging) string file = assembly_builder.Assembly.Path; CacheEntry entry; LoadSSA(ns, file, out entry); } else { // We only add the assembly into the cache, if it was built and loaded into memory. // Otherwise the assembly has to be reloaded from the disk. // This is because of debugging, since we don't want to load dynamic assemblies into memory, which breaks debug symbols. // makes up a list of dependent assembly names: string[] includers = new string[unit.Includers.Count]; int i = 0; foreach (StaticInclusion inclusion in unit.Includers) includers[i++] = ScriptModule.GetSubnamespace(inclusion.Includer.SourceUnit.SourceFile.RelativePath, false); // what assemblies are included by this one? string[] inclusions = new string[unit.Inclusions.Count]; int j = 0; foreach (StaticInclusion inclusion in unit.Inclusions) inclusions[j++] = ScriptModule.GetSubnamespace(new RelativePath(0, inclusion.Includee.RelativeSourcePath), false); // adds dependencies on the source file and the included assemblies: SetCacheEntry(ns, new CacheEntry( assembly_builder.SingleScriptAssembly.GetScriptType(), assembly_builder.SingleScriptAssembly, context.RequestTimestamp, includers, inclusions, true), true, true); } }
public static bool CompileScripts(IEnumerable<PhpSourceFile>/*!!*/ sourceFiles, CompilationContext/*!*/ context) { bool success = true; InclusionGraphBuilder graph_builder = null; Debug.WriteLine("SAB", "CompileScripts()"); try { graph_builder = new InclusionGraphBuilder(context); foreach (PhpSourceFile source_file in sourceFiles) success &= graph_builder.AnalyzeDfsTree(source_file); if (success) graph_builder.EmitAllUnits(new CodeGenerator(context)); } catch (Exception) { success = false; throw; } finally { if (graph_builder != null) { graph_builder.CleanAllUnits(context, success); graph_builder.Dispose(); } context.Manager.Finish(success); } return success; }
/// <summary> /// The argument <paramref name="completeSource" /> determines whether the source code /// is complete PHP script file, which is a case in dynamic include in Silverlight /// </summary> public TransientCompilationUnit Build(string/*!*/ sourceCode, SourceCodeDescriptor descriptor, EvalKinds kind, CompilationContext/*!*/ context, ScriptContext/*!*/ scriptContext, DTypeDesc referringType, NamingContext namingContext, bool completeSource) { PhpSourceFile source_file = new PhpSourceFile(context.Config.Compiler.SourceRoot, RelativePath.ParseCanonical(descriptor.ContainingSourcePath)); Encoding encoding = context.Config.Globalization.PageEncoding; TransientCompilationUnit result = new TransientCompilationUnit (sourceCode, source_file, encoding, namingContext, descriptor.Line, descriptor.Column, completeSource); if (!result.PreCompile(context, scriptContext, descriptor, kind, referringType)) return null; DefineGlobalType(((TransientModuleBuilder)result.ModuleBuilder).AssemblyBuilder.RealModuleBuilder); if (!result.Compile(context, kind)) return null; BakeGlobals(); result.PostCompile(descriptor); return result; }