public Evaluator(Regex reg, string replacement, SourceCodeDescriptor sourceCodeDesc, DObject self, Dictionary<string, object> definedVariables) { this.reg = reg; this.definedVariables = definedVariables; this.replacement = replacement; this.sourceCodeDesc = sourceCodeDesc; this.count = 0; this.self = self; }
/// <summary> /// Takes a regular expression <paramref name="pattern"/> and one of <paramref name="replacement"/> or /// <paramref name="callback"/>. Performs replacing on <paramref name="data"/>, which can be /// <see cref="PhpArray"/>, in other cases it is converted to string. /// If <paramref name="data"/> is <see cref="PhpArray"/>, every value is converted to string and /// replacement is performed in place in this array. /// Either <paramref name="replacement"/> or <paramref name="callback"/> should be null. /// </summary> /// <param name="self">Instance of object that called the replace method (replace pattern may contain $this)</param> /// <param name="definedVariables">Array with local variables - can be used by replace pattern</param> /// <param name="pattern">Regular expression to search.</param> /// <param name="replacement">Regular replacement expression. Should be null if callback is specified.</param> /// <param name="callback">Callback function that should be called to make replacements. Should be null /// if replacement is specified.</param> /// <param name="data">Array or string where pattern is searched.</param> /// <param name="limit">Max count of replacements for each item in subject.</param> /// <param name="descriptor"><see cref="SourceCodeDescriptor"/> for possible lambda function creation.</param> /// <param name="count">Cumulated number of replacements.</param> /// <returns></returns> private static object SimpleReplace(DObject self, Dictionary<string, object> definedVariables, object pattern, string replacement, PhpCallback callback, object data, int limit, SourceCodeDescriptor descriptor, ref int count) { Debug.Assert(limit >= -1); // exactly one of replacement or callback is valid: Debug.Assert(replacement != null ^ callback != null); PerlRegExpConverter converter = ConvertPattern(pattern, replacement); if (converter == null) return null; // get types of data we need: PhpArray data_array = data as PhpArray; string data_string = (data_array == null) ? ConvertData(data, converter) : null; // data comprising of a single string: if (data_array == null) { return ReplaceInternal(self, definedVariables, converter, callback, data_string, limit, descriptor, ref count); } else { // data is array, process each item: var enumerator = data_array.GetFastEnumerator(); while (enumerator.MoveNext()) { enumerator.CurrentValue = ReplaceInternal(self, definedVariables, converter, callback, ConvertData(enumerator.CurrentValue, converter), limit, descriptor, ref count); } enumerator.Dispose(); // return array with items replaced: return data; } }
internal void PostCompile(SourceCodeDescriptor descriptor) { module_builder.Bake(); Bake(); // TODO: analyzer.GetTypeDependencies(); var dependentTypes = new List<KeyValuePair<string, DTypeDesc>>(bakedTypes != null ? bakedTypes.Length : 0); if (bakedTypes != null) foreach (var type in bakedTypes) { // add base as this type dependency: AddDependentType(type.Value, dependentTypes, type.Value.Base); // do the same for type.Value.Interfaces var ifaces = type.Value.Interfaces; if (ifaces != null && ifaces.Length > 0) for (int i = 0; i < ifaces.Length; i++) AddDependentType(type.Value, dependentTypes, ifaces[i]); } // module = assembly_builder.TransientAssembly.AddModule(module_builder, dependentTypes, sourceUnit.Code, descriptor); }
/// <summary> /// Replaces <paramref name="limit"/> occurences of substrings. /// </summary> /// <param name="converter"> /// Converter used for replacement if <paramref name="callback"/> is <B>null</B>. /// </param> /// <param name="self">Instance of object that called the replace method (replace pattern may contain $this)</param> /// <param name="definedVariables">Array with local variables - can be used by replace pattern</param> /// <param name="callback">Callback to call for replacement strings.</param> /// <param name="str">String to search for matches.</param> /// <param name="limit">Max number of replacements performed.</param> /// <param name="sourceCodeDesc"><see cref="SourceCodeDescriptor"/> for possible lambda function creation.</param> /// <param name="count">Cumulated number of replacements.</param> /// <returns></returns> private static string ReplaceInternal(DObject self, Dictionary<string, object> definedVariables, PerlRegExpConverter converter, PhpCallback callback, string str, int limit, SourceCodeDescriptor sourceCodeDesc, ref int count) { Debug.Assert(limit >= -1); if (callback == null) { // replace without executing code or counting the number of replacements: if ((converter.PerlOptions & PerlRegexOptions.Evaluate) == 0 && count < 0) return converter.Regex.Replace(str, converter.DotNetReplaceExpression, limit); Evaluator evaluator = new Evaluator(converter.Regex, converter.DotNetReplaceExpression, sourceCodeDesc, self, definedVariables); MatchEvaluator match_evaluator; if ((converter.PerlOptions & PerlRegexOptions.Evaluate) != 0) match_evaluator = new MatchEvaluator(evaluator.ReplaceCodeExecute); else match_evaluator = new MatchEvaluator(evaluator.ReplaceCount); string result = converter.Regex.Replace(str, match_evaluator, limit); count += evaluator.Count; return result; } else { StringBuilder result = new StringBuilder((str != null) ? str.Length : 0); int last_index = 0; Match m = converter.Regex.Match(str); while (m.Success && (limit == -1 || limit-- > 0)) { // append everything from input string to current match result.Append(str, last_index, m.Index - last_index); // move index after current match last_index = m.Index + m.Length; PhpArray arr = new PhpArray(m.Groups.Count, 0); for (int i = 0; i < m.Groups.Count; i++) arr[i] = m.Groups[i].Value; // append user callback function result string replacement = Core.Convert.ObjectToString(callback.Invoke(arr)); result.Append(replacement); m = m.NextMatch(); count++; } // remaining string result.Append(str, last_index, str.Length - last_index); return result.ToString(); } }
/// <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> /// Called before 'Compile' to initialize module & assembly builders, so they can be used by the caller. /// </summary> internal bool PreCompile(CompilationContext/*!*/ context, ScriptContext/*!*/ scriptContext, SourceCodeDescriptor descriptor, EvalKinds kind, DTypeDesc referringType) { this.resolvingScriptContext = scriptContext; this.referringType = referringType; // TODO: isDynamic is tricky... // .. we need to define module_builder before any type/etc.. is reduced from the parser // .. but we don't know whether it will be dynamic in advance! this.assembly_builder = scriptContext.ApplicationContext.TransientAssemblyBuilder; this.module_builder = assembly_builder.DefineModule(this, context.Config.Compiler.Debug, descriptor.ContainingTransientModuleId, kind, descriptor.ContainingSourcePath); this.module = module_builder; this.evalKind = kind; sourceUnit.Parse( context.Errors, this, new Position(descriptor.Line, descriptor.Column, 0, descriptor.Line, descriptor.Column, 0), context.Config.Compiler.LanguageFeatures); if (context.Errors.AnyFatalError) return false; // any declaration implies non-dynamicity: // TODO: this mode needs to be checked... // isDynamic = types == null && functions == null && constants == null; return true; }
public static bool Assert( object assertion, ScriptContext context, Dictionary<string, object> definedVariables, DObject self, DTypeDesc includer, string containingSourcePath, int line, int column, int containerId, NamingContext namingContext) { object result; string code; // skips asserts if not active: if (!context.Config.Assertion.Active) return true; if ((code = PhpVariable.AsString(assertion)) != null) { // disables error reporting if eval should be quite: if (context.Config.Assertion.Quiet) context.DisableErrorReporting(); SourceCodeDescriptor descriptor = new SourceCodeDescriptor(containingSourcePath, containerId, line, column); // evaluates the expression: result = EvalInternal("return ", code, ";", EvalKinds.Assert, context, definedVariables, self, includer, descriptor, false, namingContext); // restores error reporting if eval have been quite: if (context.Config.Assertion.Quiet) context.EnableErrorReporting(); } else { result = assertion; } // checks the result of assertion: return CheckAssertion(result, code, context, containingSourcePath, line, column, namingContext); }
/// <summary> /// Compiles a function with a specified parameters and body and adds it to dynamic module. /// </summary> /// <param name="parameters">The function's parameters (e.g. <c>"$x, $y = 1, &$z"</c>).</param> /// <param name="body">The function's body.</param> /// <param name="context">A script context.</param> /// <param name="descriptor"></param> /// <returns>A name of the created function.</returns> /// <exception cref="ArgumentNullException">Any parameter is a <B>null</B> reference.</exception> public static string CreateLambdaFunction(string/*!*/ parameters, string/*!*/ body, ScriptContext/*!*/ context, SourceCodeDescriptor descriptor) { if (parameters == null) throw new ArgumentNullException("parameters"); if (body == null) throw new ArgumentNullException("body"); if (context == null) throw new ArgumentNullException("context"); string name = GenerateLambdaName(parameters, body); if (context.DeclaredFunctions != null && context.DeclaredFunctions.ContainsKey(name)) return name; string prefix1, prefix2; GetLamdaFunctionCodePrefixes(name, parameters, out prefix1, out prefix2); context.ClearCapturedSourceCodeDescriptor(); EvalInternal(prefix2, body, "}", EvalKinds.LambdaFunction, context, null, null, null, descriptor, false, null); // TODO: naming context in lambda function?? return name; }
/// <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> /// 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; }
internal void PostCompile(SourceCodeDescriptor descriptor) { module_builder.Bake(); Bake(); // TODO: analyzer.GetTypeDependencies(); var dependentTypes = new List<KeyValuePair<string, DTypeDesc>>(bakedTypes != null ? bakedTypes.Length : 0); if (bakedTypes != null) foreach (var type in bakedTypes) { var parent = type.Value.Base; if (parent is PhpTypeDesc && parent.RealType.Module != type.Value.RealType.Module) // base type if from different module (external dependency) dependentTypes.Add(new KeyValuePair<string, DTypeDesc>(parent.MakeFullName(), parent)); // TODO: do the same for type.Value.Interfaces } // module = assembly_builder.TransientAssembly.AddModule(module_builder, dependentTypes, sourceUnit.Code, descriptor); }