public static T EvaluateBindingAs <T>(TemplateScopeContext scope, JsBinding binding) { var result = EvaluateBinding(scope, binding); var converted = result.ConvertTo <T>(); return(converted); }
/// <summary> /// Creates a new <see cref="JsExecutionContext"/> instance. /// </summary> public JsExecutionContext(JavaScriptRuntime runtime) { _runtime = runtime; _scope = new JsContextScope(_runtime.CreateContext()); _binder = new JsBinder(_scope); _interop = new JsInterop(this, _scope, _binder); _scope.Run(() => { _global = new JsBinding(_scope, _binder, _interop, JavaScriptValue.GlobalObject); }); }
public void Does_parse_linq_examples() { ConditionExpression expr; var it = new JsBinding("it"); "it < 5".ParseConditionExpression(out expr); Assert.That(expr, Is.EqualTo(new BinaryExpression(it, JsLessThan.Operand, new JsConstant(5)))); "it.UnitsInStock > 0 and it.UnitPrice > 3".ParseConditionExpression(out expr); Assert.That(expr, Is.EqualTo( new AndExpression( new BinaryExpression(new JsExpression("it.UnitsInStock"), JsGreaterThan.Operand, new JsConstant(0)), new BinaryExpression(new JsExpression("it.UnitPrice"), JsGreaterThan.Operand, new JsConstant(3)) ) )); }
/// <summary> /// Creates a new <see cref="JsModule"/> instance. /// </summary> public JsModule(JsContextScope scope, JsBinder binder, JsInterop interop, string moduleId) { _scope = scope; _binder = binder; _interop = interop; ModuleId = moduleId; // Create JS Representation Module = _scope.Run(() => { var jsValue = JavaScriptValue.CreateObject(); jsValue.AddRef(); return(new JsBinding(_scope, _binder, _interop, jsValue)); }); }
private static Expression CreateStringIndexExpression(Expression body, JsBinding binding, ParameterExpression scope, Expression valueExpr, ref Type currType) { body = Expression.Call(body, typeof(string).GetMethod("ToCharArray", Type.EmptyTypes)); currType = typeof(char[]); if (binding != null) { var evalAsInt = typeof(TemplatePageUtils).GetStaticMethod(nameof(EvaluateBindingAs)) .MakeGenericMethod(typeof(int)); body = Expression.ArrayIndex(body, Expression.Call(evalAsInt, scope, Expression.Constant(binding))); } else { body = Expression.ArrayIndex(body, valueExpr); } return(body); }
/// <inheritdoc/> public T GetExportedValue <T>(string name) { return(_scope.Run(() => { if (null == _exports) { if (!Module.HasValue("exports")) { return default(T); } var exports = Module.GetValue("exports"); _exports = new JsBinding(_scope, _binder, _interop, exports); } return _exports.GetValue <T>(name); })); }
public void Does_parse_not_unary_expression() { ConditionExpression expr; var it = new JsBinding("it"); "!it".ParseConditionExpression(out expr); Assert.That(expr, Is.EqualTo(new BinaryExpression( new UnaryExpression(JsNot.Operator, it), JsEquals.Operand, JsConstant.True))); "!contains(items, it)".ParseConditionExpression(out expr); Assert.That(expr, Is.EqualTo(new BinaryExpression( new UnaryExpression(JsNot.Operator, new JsExpression("contains") { Args = { "items".ToStringSegment(), "it".ToStringSegment() } }), JsEquals.Operand, JsConstant.True))); }
public static StringSegment ParseNextToken(this StringSegment literal, out object value, out JsBinding binding, bool allowWhitespaceSyntax) { binding = null; value = null; var c = (char)0; if (literal.IsNullOrEmpty()) { return(TypeConstants.EmptyStringSegment); } var i = 0; literal = literal.AdvancePastWhitespace(); var firstChar = literal.GetChar(0); if (firstChar == '\'' || firstChar == '"' || firstChar == '`') { i = 1; var hasEscapeChar = false; while (i < literal.Length && ((c = literal.GetChar(i)) != firstChar || literal.GetChar(i - 1) == '\\')) { i++; if (!hasEscapeChar) { hasEscapeChar = c == '\\'; } } if (i >= literal.Length || literal.GetChar(i) != firstChar) { throw new ArgumentException($"Unterminated string literal: {literal}"); } var str = literal.Substring(1, i - 1); value = str; if (hasEscapeChar) { var sb = StringBuilderCache.Allocate(); for (var j = 0; j < str.Length; j++) { // strip the back-slash used to escape quote char in strings var ch = str[j]; if (ch != '\\' || (j + 1 >= str.Length || str[j + 1] != firstChar)) { sb.Append(ch); } } value = StringBuilderCache.ReturnAndFree(sb); } return(literal.Advance(i + 1)); } if (firstChar >= '0' && firstChar <= '9' || (literal.Length >= 2 && (firstChar == '-' || firstChar == '+') && literal.GetChar(1).IsNumericChar())) { i = 1; var hasExponent = false; var hasDecimal = false; while (i < literal.Length && IsNumericChar(c = literal.GetChar(i)) || (hasExponent = (c == 'e' || c == 'E'))) { if (c == '.') { hasDecimal = true; } i++; if (hasExponent) { i += 2; // [e+1]0 while (i < literal.Length && IsNumericChar(literal.GetChar(i))) { i++; } break; } } var numLiteral = literal.Subsegment(0, i); //don't convert into ternary to avoid Type coercion if (hasDecimal || hasExponent) { value = numLiteral.TryParseDouble(out double d) ? d : default(double); } else { value = numLiteral.ParseSignedInteger(); } return(literal.Advance(i)); } if (firstChar == '{') { var map = new Dictionary <string, object>(); literal = literal.Advance(1); while (!literal.IsNullOrEmpty()) { literal = literal.AdvancePastWhitespace(); if (literal.GetChar(0) == '}') { literal = literal.Advance(1); break; } literal = literal.ParseNextToken(out object mapKeyString, out JsBinding mapKeyVar); if (mapKeyVar is JsExpression) { throw new NotSupportedException($"JsExpression '{mapKeyVar?.Binding}' is not a valid Object key."); } var mapKey = mapKeyVar != null ? mapKeyVar.Binding.Value : (string)mapKeyString; if (mapKey != null) { literal = literal.AdvancePastWhitespace(); if (literal.Length > 0 && literal.GetChar(0) == ':') { literal = literal.Advance(1); literal = literal.ParseNextToken(out object mapValue, out JsBinding mapValueBinding); map[mapKey] = mapValue ?? mapValueBinding; } else //shorthand notation { if (literal.Length == 0 || (c = literal.GetChar(0)) != ',' && c != '}') { throw new ArgumentException($"Unterminated object literal near: {literal.SubstringWithElipsis(0, 50)}"); } map[mapKey] = new JsBinding(mapKey); } } literal = literal.AdvancePastWhitespace(); if (literal.IsNullOrEmpty()) { break; } if (literal.GetChar(0) == '}') { literal = literal.Advance(1); break; } literal = literal.AdvancePastChar(','); literal = literal.AdvancePastWhitespace(); } value = map; return(literal); } if (firstChar == '[') { var list = new List <object>(); literal = literal.Advance(1); while (!literal.IsNullOrEmpty()) { literal = literal.AdvancePastWhitespace(); if (literal.GetChar(0) == ']') { literal = literal.Advance(1); break; } literal = literal.ParseNextToken(out object mapValue, out JsBinding mapVarRef); list.Add(mapVarRef ?? mapValue); literal = literal.AdvancePastWhitespace(); if (literal.IsNullOrEmpty()) { break; } if (literal.GetChar(0) == ']') { literal = literal.Advance(1); break; } literal = literal.AdvancePastWhitespace(); c = literal.GetChar(0); if (c == ']') { literal = literal.Advance(1); break; } if (c != ',') { throw new ArgumentException($"Unterminated array literal near: {literal.SubstringWithElipsis(0, 50)}"); } literal = literal.Advance(1); literal = literal.AdvancePastWhitespace(); } literal = literal.AdvancePastWhitespace(); value = list; return(literal); } if (literal.StartsWith("true") && (literal.Length == 4 || !IsValidVarNameChar(literal.GetChar(4)))) { value = true; return(literal.Advance(4)); } if (literal.StartsWith("false") && (literal.Length == 5 || !IsValidVarNameChar(literal.GetChar(5)))) { value = false; return(literal.Advance(5)); } if (literal.StartsWith("null") && (literal.Length == 4 || !IsValidVarNameChar(literal.GetChar(4)))) { value = JsNull.Value; return(literal.Advance(4)); } if (firstChar.IsOperatorChar()) { if (literal.StartsWith(">=")) { binding = JsGreaterThanEqual.Operand; return(literal.Advance(2)); } if (literal.StartsWith("<=")) { binding = JsLessThanEqual.Operand; return(literal.Advance(2)); } if (literal.StartsWith("!==")) { binding = JsStrictNotEquals.Operand; return(literal.Advance(3)); } if (literal.StartsWith("!=")) { binding = JsNotEquals.Operand; return(literal.Advance(2)); } if (literal.StartsWith("===")) { binding = JsStrictEquals.Operand; return(literal.Advance(3)); } if (literal.StartsWith("==")) { binding = JsEquals.Operand; return(literal.Advance(2)); } if (literal.StartsWith("||")) { binding = JsOr.Operator; return(literal.Advance(2)); } if (literal.StartsWith("&&")) { binding = JsAnd.Operator; return(literal.Advance(2)); } switch (firstChar) { case '>': binding = JsGreaterThan.Operand; return(literal.Advance(1)); case '<': binding = JsLessThan.Operand; return(literal.Advance(1)); case '=': binding = JsAssignment.Operator; return(literal.Advance(1)); case '!': binding = JsNot.Operator; return(literal.Advance(1)); case '+': binding = JsAddition.Operator; return(literal.Advance(1)); case '-': binding = JsSubtraction.Operator; return(literal.Advance(1)); case '*': binding = JsMultiplication.Operator; return(literal.Advance(1)); case '\\': binding = JsDivision.Operator; return(literal.Advance(1)); case '|': binding = JsBitwiseOr.Operator; return(literal.Advance(1)); case '&': binding = JsBitwiseAnd.Operator; return(literal.Advance(1)); default: throw new NotSupportedException($"Invalid Operator found near: '{literal.SubstringWithElipsis(0, 50)}'"); } } // name i = 1; var isExpression = false; var hadWhitespace = false; while (i < literal.Length && IsValidVarNameChar(c = literal.GetChar(i)) || (isExpression = c.IsBindingExpressionChar() || (allowWhitespaceSyntax && c == ':'))) { if (isExpression) { literal = literal.ParseNextExpression(out JsExpression expr); binding = expr; return(literal); } i++; while (i < literal.Length && literal.GetChar(i).IsWhiteSpace()) // advance past whitespace { i++; hadWhitespace = true; } if (hadWhitespace && (i >= literal.Length || !literal.GetChar(i).IsBindingExpressionChar())) { break; } } binding = new JsBinding(literal.Subsegment(0, i).TrimEnd()); return(literal.Advance(i)); }
public static StringSegment ParseNextToken(this StringSegment literal, out object value, out JsBinding binding) => ParseNextToken(literal, out value, out binding, false);
public static JsUnaryOperator GetUnaryOperator(JsBinding op) => (JsUnaryOperator)( op == JsSubtraction.Operator ? JsMinus.Operator : op == JsNot.Operator ? op : null);
public static object EvaluateBinding(TemplateScopeContext scope, JsBinding binding) { var result = scope.EvaluateToken(binding); return(result); }
public static JsUnaryOperator GetUnaryOperator(JsBinding op) => op == JsSubtraction.Operator ? JsMinus.Operator : null;
public static StringSegment ParseNextToken(this StringSegment literal, out object value, out JsBinding binding) { binding = null; value = null; var c = (char)0; if (literal.IsNullOrEmpty()) { return(TypeConstants.EmptyStringSegment); } var i = 0; literal = literal.AdvancePastWhitespace(); var firstChar = literal.GetChar(0); if (firstChar == '\'' || firstChar == '"') { i = 1; while (i < literal.Length && (literal.GetChar(i) != firstChar || literal.GetChar(i - 1) == '\\')) { i++; } if (i >= literal.Length || literal.GetChar(i) != firstChar) { throw new ArgumentException($"Unterminated string literal: {literal}"); } value = literal.Substring(1, i - 1); return(literal.Advance(i)); } if (firstChar >= '0' && firstChar <= '9' || firstChar == '-' || firstChar == '+') { i = 1; var hasExponent = false; var hasDecimal = false; while (i < literal.Length && IsValidNumericChar(c = literal.GetChar(i)) || (hasExponent = (c == 'e' || c == 'E'))) { if (c == '.') { hasDecimal = true; } i++; if (hasExponent) { i += 2; // [e+1]0 while (i < literal.Length && IsValidNumericChar(literal.GetChar(i))) { i++; } break; } } var numLiteral = literal.Subsegment(0, i); //don't convert into ternary to avoid Type coercion if (hasDecimal || hasExponent) { value = numLiteral.TryParseDouble(out double d) ? d : default(double); } else { value = numLiteral.ParseSignedInteger(); } return(literal.Advance(i)); } if (firstChar == '{') { var map = new Dictionary <string, object>(); literal = literal.Advance(1); while (!literal.IsNullOrEmpty()) { literal = literal.ParseNextToken(out object mapKeyString, out JsBinding mapKeyVar); if (mapKeyVar is JsExpression) { throw new NotSupportedException($"JsExpression '{mapKeyVar?.Binding}' is not a valid Object key."); } var mapKey = mapKeyVar != null ? mapKeyVar.Binding.Value : (string)mapKeyString; if (mapKey != null) { literal = literal.AdvancePastChar(':'); literal = literal.ParseNextToken(out object mapValue, out JsBinding mapValueBinding); map[mapKey] = mapValue ?? mapValueBinding; } literal = literal.AdvancePastWhitespace(); if (literal.IsNullOrEmpty()) { break; } if (literal.GetChar(0) == '}') { literal = literal.Advance(1); break; } literal = literal.AdvancePastChar(','); literal = literal.AdvancePastWhitespace(); } value = map; return(literal); } if (firstChar == '[') { var list = new List <object>(); literal = literal.Advance(1); while (!literal.IsNullOrEmpty() && literal.GetChar(0) != ']') { literal = literal.ParseNextToken(out object mapValue, out JsBinding mapVarRef); list.Add(mapVarRef ?? mapValue); literal = literal.AdvancePastWhitespace(); if (literal.IsNullOrEmpty()) { break; } if (literal.GetChar(0) == ']') { literal = literal.Advance(1); break; } literal = literal.AdvancePastChar(','); literal = literal.AdvancePastWhitespace(); } value = list; return(literal); } if (literal.StartsWith("true") && (literal.Length == 4 || !IsValidVarNameChar(literal.GetChar(4)))) { value = true; return(literal.Advance(4)); } if (literal.StartsWith("false") && (literal.Length == 5 || !IsValidVarNameChar(literal.GetChar(5)))) { value = false; return(literal.Advance(5)); } if (literal.StartsWith("null") && (literal.Length == 4 || !IsValidVarNameChar(literal.GetChar(4)))) { value = JsNull.Value; return(literal.Advance(4)); } // name i = 1; var isExpression = false; while (i < literal.Length && IsValidVarNameChar(c = literal.GetChar(i)) || (isExpression = (c == '.' || c == '(' || c == '['))) { if (isExpression) { binding = literal.ParseJsExpression(out int pos).FirstOrDefault(); return(literal.Advance(pos)); } i++; while (i < literal.Length && literal.GetChar(i).IsWhiteSpace()) // advance past whitespace { i++; } } binding = new JsBinding(literal.Subsegment(0, i).TrimEnd()); return(literal.Advance(i)); }