public static Hashtable MapFilter( [DekiScriptParam("map value")] Hashtable map, [DekiScriptParam("condition to execute for each item (use '$' to refer to the key-value pair)")] string condition, DekiScriptRuntime runtime ) { DekiScriptExpression expr = DekiScriptParser.Parse(new Location("map.select(condition)"), condition); Hashtable result = new Hashtable(StringComparer.OrdinalIgnoreCase); foreach (DictionaryEntry entry in map) { DekiScriptMap keyvalue = new DekiScriptMap(); object value = Eval(entry.Value, runtime); keyvalue.Add("key", DekiScriptLiteral.FromNativeValue(entry.Key)); keyvalue.Add("value", DekiScriptLiteral.FromNativeValue(value)); DekiScriptEnv env = runtime.CreateEnv(); env.Vars.Add(DekiScriptRuntime.DEFAULT_ID, keyvalue); DekiScriptLiteral test = runtime.Evaluate(expr, DekiScriptEvalMode.EvaluateSafeMode, env); if (!test.IsNilFalseZero) { result.Add(entry.Key, value); } } return(result); }
public static object StringDeserialize( [DekiScriptParam("string value")] string value, DekiScriptRuntime runtime ) { return(runtime.Evaluate(DekiScriptParser.Parse(new Location("string.deserialize(value)"), value), DekiScriptEvalMode.EvaluateSafeMode, new DekiScriptEnv()).NativeValue); }
public void Dom_evaluation_with_dynamic_content() { XDoc doc = new XDoc("html").UsePrefix("eval", "http://mindtouch.com/2007/dekiscript") .Start("body") .Start("div").Attr("block", "var x = string.toupper(date.now)") .Elem("eval:expr", "x .. 3; string.nbsp") .End() .End(); // parse node DekiScriptExpression node = DekiScriptParser.Parse(doc); Assert.AreEqual("<html><body>(discard (var x = string.toupper(date.now)); <div>(x .. 3; string.nbsp)</div>) !! web.showerror(__error)</body></html> !! web.showerror(__error)", node.ToString()); // TODO (steveb): disabled the partial evaluation test for now // partial evaluation //node = node.Optimize(DekiScriptEvalMode.Evaluate, DekiScriptEnv.Create()); //Assert.AreEqual("<html><body>discard (var x = \"native:///string.toupper\"( \"native:///$date.now\"())); <div>x .. 3; \"�\"; </div>; </body></html>", node.ToString()); // full evaluation //string now = DekiScriptLibrary.DateNow(); //XDoc value = node.Evaluate(DekiScriptEvalMode.Evaluate, DekiScriptEnv.Create()).AsEmbeddableXml(false); //Assert.AreEqual("<html><body><div>" + now.ToUpperInvariant() + "3 </div></body></html>", value.ToXHtml()); }
public static Hashtable ListGroupBy( [DekiScriptParam("list value")] ArrayList list, [DekiScriptParam("expression to apply for grouping (use '$' to refer to the current item)")] string expression, DekiScriptRuntime runtime ) { Hashtable result = new Hashtable(StringComparer.OrdinalIgnoreCase); DekiScriptExpression expr = DekiScriptParser.Parse(new Location("list.groupby(expression)"), expression); // loop over all items in list foreach (object entry in list) { DekiScriptEnv env = runtime.CreateEnv(); env.Vars.Add(DekiScriptRuntime.DEFAULT_ID, DekiScriptLiteral.FromNativeValue(entry)); // evalute grouping expression string key = runtime.Evaluate(expr, DekiScriptEvalMode.EvaluateSafeMode, env).AsString(); if (key != null) { // check if an accumulation list already exists; otherwise, create one ArrayList accumulator = (ArrayList)result[key]; if (accumulator == null) { accumulator = new ArrayList(); result[key] = accumulator; } accumulator.Add(entry); } } return(result); }
public void Can_parse_string() { var code = "(var x = 5; if(x > 5) { x })"; var expr = DekiScriptParser.Parse(Location.Start, code); Assert.AreEqual(code, expr.ToString()); }
public void Can_parse_stream() { var code = "(var x = 5; if(x > 5) { x })"; var stream = new MemoryStream(Encoding.UTF8.GetBytes(code)); var expr = DekiScriptParser.Parse(Location.Start, stream); Assert.AreEqual(code, expr.ToString()); }
public static ArrayList ListOrderBy( [DekiScriptParam("list value")] ArrayList list, [DekiScriptParam("key name or list of key names; sort direction is controlled by appendending \" ascending\" or \" descending\" to the key(s); when omitted, the direction is asending by default")] object keys, DekiScriptRuntime runtime ) { // check for trivial sort case if (keys is string) { string key = ((string)keys).Trim(); // the key cannot contain access operators if (key.IndexOfAny(new[] { '.', '[', ']' }) < 0) { return(ListOrderBy_IsReversed(ref key) ? ListSort(list, key, true, null, runtime) : ListSort(list, key, false, null, runtime)); } // let's change 'keys' into an array list for processing convenience ArrayList temp = new ArrayList { keys }; keys = temp; } // check that 'keys' has a valid type if (!(keys is ArrayList)) { throw new DekiScriptBadTypeException(Location.None, DekiScriptLiteral.AsScriptType(keys.GetType()), new[] { DekiScriptType.STR, DekiScriptType.LIST }); } // build comparison expression StringBuilder compare = new StringBuilder(); foreach (string key in (ArrayList)keys) { if (compare.Length > 0) { compare.Append(" || "); } // process sort field string field = key; if (ListOrderBy_IsReversed(ref field)) { compare.AppendFormat("(if($left.{0} < $right.{0}) 1; else if($left.{0} > $right.{0}) -1; else 0;)", field); } else { compare.AppendFormat("(if($left.{0} < $right.{0}) -1; else if($left.{0} > $right.{0}) 1; else 0;)", field); } } // sort list list.Sort(new DekiScriptComparer(runtime, DekiScriptParser.Parse(new Location("list.orderby(compare)"), compare.ToString()))); return(list); }
public void Dom_partial_evaluation_for_foreach2() { const string source = "<html xmlns:eval='http://mindtouch.com/2007/dekiscript'><body init='var x = 2'><div foreach='var y in num.series(1, 5)' where='y != x'><eval:expr>y</eval:expr></div></body></html>"; XDoc doc = XDocFactory.From(source, MimeType.XML); DekiScriptExpression node = DekiScriptParser.Parse(doc); node = _dekiScriptTester.Runtime.Optimize(node, DekiScriptEvalMode.Evaluate, _dekiScriptTester.Runtime.CreateEnv()); Assert.AreEqual("<html><body><div>1; </div><div>3; </div><div>4; </div><div>5; </div></body></html>", node.ToString()); }
public Yield Execute(DreamContext context, DreamMessage request, Result <DreamMessage> response) { string expression = context.GetParam("expression"); DekiScriptExpression expr = DekiScriptParser.Parse(new Location("POST:execute"), expression); DekiScriptLiteral result = _runtime.Evaluate(expr, DekiScriptEvalMode.Evaluate, _env); if (result.ScriptType == DekiScriptType.XML) { response.Return(DreamMessage.Ok(MimeType.XML, (XDoc)result.NativeValue)); } else { response.Return(DreamMessage.Ok(MimeType.TEXT, result.ToString())); } yield break; }
public static ArrayList ListApply( [DekiScriptParam("list value")] ArrayList list, [DekiScriptParam("expression to apply (use '$' to refer to the current item)")] string expression, DekiScriptRuntime runtime ) { DekiScriptExpression expr = DekiScriptParser.Parse(new Location("list.apply(expression)"), expression); ArrayList result = new ArrayList(); foreach (object entry in list) { DekiScriptEnv env = runtime.CreateEnv(); env.Vars.Add(DekiScriptRuntime.DEFAULT_ID, DekiScriptLiteral.FromNativeValue(entry)); result.Add(runtime.Evaluate(expr, DekiScriptEvalMode.EvaluateSafeMode, env).NativeValue); } return(result); }
public Empty Visit(DekiScriptXml expr, StringBuilder state) { XDoc doc = expr.Value; if (!doc.IsEmpty) { XmlNode node = doc.AsXmlNode; if (node.NodeType == XmlNodeType.Attribute) { state.Append(node.OuterXml); } else { DekiScriptParser.Parse(doc, false).VisitWith(this, state); } } return(Empty.Value); }
public Empty Visit(DekiScriptAccess expr, StringBuilder state) { expr.Prefix.VisitWith(this, state); DekiScriptString index = expr.Index as DekiScriptString; if ((index != null) && DekiScriptParser.IsIdentifier(index.Value)) { state.Append("."); state.Append(index.Value); } else { state.Append("["); expr.Index.VisitWith(this, state); state.Append("]"); } return(Empty.Value); }
public static ArrayList ListIntersect( [DekiScriptParam("first list")] ArrayList first, [DekiScriptParam("second list")] ArrayList second, [DekiScriptParam("expression to determine if item from the first list should be included in final list (return true to include, false to exclude; use '$left' and '$right' to refer to the current item from the first and seconds lists respectively; default: equality condition)", true)] string condition, DekiScriptRuntime runtime ) { ArrayList result = new ArrayList(); if (condition != null) { // loop over both lists and keep items from the first list based on the outcome of the condition DekiScriptExpression expr = DekiScriptParser.Parse(new Location("list.intersect(condition)"), condition); foreach (object left in first) { foreach (object right in second) { DekiScriptEnv env = runtime.CreateEnv(); DekiScriptMap keyvalue = new DekiScriptMap(); keyvalue.Add("left", DekiScriptLiteral.FromNativeValue(left)); keyvalue.Add("right", DekiScriptLiteral.FromNativeValue(right)); env.Vars.Add(DekiScriptRuntime.DEFAULT_ID, keyvalue); DekiScriptLiteral test = runtime.Evaluate(expr, DekiScriptEvalMode.EvaluateSafeMode, env); if (!test.IsNilFalseZero) { result.Add(left); break; } } } } else { // using simple containment check to keep items foreach (object item in first) { if (second.Contains(item)) { result.Add(item); } } } return(result); }
public static object StringEval( [DekiScriptParam("dekiscript expression")] string code, [DekiScriptParam("ignore errors during evaluation", true)] bool?ignoreErrors, DekiScriptRuntime runtime ) { try { return(runtime.Evaluate(DekiScriptParser.Parse(new Location("string.eval(code)"), code), DekiScriptEvalMode.EvaluateSafeMode, runtime.CreateEnv()).NativeValue); } catch (Exception e) { if (ignoreErrors ?? false) { return(e.ToString()); } else { throw; } } }
public Empty Visit(DekiScriptMapConstructor expr, StringBuilder state) { state.Append("{"); bool first = true; foreach (var field in expr.Fields) { if (!first) { state.Append(", "); } else { state.Append(" "); } first = false; // check if key is a string and a valid identifier if (field.Key is DekiScriptString) { string key = ((DekiScriptString)field.Key).Value; if (DekiScriptParser.IsIdentifier(key)) { state.Append(key + " : " + field.Value); } else { state.Append(key.QuoteString() + " : " + field.Value); } } else { state.Append("(" + field.Key + ") : " + field.Value); } } if (expr.Generator != null) { state.Append(" foreach "); state.Append(expr.Generator.ToString()); } state.Append(" }"); return(Empty.Value); }
public static Hashtable MapApply( [DekiScriptParam("map value")] Hashtable map, [DekiScriptParam("expression to apply (use '$' to refer to the item)")] string expression, DekiScriptRuntime runtime ) { DekiScriptExpression expr = DekiScriptParser.Parse(new Location("map.apply(expression)"), expression); Hashtable result = new Hashtable(StringComparer.OrdinalIgnoreCase); foreach (DictionaryEntry entry in map) { DekiScriptMap keyvalue = new DekiScriptMap(); keyvalue.Add("key", DekiScriptLiteral.FromNativeValue(entry.Key)); keyvalue.Add("value", DekiScriptLiteral.FromNativeValue(Eval(entry.Value, runtime))); DekiScriptEnv env = runtime.CreateEnv(); env.Vars.Add(DekiScriptRuntime.DEFAULT_ID, keyvalue); result.Add(entry.Key, runtime.Evaluate(expr, DekiScriptEvalMode.EvaluateSafeMode, env)); } return(result); }
public static object ListReduce( [DekiScriptParam("list value")] ArrayList list, [DekiScriptParam("expression to compute combined value (use '$value' and '$item' to refer to the current value and item, respectively)")] string expression, [DekiScriptParam("starting value (default: nil)", true)] object value, DekiScriptRuntime runtime ) { DekiScriptExpression expr = DekiScriptParser.Parse(new Location("list.reduce(expression)"), expression); foreach (object entry in list) { DekiScriptEnv env = runtime.CreateEnv(); DekiScriptMap values = new DekiScriptMap(); values.Add("value", DekiScriptLiteral.FromNativeValue(value)); values.Add("item", DekiScriptLiteral.FromNativeValue(entry)); env.Vars.Add(DekiScriptRuntime.DEFAULT_ID, values); value = runtime.Evaluate(expr, DekiScriptEvalMode.EvaluateSafeMode, env).NativeValue; } return(value); }
public Empty Visit(DekiScriptMap expr, StringBuilder state) { state.Append("{"); // convert values to an array so they can be sorted var values = new List <KeyValuePair <string, DekiScriptLiteral> >(); foreach (KeyValuePair <string, DekiScriptLiteral> entry in expr.Value) { values.Add(entry); } values.Sort((left, right) => left.Key.CompareInvariantIgnoreCase(right.Key)); // emit values bool first = true; foreach (KeyValuePair <string, DekiScriptLiteral> entry in values) { if (!first) { state.Append(", "); } else { state.Append(" "); } first = false; if (DekiScriptParser.IsIdentifier(entry.Key) || DekiScriptParser.IsNumber(entry.Key)) { state.Append(entry.Key); } else { state.Append(entry.Key.QuoteString()); } state.Append(" : "); entry.Value.VisitWith(this, state); } state.Append(" }"); return(Empty.Value); }
public static int ListCount( [DekiScriptParam("list value")] ArrayList list, [DekiScriptParam("condition to execute for each item (use '$' to refer to the item)")] string condition, DekiScriptRuntime runtime ) { DekiScriptExpression expr = DekiScriptParser.Parse(new Location("list.count(condition)"), condition); int count = 0; foreach (object entry in list) { DekiScriptEnv env = runtime.CreateEnv(); env.Vars.Add(DekiScriptRuntime.DEFAULT_ID, DekiScriptLiteral.FromNativeValue(entry)); DekiScriptLiteral test = runtime.Evaluate(expr, DekiScriptEvalMode.EvaluateSafeMode, env); if (!test.IsNilFalseZero) { ++count; } } return(count); }
public static DekiScriptLiteral Constant(XUri value, DekiScriptLiteral[] items) { DekiScriptLiteral args = DekiScriptNil.Value; if (!ArrayUtil.IsNullOrEmpty(items)) { args = List(items); } else { // BUGBUGBUG (steveb): we should NOT do execution when building the model! if (value.Fragment != null) { DekiScriptExpression expr = DekiScriptParser.TryParse(value.Fragment) ?? DekiScriptNil.Value; args = EvalRuntime.Evaluate(expr, DekiScriptEvalMode.EvaluateSafeMode, new DekiScriptEnv()); } } if ((args.ScriptType != DekiScriptType.NIL) && (args.ScriptType != DekiScriptType.LIST) && (args.ScriptType != DekiScriptType.MAP)) { throw new DekiScriptBadTypeException(Location.None, args.ScriptType, new[] { DekiScriptType.NIL, DekiScriptType.LIST, DekiScriptType.MAP }); } return(new DekiScriptUri(value.WithoutFragment(), args)); }
public static ArrayList ListSort( [DekiScriptParam("list of values")] ArrayList list, [DekiScriptParam("key for value if list contains maps (default: nil)", true)] object key, [DekiScriptParam("sort in reverse order (default: false)", true)] bool?reverse, [DekiScriptParam("compare two items (return -1 if left item less than the right item, 0 if they are equal, and +1 if the left item is greater than the right item; use '$left' and '$right' to refer to the left and right items respectively)", true)] string compare, DekiScriptRuntime runtime ) { // prepare custom comparer IComparer comparer = null; if (compare != null) { comparer = new DekiScriptComparer(runtime, DekiScriptParser.Parse(new Location("list.sort(compare)"), compare)); } // sort list if (key == null || (key as string != null && string.IsNullOrEmpty(key as string))) { list.Sort(comparer); } else { Array keys = (key is ArrayList) ? ((ArrayList)key).ToArray() : ListCollect(list, key.ToString(), runtime).ToArray(); Array values = list.ToArray(); Array.Sort(keys, values, comparer); list = new ArrayList(values); } // check if results need to be reveresed if (reverse ?? false) { list.Reverse(); } return(list); }
public void Dom_evaluation_with_static_content() { XDoc doc = new XDoc("html").UsePrefix("eval", "http://mindtouch.com/2007/dekiscript") .Start("body") .Start("div").Attr("block", "var x = string.toupper('foo')") .Elem("eval:expr", "x .. 3; string.nbsp") .End() .End(); // parse node DekiScriptExpression node = DekiScriptParser.Parse(doc); Assert.AreEqual("<html><body>(discard (var x = string.toupper(\"foo\")); <div>(x .. 3; string.nbsp)</div>) !! web.showerror(__error)</body></html> !! web.showerror(__error)", node.ToString()); // TODO (steveb): disabled the partial evaluation test for now // partial evaluation //node = node.Optimize(DekiScriptEvalMode.Evaluate, DekiScriptEnv.Create()); //Assert.AreEqual("<html><body><div>\"FOO3�\"; </div></body></html>", node.ToString()); // full evaluation //XDoc value = node.Evaluate(DekiScriptEvalMode.Evaluate, DekiScriptEnv.Create()).AsEmbeddableXml(false); //Assert.AreEqual("<html><body><div>FOO3 </div></body></html>", value.ToXHtml()); }
private void Test(string expression) { DekiScriptExpression expr = DekiScriptParser.Parse(Location.Start, expression); Assert.AreEqual(expression, expr.ToString()); }
//--- Class Methods --- public static void StartExtensionService(DekiContext context, ServiceBE service, ServiceRepository.IServiceInfo serviceInfo, bool forceRefresh) { // retrieve document describing the extension functions XUri uri = new XUri(service.Uri); XDoc manifest = null; DekiWikiService deki = context.Deki; var extension = serviceInfo.Extension; if (!service.ServiceLocal) { lock (deki.RemoteExtensionLibraries) { deki.RemoteExtensionLibraries.TryGetValue(uri, out manifest); } } if (manifest == null || forceRefresh) { manifest = Plug.New(uri).Get().ToDocument(); // normalize the extension XML manifest = manifest.TransformAsXml(_extensionConverterXslt); // check if document describes a valid extension: either the extension has no functions, or the functions have end-points if (manifest.HasName("extension") && ((manifest["function"].ListLength == 0) || (manifest["function/uri"].ListLength > 0))) { // add source uri for service manifest.Attr("uri", uri); // register service in extension list lock (deki.RemoteExtensionLibraries) { deki.RemoteExtensionLibraries[uri] = manifest; } } else { throw new ExtensionRemoveServiceInvalidOperationException(uri); } } extension.Manifest = manifest; // add function prefix if one is defined serviceInfo.Extension.SetPreference("namespace.custom", service.Preferences["namespace"]); string serviceNamespace = service.Preferences["namespace"] ?? manifest["namespace"].AsText; if (serviceNamespace != null) { serviceNamespace = serviceNamespace.Trim(); if (string.IsNullOrEmpty(serviceInfo.Namespace)) { // Note (arnec): Namespace from preferences is assigned at service creation. If we do not have one at this // point, it came from the extension manifest and needs to be registered as our default. Otherwise the // preference override persists as the namespace. context.Instance.RunningServices.RegisterNamespace(serviceInfo, serviceNamespace); } if (serviceNamespace.Length != 0) { if (!DekiScriptParser.IsIdentifier(serviceNamespace)) { throw new ExtensionNamespaceInvalidArgumentException(service.Preferences["namespace"] ?? manifest["namespace"].AsText); } } else { serviceNamespace = null; } } serviceNamespace = (serviceNamespace == null) ? string.Empty : (serviceNamespace + "."); // add custom library title extension.SetPreference("title.custom", service.Preferences["title"]); extension.SetPreference("label.custom", service.Preferences["label"]); extension.SetPreference("description.custom", service.Preferences["description"]); extension.SetPreference("uri.logo.custom", service.Preferences["uri.logo"]); extension.SetPreference("functions", service.Preferences["functions"]); extension.SetPreference("protected", service.Preferences["protected"]); // add each extension function bool.TryParse(service.Preferences["protected"], out extension.IsProtected); var functions = new List <ServiceRepository.ExtensionFunctionInfo>(); foreach (XDoc function in manifest["function"]) { XUri functionUri = function["uri"].AsUri; if (functionUri != null) { functions.Add(new ServiceRepository.ExtensionFunctionInfo(serviceNamespace + function["name"].Contents, functionUri)); } } extension.Functions = functions.ToArray(); }
private DekiScriptInvocationTargetDescriptor ConvertFunction(XDoc function) { string functionName = function["name"].AsText; if (string.IsNullOrEmpty(functionName)) { _log.WarnFormat("function without name in script {0}; skipping function definition", _manifestUri); return(null); } // determine function access level DreamAccess access; switch (function["access"].AsText ?? "public") { case "private": access = DreamAccess.Private; break; case "internal": access = DreamAccess.Internal; break; case "public": access = DreamAccess.Public; break; default: _log.WarnFormat("unrecognized access level '{0}' for function {1} in script {2}; defaulting to public", function["access"].AsText, functionName, _manifestUri); access = DreamAccess.Public; break; } // convert parameters List <DekiScriptParameter> parameters = new List <DekiScriptParameter>(); foreach (XDoc param in function["param"]) { string paramName = param["@name"].AsText; // determine if parameter has a default value string paramDefault = param["@default"].AsText; DekiScriptLiteral paramDefaultExpression = DekiScriptNil.Value; bool paramOptional = false; if (paramDefault != null) { paramOptional = true; try { paramDefaultExpression = ScriptRuntime.Evaluate(DekiScriptParser.Parse(Location.Start, paramDefault), DekiScriptEvalMode.Evaluate, ScriptRuntime.CreateEnv()); } catch (Exception e) { _log.ErrorExceptionFormat(e, "invalid default value for parameter {0} in function {1} in script {2}; skipping function definition", paramName, functionName, _manifestUri); return(null); } } else { paramOptional = (param["@optional"].AsText == "true"); } // determine parameter type string paramType = param["@type"].AsText ?? "any"; DekiScriptType paramScriptType; if (!SysUtil.TryParseEnum(paramType, out paramScriptType)) { _log.WarnFormat("unrecognized param type '{0}' for parameter {1} in function {2} in script {3}; defaulting to any", paramType, paramName, functionName, _manifestUri); paramScriptType = DekiScriptType.ANY; } // add parameter parameters.Add(new DekiScriptParameter(paramName, paramScriptType, paramOptional, param.Contents, typeof(object), paramDefaultExpression)); } var parameterArray = parameters.ToArray(); // determine function body XDoc ret = function["return"]; string src = ret["@src"].AsText; string type = ret["@type"].AsText; DekiScriptExpression expression; if (!string.IsNullOrEmpty(src)) { // 'src' attribute is set, load the script from it XDoc script; if (_manifestUri != null) { // check if uri is relative XUri scriptUri = XUri.TryParse(src) ?? _manifestUri.AtPath(src); script = Plug.New(scriptUri).Get().ToDocument(); } else { // check if filename is relative if (!Path.IsPathRooted(src)) { src = Path.Combine(_resourcesPath, src); } script = XDocFactory.LoadFrom(src, MimeType.XML); } expression = DekiScriptParser.Parse(script); type = type ?? "xml"; } else if (!ret["html"].IsEmpty) { // <return> element contains a <html> node; parse it as a script expression = DekiScriptParser.Parse(ret["html"]); type = type ?? "xml"; } else if (!ret.IsEmpty) { // <return> element contains something else; use the text contents as deki-script expression var location = new Location(string.Format("/function[name={0}]/return", functionName)); expression = DekiScriptParser.Parse(location, function["return"].AsText ?? string.Empty); expression = DekiScriptExpression.ReturnScope(location, expression); type = type ?? "any"; } else { _log.WarnFormat("function {0} has no body in script {1}; skipping function definition", functionName, _manifestUri); return(null); } // determine return type DekiScriptType returnScriptType; if (!SysUtil.TryParseEnum(type, out returnScriptType)) { _log.WarnFormat("unrecognized return type '{0}' for function {1} in script {2}; defaulting to any", type, functionName, _manifestUri); returnScriptType = DekiScriptType.ANY; } // create function descriptor var target = new DekiScriptScriptFunctionInvocationTarget(access, parameterArray, expression, _commonEnv, returnScriptType); string description = function["description"].AsText; string transform = function["@transform"].AsText; return(new DekiScriptInvocationTargetDescriptor(access, false, false, functionName, parameterArray, returnScriptType, description, transform, target)); }
//--- Methods --- public DekiScriptExpression Parse(string code) { return(DekiScriptParser.Parse(Location.Start, code)); }
private void Add(DekiExtFunctionAttribute ext, MethodInfo method, XDoc script) { // convert DekiExtParamAttribute into DekiScriptNativeInvocationTarget.Parameter var parameters = from param in method.GetParameters() let attr = (DekiExtParamAttribute[])param.GetCustomAttributes(typeof(DekiExtParamAttribute), false) select((attr != null) && (attr.Length > 0)) ? new DekiScriptNativeInvocationTarget.Parameter(attr[0].Hint, attr[0].Optional) : null; // create native target invocation var target = new DekiScriptNativeInvocationTarget(this, method, parameters.ToArray()); DekiScriptInvocationTargetDescriptor function; // check if implementation is provided by a script instead if (script != null) { var scriptTarget = new DekiScriptExpressionInvocationTarget(target.Access, target.Parameters, DekiScriptParser.Parse(script)); function = new DekiScriptInvocationTargetDescriptor(target.Access, ext.IsProperty, false, ext.Name ?? method.Name, target.Parameters, target.ReturnType, ext.Description, ext.Transform, scriptTarget); } else { function = new DekiScriptInvocationTargetDescriptor(target.Access, ext.IsProperty, false, ext.Name ?? method.Name, target.Parameters, target.ReturnType, ext.Description, ext.Transform, target); } _functions[Self.At(function.SystemName)] = function; }
//--- Class Methods --- public static XmlNode Evaluate( XDoc script, XmlElement node, DekiScriptEvalContext context, DekiScriptEnv env, DekiScriptRuntime runtime, out bool scripted, ref bool error ) { if ((context.Mode != DekiScriptEvalMode.Verify) && (context.Mode != DekiScriptEvalMode.EvaluateEditOnly) && (context.Mode != DekiScriptEvalMode.EvaluateSaveOnly)) { throw new InvalidOperationException("DekiScript interpreter can only used for save, edit, or verify evaluations."); } scripted = false; XmlNode next = node.NextSibling; try { // check if element needs to be evaluated if (StringUtil.EqualsInvariant(node.NamespaceURI, XDekiScript.ScriptNS)) { scripted = true; // NOTE (steveb): <eval:xyz> nodes are not processed by the interpreter anymore } else { XDoc current = script[node]; bool hasScriptClass = StringUtil.EqualsInvariant(node.GetAttribute("class"), "script"); #region <elem class="script" init="..." if="..." foreach="..." where="..." block="..."> // check if element has form <elem class="script" init="..." if="..." foreach="..." where="..." block="..."> scripted = node.HasAttribute("init") || node.HasAttribute("if") || node.HasAttribute("foreach") || node.HasAttribute("block"); if (context.Mode == DekiScriptEvalMode.Verify) { // check if "block" is present string blockAttr = node.GetAttribute("block"); if (!string.IsNullOrEmpty(blockAttr)) { // TODO (steveb): validate script expression } // check if "foreach" is present string foreachAttr = node.GetAttribute("foreach"); if (!string.IsNullOrEmpty(foreachAttr)) { // TODO (steveb): validate script expression } // check if "if" is present string ifAttr = node.GetAttribute("if"); if (!string.IsNullOrEmpty(ifAttr)) { // TODO (steveb): validate script expression } // check if "init" is present string initAttr = node.GetAttribute("init"); if (!string.IsNullOrEmpty(initAttr)) { // TODO (steveb): validate script expression } } #endregion // evaluate child nodes EvaluateChildren(script, node, context, env, runtime, out scripted, ref error); #region evaluate attributes for (int i = 0; i < node.Attributes.Count; ++i) { XmlAttribute attribute = node.Attributes[i]; // check if attribute needs to be evaluated if (attribute.NamespaceURI == XDekiScript.ScriptNS) { scripted = true; // NOTE (steveb): eval:xyz="abc" attributes are not processed by the interpreter anymore } else if (StringUtil.StartsWithInvariant(attribute.Value, "{{") && StringUtil.EndsWithInvariant(attribute.Value, "}}")) { scripted = true; // NOTE (steveb): key="{{value}}" string code = attribute.Value.Substring(2, attribute.Value.Length - 4).Trim(); // check if script content is substituted bool isPermanentReplacement = false; if (StringUtil.StartsWithInvariantIgnoreCase(code, DekiScriptRuntime.ON_SAVE_PATTERN)) { isPermanentReplacement = (context.Mode == DekiScriptEvalMode.EvaluateSaveOnly); code = code.Substring(DekiScriptRuntime.ON_SAVE_PATTERN.Length); } else if (StringUtil.StartsWithInvariantIgnoreCase(code, DekiScriptRuntime.ON_SUBST_PATTERN)) { isPermanentReplacement = (context.Mode == DekiScriptEvalMode.EvaluateSaveOnly); code = code.Substring(DekiScriptRuntime.ON_SUBST_PATTERN.Length); } else if (StringUtil.StartsWithInvariantIgnoreCase(code, DekiScriptRuntime.ON_EDIT_PATTERN)) { isPermanentReplacement = (context.Mode == DekiScriptEvalMode.EvaluateEditOnly); code = code.Substring(DekiScriptRuntime.ON_EDIT_PATTERN.Length); } // parse expression if ((context.Mode == DekiScriptEvalMode.Verify) || isPermanentReplacement) { DekiScriptExpression expression = DekiScriptParser.Parse(ComputeNodeLocation(attribute), code); if (isPermanentReplacement) { DekiScriptLiteral eval = runtime.Evaluate(expression, DekiScriptEvalMode.EvaluateSafeMode, env); // determine what the outcome value is string value = eval.AsString(); // check if we have a value to replace the current attribute with if ((value != null) && !DekiScriptLibrary.ContainsXSSVulnerability(attribute.LocalName, value)) { attribute.Value = value; } else { node.Attributes.RemoveAt(i); --i; } } } } } #endregion #region evaluate <span class="script"> or <pre class="script"> if (hasScriptClass && (StringUtil.EqualsInvariant(node.LocalName, "pre") || StringUtil.EqualsInvariant(node.LocalName, "span")) && !node.HasAttribute("function")) { // replace the non-breaking space character with space string code = node.InnerText.ReplaceAll("\u00A0", " ", "\u00AD", "").Trim(); // check if script content is substituted bool isPermanentReplacement = false; if (StringUtil.StartsWithInvariantIgnoreCase(code, DekiScriptRuntime.ON_SAVE_PATTERN)) { isPermanentReplacement = (context.Mode == DekiScriptEvalMode.EvaluateSaveOnly); code = code.Substring(DekiScriptRuntime.ON_SAVE_PATTERN.Length); } else if (StringUtil.StartsWithInvariantIgnoreCase(code, DekiScriptRuntime.ON_SUBST_PATTERN)) { isPermanentReplacement = (context.Mode == DekiScriptEvalMode.EvaluateSaveOnly); code = code.Substring(DekiScriptRuntime.ON_SUBST_PATTERN.Length); } else if (StringUtil.StartsWithInvariantIgnoreCase(code, DekiScriptRuntime.ON_EDIT_PATTERN)) { isPermanentReplacement = (context.Mode == DekiScriptEvalMode.EvaluateEditOnly); code = code.Substring(DekiScriptRuntime.ON_EDIT_PATTERN.Length); } // parse expression if ((context.Mode == DekiScriptEvalMode.Verify) || isPermanentReplacement) { DekiScriptExpression expression = DekiScriptParser.Parse(ComputeNodeLocation(node), code); if (isPermanentReplacement) { DekiScriptLiteral value = runtime.Evaluate(expression, DekiScriptEvalMode.EvaluateSafeMode, env); context.ReplaceNodeWithValue(node, value); } if (!isPermanentReplacement) { scripted = true; } } } #endregion } } catch (Exception e) { // only embed error in verify mode, not in save/edit modes if (context.Mode == DekiScriptEvalMode.Verify) { context.InsertExceptionMessageBeforeNode(env, node.ParentNode, node, ComputeNodeLocation(node), e); node.ParentNode.RemoveChild(node); } error |= true; } return(next); }