private DekiScriptExpressionEvaluationState(DekiScriptEnv env, DekiScriptRuntime runtime, XmlNamespaceManager namespaces, DekiScriptOutputBuffer buffer, SharedState sharedState) { this.Env = env; this.Namespaces = namespaces; this.Buffer = buffer; this.Runtime = runtime; _sharedState = sharedState; }
protected override Yield Start(XDoc config, Result result) { yield return Coroutine.Invoke(base.Start, config, new Result()); // init environment for script execution _runtime = new DekiScriptRuntime(); _env = _runtime.CreateEnv(); result.Return(); }
public DekiScriptExpressionEvaluationState(DekiScriptEvalMode mode, DekiScriptEnv env, DekiScriptRuntime runtime, XmlNamespaceManager namespaces, DekiScriptOutputBuffer buffer) { this.Mode = mode; this.Env = env; this.Namespaces = namespaces; this.Buffer = buffer; this.Runtime = runtime; _safe = (mode == DekiScriptEvalMode.EvaluateSafeMode); }
//--- Constructor --- public DekiScriptExpressionEvaluationState(DekiScriptEvalMode mode, DekiScriptEnv env, DekiScriptRuntime runtime) { this.Mode = mode; this.Env = env; this.Namespaces = new XmlNamespaceManager(XDoc.XmlNameTable); this.Buffer = new DekiScriptOutputBuffer(); this.Runtime = runtime; _safe = (mode == DekiScriptEvalMode.EvaluateSafeMode); }
//--- Constructor --- public DekiScriptExpressionEvaluationState(DekiScriptEvalMode mode, DekiScriptEnv env, DekiScriptRuntime runtime, TimeSpan evaluationTimeout, int maxOutputBufferSize) { this.Env = env; this.Namespaces = new XmlNamespaceManager(XDoc.XmlNameTable); this.Buffer = new DekiScriptOutputBuffer(maxOutputBufferSize); this.Runtime = runtime; _sharedState = new SharedState(); _sharedState.Safe = (mode == DekiScriptEvalMode.EvaluateSafeMode); if(evaluationTimeout == TimeSpan.MaxValue) { return; } _sharedState.EvaluationTimeout = evaluationTimeout; _sharedState.EvaluationTimer = Stopwatch.StartNew(); }
//--- Constructors --- public DekiScriptScriptFunctionInvocationTarget(DreamAccess access, DekiScriptParameter[] parameters, DekiScriptExpression expr, DekiScriptEnv env, DekiScriptType returnType) { if(parameters == null) { throw new ArgumentNullException("parameters"); } if(expr == null) { throw new ArgumentNullException("expr"); } this.Access = access; this.Parameters = parameters; this.Expression = expr; _env = env; _returnType = returnType; }
//--- Class Methods --- public static string Parse(string code, string id, DekiScriptEnv env, DekiScriptRuntime runtime) { StringBuilder result = new StringBuilder(); // check if code is attached to an id'ed element if(!string.IsNullOrEmpty(id)) { result.AppendLine("$(\"#" + id + "\").each(function() {"); result.AppendLine("var $this = $(this);"); } // convert JEM code into regular javascript int i = 0; result.Append(ParseExpression(code, id, ParseMode.ALL, false, env, runtime, new Dictionary<string, string>(), ref i)); // check if code is attached to an id'ed element if(!string.IsNullOrEmpty(id)) { result.AppendLine("});"); } return result.ToString(); }
private void LoadScript() { _manifestPath = null; _resourcesPath = null; _manifestUri = null; _resourcesUri = null; _manifest = null; // read manifest _manifestPath = Config["manifest"].AsText; if(string.IsNullOrEmpty(_manifestPath)) { throw new ArgumentNullException("manifest"); } _manifestUri = XUri.TryParse(_manifestPath); if(_manifestUri != null) { _manifestPath = null; _manifest = Plug.New(_manifestUri).Get().ToDocument(); _resourcesUri = Config["resources"].AsUri ?? _manifestUri.WithoutLastSegment(); } else { _manifest = XDocFactory.LoadFrom(_manifestPath, MimeType.XML); _resourcesPath = Config["resources"].AsText ?? Path.GetDirectoryName(_manifestPath); } if(!_manifest.HasName("extension")) { throw new ArgumentException("invalid extension manifest"); } // initilize runtime _runtime = new DekiScriptRuntime(); // read manifest settings _title = _manifest["title"].AsText; _label = _manifest["label"].AsText; _copyright = _manifest["copyright"].AsText; _description = _manifest["description"].AsText; _help = _manifest["uri.help"].AsText; _logo = _manifest["uri.logo"].AsText; _namespace = _manifest["namespace"].AsText; // initialize evaluation environment _commonEnv = _runtime.CreateEnv(); // read functions _functions = new Dictionary<XUri, DekiScriptInvocationTargetDescriptor>(); foreach(var function in _manifest["function"]) { var descriptor = ConvertFunction(function); if(descriptor != null) { var uri = Self.At(descriptor.SystemName); DekiScriptInvocationTargetDescriptor old; if(_functions.TryGetValue(uri, out old)) { _log.WarnFormat("duplicate function name {0} in script {1}", descriptor.Name, _manifestUri); } _functions[uri] = descriptor; } } _runtime.RegisterExtensionFunctions(_functions); // add extension functions to env foreach(var function in _functions) { _commonEnv.Vars.AddNativeValueAt(function.Value.Name.ToLowerInvariant(), function.Key); } // add configuration settings DreamContext context = DreamContext.Current; DekiScriptMap scriptConfig = new DekiScriptMap(); foreach(KeyValuePair<string, string> entry in Config.ToKeyValuePairs()) { XUri local; if(XUri.TryParse(entry.Value, out local)) { local = context.AsPublicUri(local); scriptConfig.AddAt(entry.Key.Split('/'), DekiScriptExpression.Constant(local.ToString())); } else { scriptConfig.AddAt(entry.Key.Split('/'), DekiScriptExpression.Constant(entry.Value)); } } _commonEnv.Vars.Add("config", scriptConfig); }
protected override Yield Stop(Result result) { _publicDigitalSignature = null; _manifestPath = null; _resourcesPath = null; _manifestUri = null; _resourcesUri = null; _manifest = null; _debug = false; _runtime = null; _commonEnv = null; _copyright = null; _description = null; _functions = null; _help = null; _label = null; _logo = null; _namespace = null; _title = null; yield return Coroutine.Invoke(base.Stop, new Result()); result.Return(); }
//--- Class Constructor --- static DekiScriptRuntime() { // register built-in functions _commonFunctions = new Dictionary<XUri, DekiScriptInvocationTargetDescriptor>(); foreach(MethodInfo method in typeof(DekiScriptLibrary).GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy)) { // check if it has the DekiScriptFunction attribute DekiScriptFunctionAttribute functionAttribute = (DekiScriptFunctionAttribute)Attribute.GetCustomAttribute(method, typeof(DekiScriptFunctionAttribute)); if(functionAttribute != null) { var parameters = from param in method.GetParameters() let attr = (DekiScriptParamAttribute[])param.GetCustomAttributes(typeof(DekiScriptParamAttribute), false) select ((attr != null) && (attr.Length > 0)) ? new DekiScriptNativeInvocationTarget.Parameter(attr[0].Hint, attr[0].Optional) : null; var target = new DekiScriptNativeInvocationTarget(null, method, parameters.ToArray()); var function = new DekiScriptInvocationTargetDescriptor(target.Access, functionAttribute.IsProperty, functionAttribute.IsIdempotent, functionAttribute.Name ?? method.Name, target.Parameters, target.ReturnType, functionAttribute.Description, functionAttribute.Transform, target); _commonFunctions[new XUri("native:///").At(function.SystemName)] = function; } } // build common env DekiScriptMap common = new DekiScriptMap(); // add global constants common.AddNativeValueAt("num.e", Math.E); common.AddNativeValueAt("num.pi", Math.PI); common.AddNativeValueAt("num.epsilon", double.Epsilon); common.AddNativeValueAt("num.positiveinfinity", double.PositiveInfinity); common.AddNativeValueAt("num.negativeinfinity", double.NegativeInfinity); common.AddNativeValueAt("num.nan", double.NaN); // add global functions & properties foreach(var function in _commonFunctions) { common.AddNativeValueAt(function.Value.Name, function.Key); } _commonEnv = new DekiScriptEnv(common); }
public virtual DekiScriptLiteral Invoke(XUri uri, DekiScriptLiteral args, DekiScriptEnv env) { DekiScriptInvocationTargetDescriptor descriptor; var target = _functions.TryGetValue(uri, out descriptor) ? descriptor.Target : FindTarget(uri); // invoke function directly try { return target.Invoke(this, args); } catch(Exception e) { throw UnwrapAsyncException(uri, e).Rethrow(); } }
public virtual DekiScriptLiteral EvaluateProperty(Location location, DekiScriptLiteral value, DekiScriptEnv env) { if(IsProperty(value)) { DekiScriptUri uri = (DekiScriptUri)value; try { value = Invoke(uri.Value, uri.Arguments.IsNil ? _emptyList : uri.Arguments, env); } catch(DekiScriptFatalException) { throw; } catch(Exception e) { var descriptor = ResolveRegisteredFunctionUri(uri.Value); throw new DekiScriptInvokeException(location, uri.Value, (descriptor != null) ? descriptor.Name : uri.Value.ToString(), e); } } return value; }
public virtual DekiScriptLiteral Evaluate(DekiScriptExpression expr, DekiScriptEvalMode mode, DekiScriptEnv env) { DekiScriptExpressionEvaluationState state = new DekiScriptExpressionEvaluationState(mode, env, this); try { return state.Pop(expr.VisitWith(DekiScriptExpressionEvaluation.Instance, state)); } catch(DekiScriptReturnException e) { state.Push(e.Value); return state.PopAll(); } }
//--- Extension Methods --- public static DekiScriptExpression Optimize(this DekiScriptRuntime runtime, DekiScriptExpression expr, DekiScriptEvalMode mode, DekiScriptEnv env) { return expr.VisitWith(DekiScriptExpressionOptimizer.Instance, new DekiScriptExpressionEvaluationState(mode, env, runtime)); }
public void EvaluateContent(XDoc script, DekiScriptEnv env, DekiScriptEvalMode mode, out bool scripted) { scripted = false; switch(mode) { case DekiScriptEvalMode.None: break; case DekiScriptEvalMode.Evaluate: case DekiScriptEvalMode.EvaluateSafeMode: { var expr = DekiScriptParser.Parse(script); try { expr = DekiContext.Current.Instance.ScriptRuntime.Evaluate(expr, mode, env); } catch(Exception e) { var error = DekiScriptLibrary.MakeErrorObject(e, env); DekiContext.Current.Instance.ScriptRuntime.LogExceptionInOutput(error); expr = DekiScriptExpression.Constant(DekiScriptLibrary.WebShowError((Hashtable)error.NativeValue)); } // check if outcome is an XML document var xml = expr as DekiScriptXml; if(xml != null) { // check if outcome is the expected content document if(xml.Value.HasName("content")) { script.Replace(((DekiScriptXml)expr).Value); } else { // remove all contents from existing document and append new document script.RemoveNodes(); script.Start("body").Add(xml.Value).End(); } } else if(expr is DekiScriptString) { // remove all contents from existing document and append new document script.RemoveNodes(); script.Start("body").Value(((DekiScriptString)expr).Value).End(); } else { // remove all contents from existing document and append new document script.RemoveNodes(); script.Start("body").Value(expr.ToString()).End(); } } break; case DekiScriptEvalMode.EvaluateEditOnly: case DekiScriptEvalMode.EvaluateSaveOnly: case DekiScriptEvalMode.Verify: { DekiScriptEvalContext context = new DekiScriptEvalContext(script, mode, false, DekiContext.Current.Instance.ScriptRuntime.GetMaxOutputSize(mode)); // add <head> and <tail> sections context.AddHeadElements(script); context.AddTailElements(script); script["head"].RemoveNodes(); script["tail"].RemoveNodes(); // evaluate the script bool error = false; DekiScriptInterpreter.Evaluate(script, (XmlElement)script.Root.AsXmlNode, context, env, DekiContext.Current.Instance.ScriptRuntime, out scripted, ref error); if((mode == DekiScriptEvalMode.Verify) || !error) { context.MergeContextIntoDocument(script.AsXmlNode.OwnerDocument); } } break; default: throw new InvalidOperationException(string.Format("unrecognized evaluation mode '{0}'", mode)); } }
internal void InsertExceptionMessageBeforeNode(DekiScriptEnv env, XmlNode parent, XmlNode reference, Location location, Exception exception) { if(Fallthrough) { throw exception; } if(exception is DekiScriptDocumentTooLargeException) { DekiScriptDocumentTooLargeException e = (DekiScriptDocumentTooLargeException)exception; // check if an error message was already embedded if(e.Handled) { throw exception; } e.Handled = true; } // check if environment has a __callstack variable DekiScriptList callstack = null; DekiScriptLiteral callstackVar; if(env.Vars.TryGetValue(DekiScriptEnv.CALLSTACK, out callstackVar)) { callstack = callstackVar as DekiScriptList; } XDoc warning = DekiScriptRuntime.CreateWarningFromException(callstack, location, exception); parent.InsertBefore(parent.OwnerDocument.ImportNode(warning.AsXmlNode, true), reference); if(exception is DekiScriptDocumentTooLargeException) { throw exception; } }
private void InitScriptRuntimeAndEnv() { if(_runtime != null) { return; } // Note (arnec): have to lazily init the runtime and env, because they require instance settings and they currently have // a requirement of DekiContext.Current.Instance being set, i.e. a race condition in the ctor lock(SettingsSyncRoot) { if(_runtime != null) { return; } _runtime = new ExtensionRuntime(new DekiInstanceSettings()); _scriptEnv = _deki.InitializeRuntimeAndEnvironment(_runtime); } }
private static void BuildWhenStatement(string expr, string id, string body, StringBuilder head, DekiScriptEnv env, Dictionary<string, string> channels) { // remove the optional outer () and {}; the expression is already scoped, so we don't need them anymore if(expr.StartsWith("(") && expr.EndsWith(")")) { expr = expr.Substring(1, expr.Length - 2).Trim(); } // gather all events from 'when()' condition expression into a map { sink1: [ event1, event2, ... ], sink2: [ event1, event2, ... ], ... } Dictionary<string, Dictionary<string, string>> sinks = new Dictionary<string, Dictionary<string, string>>(); string condition = EVENT_PATTERN.Replace(expr, delegate(Match match) { // check if a tail element was matched, which disqualifies this match Group tail = match.Groups["tail"]; if(!tail.Success) { // check for event match Group group = match.Groups["event"]; if(group.Success) { string sink = match.Groups["sink"].Value; Dictionary<string, string> events; if(!sinks.TryGetValue(sink, out events)) { events = new Dictionary<string, string>(); sinks[sink] = events; } events[group.Value] = group.Value; // TODO (steveb): we should also check that the event source is what we expect it to be return string.Format("($event.type == {0})", StringUtil.QuoteString(group.Value)); } } else { string sink = match.Groups["sink"].Value; if(sink.StartsWith("#")) { return string.Format("$(\"{0}\").{1}{2}", sink, match.Groups["event"].Value, tail.Value); } } return match.Value; }); // create stub function; $event is only set when invoked for events, not for messages string function = "_" + StringUtil.CreateAlphaNumericKey(8); if(sinks.Count > 0) { head.Append("var " + function + " = function($event) { $event = $event||{}; "); } else { head.Append("var " + function + " = function() { "); } // read channel state for all channels that a read from; including 'when()' condition and body if(channels.Count > 0) { bool first = true; head.Append("var $channels = {"); foreach(KeyValuePair<string, string> channel in channels) { if(!first) { head.Append(", "); } first = false; head.AppendFormat("{0}: Deki.query({1})||{{}}", channel.Key.QuoteString(), channel.Value); } head.Append(" }; "); } // add optional condition if(!string.IsNullOrEmpty(condition)) { head.Append("if(" + condition + ") "); } // append body of 'when()' statement and close the function if(string.IsNullOrEmpty(body)) { head.Append(";"); } else { head.Append(body); } head.AppendLine(" };"); // register function for event handlers if(sinks.Count > 0) { foreach(KeyValuePair<string, Dictionary<string, string>> events in sinks) { string bind = string.Join(" ", new List<string>(events.Value.Values).ToArray()).QuoteString(); if(events.Key.EqualsInvariant("$this")) { head.AppendLine("$this.bind(" + bind + ", " + function + ");"); } else { head.AppendLine("$(" + events.Key.QuoteString() + ").bind(" + bind + ", " + function + ");"); } } } // register function for message handlers foreach(KeyValuePair<string, string> channel in channels) { head.AppendLine("Deki.subscribe(" + channel.Value + ", " + (string.IsNullOrEmpty(id) ? "null" : "this") + ", " + function + ");"); } }
private static bool TryParseDekiScriptExpression(string ctor, DekiScriptEnv env, DekiScriptRuntime runtime, ref int i, out string value) { string source = ParseExpression(ctor, null, ParseMode.EXPRESSION, false, env, runtime, null, ref i); if((i >= ctor.Length) || (ctor[i] != '}')) { value = null; return false; } // try to parse and execute the dekiscript fragment try { source = source.Substring(1, source.Length - 2); DekiScriptExpression dekiscript = DekiScriptParser.Parse(new Location("jem"), source); DekiScriptLiteral result = runtime.Evaluate(dekiscript, DekiScriptEvalMode.EvaluateSafeMode, env); value = DekiScriptLibrary.JsonEmit(result.NativeValue); } catch(Exception e) { // execution failed; convert exception into a javascript comment value = "alert(\"ERROR in DekiScript expression:\\n---------------------------\\n\\n\" + " + e.GetCoroutineStackTrace().QuoteString() + ")"; } return true; }
private static string ParseExpression(string code, string id, ParseMode mode, bool whenCondition, DekiScriptEnv env, DekiScriptRuntime runtime, Dictionary<string, string> channels, ref int i) { StringBuilder result = new StringBuilder(); int nesting = 0; for(; i < code.Length; ++i) { int start; switch(code[i]) { case '"': case '\'': // process strings start = i; ScanString(code, code[i], ref i); result.Append(code, start, i - start); --i; break; case '/': // check if / denotes the beginning of a comment, if so process it start = i; if(TryScanComment(code, ref i)) { // NOTE: remove comments in when-condition if(!whenCondition) { result.Append(code, start, i - start); result.Append("\n"); } --i; } else { result.Append(code[i]); } break; case '\\': // backslash (\) always appends the next character result.Append(code[i++]); if(i < code.Length) { result.Append(code[i]); } break; case '(': // increase nesting level result.Append(code[i]); ++nesting; break; case '{': // check if this is the beginning of a dekiscript block {{ }} if(((i + 1) < code.Length) && (code[i + 1] == '{')) { ++i; string value; start = i; if(TryParseDekiScriptExpression(code, env, runtime, ref i, out value)) { result.Append(value); } else { ++nesting; result.Append('{'); result.Append(code, start, i - start); --i; } } else { ++nesting; result.Append(code[i]); } break; case ')': case '}': // decrease nesting level and check if this is the end of the sougth expression result.Append(code[i]); --nesting; // NOTE: only exit if // 1) we don't have to read all of the code // 2) there are no open parentheses or cruly braces // 3) we don't on a complete statement or the current characteris a closing curly brace if((mode != ParseMode.ALL) && (nesting <= 0) && ((mode != ParseMode.STATEMENT) || (code[i] == '}'))) { // found the end of the expression ++i; return result.ToString(); } break; case ';': // check if the statement is the end of the sougth expression result.Append(code[i]); // NOTE: only exit if // 1) we don't have to read all of the code // 2) there are no open parentheses or cruly braces // 3) we stop on a complete statement if((nesting <= 0) && (mode == ParseMode.STATEMENT)) { // found the end of the expression ++i; return result.ToString(); } break; case '@': // channel name if(channels != null) { ++i; start = i; string channel; string name; if((i < code.Length) && ((code[i] == '"') || (code[i] == '\''))) { // process: @"channel_name" or @'channel_name' ScanString(code, code[i], ref i); channel = code.Substring(start, i - start); name = channel.Substring(1, channel.Length - 2).UnescapeString(); } else { // process: @channel_magic_id ScanId(code, ref i); name = code.Substring(start, i - start); if(!channels.TryGetValue(name, out channel)) { channel = env.GetMagicId(name).ToString(); } } start = i; ScanWhitespace(code, ref i); if((i < code.Length) && (code[i] == '(')) { // process: @channel ( ... ) string message = ParseExpression(code, id, ParseMode.EXPRESSION, false, env, runtime, channels, ref i); message = message.Substring(1, message.Length - 2).Trim(); if(message.Length == 0) { result.AppendFormat("Deki.publish({0})", channel); } else { result.AppendFormat("Deki.publish({0}, {1})", channel, message); } } else { // channel is used for reading; add it to the channel set to read on activation channels[name] = channel; // convert channel name and add whitespace result.AppendFormat("$channels[{0}]", name.QuoteString()); result.Append(code, start, i - start); } --i; } else { result.Append(code[i]); } break; case '#': // NOTE: don't process #id in the when-condition // element name if(!whenCondition && (channels != null)) { ++i; start = i; // process: #id ScanId(code, ref i); string name = code.Substring(start, i - start); result.Append("$(\"#" + name + "\")"); --i; } else { result.Append(code[i]); } break; default: // NOTE: don't process when() in the when-condition // check if this is the beginning of an identifier if(!whenCondition && IsAlpha(code[i])) { start = i; ScanId(code, ref i); int j = i; ScanWhitespace(code, ref j); // check if scanned identifier is the keyword 'when' if(((i - start) == WHEN.Length) && (string.Compare(code, start, WHEN, 0, WHEN.Length, StringComparison.Ordinal) == 0) && (j < code.Length) && (code[j] == '(')) { i = j; Dictionary<string, string> subChannels = new Dictionary<string, string>(); // parse the condition of the 'when()' statement string condition = ParseExpression(code, id, ParseMode.EXPRESSION, true, env, runtime, subChannels, ref i); // parse the body of the 'when()' expression string body = ParseExpression(code, id, ParseMode.STATEMENT, false, env, runtime, subChannels, ref i); BuildWhenStatement(condition.Trim(), id, body.Trim(), result, env, subChannels); } else { result.Append(code, start, i - start); } --i; } else { result.Append(code[i]); } break; } } return result.ToString(); }
//--- 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; }
//--- Methods --- public DekiScriptExpressionEvaluationState With(DekiScriptEnv env) { return new DekiScriptExpressionEvaluationState(env, Runtime, Namespaces, Buffer, _sharedState); }
private static void EvaluateChildren( XDoc script, XmlElement node, DekiScriptEvalContext context, DekiScriptEnv env, DekiScriptRuntime runtime, out bool scripted, ref bool error ) { scripted = false; // recurse first to evaluate nested script content XmlNode child = node.FirstChild; while(child != null) { XmlNode next = child.NextSibling; if(child.NodeType == XmlNodeType.Element) { bool childScripted; next = Evaluate(script, (XmlElement)child, context, env, runtime, out childScripted, ref error); scripted = scripted || childScripted; } child = next; } }
//--- Methods --- public DekiScriptExpressionEvaluationState With(DekiScriptEnv env) { return new DekiScriptExpressionEvaluationState(Mode, env, Runtime, Namespaces, Buffer); }
public virtual DekiScriptLiteral Invoke(Location location, XUri uri, DekiScriptLiteral args, DekiScriptEnv env) { var sw = Stopwatch.StartNew(); DekiScriptInvocationTargetDescriptor descriptor; var target = _functions.TryGetValue(uri, out descriptor) ? descriptor.Target : FindTarget(uri); try { // invoke function directly return target.Invoke(this, args); } catch(Exception e) { throw UnwrapAsyncException(uri, e).Rethrow(); } finally { sw.Stop(); bool property = (uri.LastSegment ?? string.Empty).StartsWithInvariant("$"); env.AddFunctionProfile(location, (descriptor != null) ? (property ? "$" : "") + descriptor.Name : uri.ToString(), sw.Elapsed); } }