1844 lines
74 KiB
C#
1844 lines
74 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using VB = AspClassic.Parser;
|
|
using Dlrsoft.VBScript.Runtime;
|
|
using AspClassic.Scripting;
|
|
using System.Dynamic;
|
|
#if USE35
|
|
using AspClassic.Scripting.Ast;
|
|
#else
|
|
using System.Linq;
|
|
using System.Linq.Expressions;
|
|
#endif
|
|
|
|
using System.Reflection;
|
|
using System.Collections;
|
|
|
|
namespace Dlrsoft.VBScript.Compiler
|
|
{
|
|
internal static class VBScriptGenerator
|
|
{
|
|
private static Set<string> _builtinFunctions;
|
|
private static Set<string> _builtinConstants;
|
|
|
|
public static bool IsBuiltInConstants(string name)
|
|
{
|
|
ensureBuiltinConstants();
|
|
return _builtinConstants.Contains(name.ToLower());
|
|
}
|
|
|
|
public static bool IsBuiltInFunction(string name)
|
|
{
|
|
ensureBuiltinFunctions();
|
|
|
|
return _builtinFunctions.Contains(name.ToLower());
|
|
}
|
|
|
|
public static Expression GenerateExpr(VB.Tree expr, AnalysisScope scope)
|
|
{
|
|
try
|
|
{
|
|
if (expr is VB.ImportsDeclaration)
|
|
{
|
|
return GenerateImportExpr((VB.ImportsDeclaration)expr, scope);
|
|
}
|
|
else if (expr is VB.NameImport)
|
|
{
|
|
return GenerateImportExpr((VB.NameImport)expr, scope);
|
|
}
|
|
else if (expr is VB.AliasImport)
|
|
{
|
|
return GenerateImportExpr((VB.AliasImport)expr, scope);
|
|
}
|
|
if (expr is VB.CallStatement)
|
|
{
|
|
return GenerateCallStmtExpr((VB.CallStatement)expr, scope);
|
|
}
|
|
else if (expr is VB.LocalDeclarationStatement)
|
|
{
|
|
return GenerateDeclarationExpr((VB.LocalDeclarationStatement)expr, scope);
|
|
}
|
|
else if (expr is VB.FunctionDeclaration)
|
|
{
|
|
return GenerateFunctionExpr((VB.FunctionDeclaration)expr, scope);
|
|
}
|
|
else if (expr is VB.SubDeclaration)
|
|
{
|
|
return GenerateSubExpr((VB.SubDeclaration)expr, scope);
|
|
}
|
|
//else if (expr is SymplLambdaExpr)
|
|
//{
|
|
// return GenerateLambdaExpr((SymplLambdaExpr)expr, scope);
|
|
//}
|
|
else if (expr is VB.CallOrIndexExpression)
|
|
{
|
|
return GenerateCallOrIndexExpr((VB.CallOrIndexExpression)expr, scope);
|
|
}
|
|
else if (expr is VB.SimpleNameExpression)
|
|
{
|
|
return GenerateIdExpr((VB.SimpleNameExpression)expr, scope);
|
|
}
|
|
//else if (expr is SymplQuoteExpr)
|
|
//{
|
|
// return GenerateQuoteExpr((SymplQuoteExpr)expr, scope);
|
|
//}
|
|
else if (expr is VB.NothingExpression)
|
|
{
|
|
return Expression.Constant(null);
|
|
}
|
|
else if (expr is VB.LiteralExpression)
|
|
{
|
|
return Expression.Constant(((VB.LiteralExpression)expr).Value);
|
|
}
|
|
else if (expr is VB.AssignmentStatement)
|
|
{
|
|
return GenerateAssignExpr((VB.AssignmentStatement)expr, scope);
|
|
}
|
|
//else if (expr is SymplEqExpr)
|
|
//{
|
|
// return GenerateEqExpr((SymplEqExpr)expr, scope);
|
|
//}
|
|
else if (expr is VB.IfBlockStatement)
|
|
{
|
|
return GenerateIfExpr((VB.IfBlockStatement)expr, scope);
|
|
}
|
|
else if (expr is VB.LineIfStatement)
|
|
{
|
|
return GenerateIfExpr((VB.LineIfStatement)expr, scope);
|
|
}
|
|
else if (expr is VB.QualifiedExpression)
|
|
{
|
|
return GenerateDottedExpr((VB.QualifiedExpression)expr, scope);
|
|
}
|
|
else if (expr is VB.NewExpression)
|
|
{
|
|
return GenerateNewExpr((VB.NewExpression)expr, scope);
|
|
}
|
|
else if (expr is VB.ForBlockStatement)
|
|
{
|
|
return GenerateForBlockExpr((VB.ForBlockStatement)expr, scope);
|
|
}
|
|
else if (expr is VB.ForEachBlockStatement)
|
|
{
|
|
return GenerateForEachBlockExpr((VB.ForEachBlockStatement)expr, scope);
|
|
}
|
|
else if (expr is VB.WhileBlockStatement)
|
|
{
|
|
return GenerateWhileBlockExpr((VB.WhileBlockStatement)expr, scope);
|
|
}
|
|
else if (expr is VB.DoBlockStatement)
|
|
{
|
|
return GenerateDoBlockExpr((VB.DoBlockStatement)expr, scope);
|
|
}
|
|
else if (expr is VB.WithBlockStatement)
|
|
{
|
|
return GenerateWithBlockExpr((VB.WithBlockStatement)expr, scope);
|
|
}
|
|
else if (expr is VB.ExitStatement)
|
|
{
|
|
return GenerateBreakExpr((VB.ExitStatement)expr, scope);
|
|
}
|
|
//else if (expr is SymplEltExpr)
|
|
//{
|
|
// return GenerateEltExpr((SymplEltExpr)expr, scope);
|
|
//}
|
|
else if (expr is VB.BinaryOperatorExpression)
|
|
{
|
|
return GenerateBinaryExpr((VB.BinaryOperatorExpression)expr, scope);
|
|
}
|
|
else if (expr is VB.UnaryOperatorExpression)
|
|
{
|
|
return GenerateUnaryExpr((VB.UnaryOperatorExpression)expr, scope);
|
|
}
|
|
else if (expr is VB.SelectBlockStatement)
|
|
{
|
|
return GenerateSelectBlockExpr((VB.SelectBlockStatement)expr, scope);
|
|
}
|
|
else if (expr is VB.BlockStatement)
|
|
{
|
|
return GenerateBlockExpr(((VB.BlockStatement)expr).Statements, scope);
|
|
}
|
|
else if (expr is VB.EmptyStatement)
|
|
{
|
|
return Expression.Empty();
|
|
}
|
|
else if (expr is VB.OptionDeclaration)
|
|
{
|
|
scope.ModuleScope.IsOptionExplicitOn = true;
|
|
return Expression.Empty();
|
|
}
|
|
else if (expr is VB.ParentheticalExpression)
|
|
{
|
|
return GenerateExpr(((VB.ParentheticalExpression)expr).Operand, scope);
|
|
}
|
|
else if (expr is VB.ReDimStatement)
|
|
{
|
|
return GenerateRedimExpr((VB.ReDimStatement)expr, scope);
|
|
}
|
|
else if (expr is VB.OnErrorStatement)
|
|
{
|
|
return GenerateOnErrorStatement((VB.OnErrorStatement)expr, scope);
|
|
}
|
|
else if (expr is ExpressionExpression)
|
|
{
|
|
return ((ExpressionExpression)expr).Expression;
|
|
}
|
|
else
|
|
{
|
|
VBScriptSyntaxError error = new VBScriptSyntaxError(
|
|
scope.ModuleScope.Name,
|
|
SourceUtil.ConvertSpan(expr.Span),
|
|
(int)VB.SyntaxErrorType.NotImplemented,
|
|
string.Format("{0} is not yet implemented.", expr.GetType().FullName)
|
|
);
|
|
|
|
scope.ModuleScope.Errors.Add(error);
|
|
return Expression.Default(typeof(object));
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
VBScriptSyntaxError error = new VBScriptSyntaxError(
|
|
scope.ModuleScope.Name,
|
|
SourceUtil.ConvertSpan(expr.Span),
|
|
(int)VB.SyntaxErrorType.Unexpected,
|
|
string.Format("Unexpected error in {0}: {1}", expr.GetType().FullName, ex.Message)
|
|
);
|
|
|
|
scope.ModuleScope.Errors.Add(error);
|
|
return Expression.Default(typeof(object));
|
|
}
|
|
}
|
|
|
|
public static Expression GenerateImportExpr(VB.ImportsDeclaration importDesc, AnalysisScope scope)
|
|
{
|
|
if (!scope.IsModule)
|
|
{
|
|
throw new InvalidOperationException(
|
|
"Import expression must be a top level expression.");
|
|
}
|
|
List<Expression> exprs = new List<Expression>();
|
|
foreach (VB.Import statement in importDesc.ImportMembers)
|
|
{
|
|
Expression expr = GenerateExpr(statement, scope);
|
|
exprs.Add(expr);
|
|
}
|
|
return Expression.Block(exprs);
|
|
}
|
|
|
|
public static Expression GenerateImportExpr(VB.NameImport import,
|
|
AnalysisScope scope)
|
|
{
|
|
if (!scope.IsModule)
|
|
{
|
|
throw new InvalidOperationException(
|
|
"Import expression must be a top level expression.");
|
|
}
|
|
return Expression.Call(
|
|
typeof(RuntimeHelpers).GetMethod("VBScriptImport"),
|
|
scope.RuntimeExpr,
|
|
scope.ModuleExpr,
|
|
Expression.Constant(new string[] {
|
|
((VB.SimpleName)((VB.NamedTypeName)import.TypeName).Name).Name
|
|
}),
|
|
Expression.Constant(new string[]{}),
|
|
Expression.Constant(new string[]{}));
|
|
}
|
|
|
|
public static Expression GenerateImportExpr(VB.AliasImport import,
|
|
AnalysisScope scope)
|
|
{
|
|
if (!scope.IsModule)
|
|
{
|
|
throw new InvalidOperationException(
|
|
"Import expression must be a top level expression.");
|
|
}
|
|
|
|
string alias = ((VB.SimpleName)import.Name).Name;
|
|
VB.NamedTypeName namedTypeName = (VB.NamedTypeName)import.AliasedTypeName;
|
|
List<string> names = new List<string>();
|
|
string[] typeNames;
|
|
if (namedTypeName.Name is VB.QualifiedName)
|
|
{
|
|
VB.QualifiedName qualifiedName = (VB.QualifiedName)namedTypeName.Name;
|
|
typeNames = new string[] {qualifiedName.Name.Name};
|
|
|
|
VB.Name name = qualifiedName.Qualifier;
|
|
while (name is VB.QualifiedName)
|
|
{
|
|
names.Insert(0, ((VB.QualifiedName)name).Name.Name);
|
|
name = ((VB.QualifiedName)name).Qualifier;
|
|
}
|
|
names.Insert(0, ((VB.SimpleName)name).Name);
|
|
}
|
|
else
|
|
{
|
|
typeNames = new string[] { };
|
|
names.Insert(0, ((VB.SimpleName)namedTypeName.Name).Name);
|
|
}
|
|
|
|
return Expression.Call(
|
|
typeof(RuntimeHelpers).GetMethod("VBScriptImport"),
|
|
scope.RuntimeExpr,
|
|
scope.ModuleExpr,
|
|
Expression.Constant(names.ToArray()),
|
|
Expression.Constant(typeNames),
|
|
Expression.Constant(new string[] {alias})
|
|
);
|
|
}
|
|
|
|
public static Expression GenerateFunctionExpr(VB.FunctionDeclaration func,
|
|
AnalysisScope scope)
|
|
{
|
|
if (!scope.IsModule)
|
|
{
|
|
throw new InvalidOperationException(
|
|
"Use Defmethod or Lambda when not defining top-level function.");
|
|
}
|
|
string funcName = func.Name.Name.ToLower();
|
|
Expression lambda = GenerateLambdaDef(func.Parameters, func.Statements, scope, funcName, false);
|
|
//ParameterExpression p = Expression.Parameter(typeof(object), funcName);
|
|
//scope.Names.Add(funcName, p);
|
|
ParameterExpression p = scope.Names[funcName];
|
|
return Expression.Assign(p, lambda);
|
|
}
|
|
|
|
public static Expression GenerateSubExpr(VB.SubDeclaration sub,
|
|
AnalysisScope scope)
|
|
{
|
|
if (!scope.IsModule)
|
|
{
|
|
throw new InvalidOperationException(
|
|
"Use Defmethod or Lambda when not defining top-level function.");
|
|
}
|
|
string subName = sub.Name.Name.ToLower();
|
|
Expression lambda = GenerateLambdaDef(sub.Parameters, sub.Statements, scope, subName, true);
|
|
//ParameterExpression p = Expression.Parameter(typeof(object), subName);
|
|
//scope.Names.Add(subName, p);
|
|
ParameterExpression p = scope.Names[subName];
|
|
return Expression.Assign(p, lambda);
|
|
}
|
|
|
|
public static Expression GenerateDeclarationExpr(VB.LocalDeclarationStatement stmt, AnalysisScope scope)
|
|
{
|
|
List<Expression> expressions = new List<Expression>();
|
|
|
|
bool isConst = false;
|
|
|
|
if (stmt.Modifiers != null)
|
|
{
|
|
if (stmt.Modifiers.ModifierTypes == VB.ModifierTypes.Const)
|
|
{
|
|
isConst = true;
|
|
}
|
|
}
|
|
|
|
foreach (VB.VariableDeclarator d in stmt.VariableDeclarators)
|
|
{
|
|
Expression initializer = null;
|
|
|
|
if (d.Initializer != null)
|
|
{
|
|
VB.ExpressionInitializer exprInitializer = d.Initializer as VB.ExpressionInitializer;
|
|
|
|
if (exprInitializer != null)
|
|
{
|
|
initializer = GenerateExpr(exprInitializer.Expression, scope);
|
|
}
|
|
else
|
|
{
|
|
new NotImplementedException(d.Initializer.GetType().Name + " is not yet implemented.");
|
|
}
|
|
}
|
|
|
|
foreach (VB.VariableName v in d.VariableNames)
|
|
{
|
|
string name = v.Name.Name.ToLower();
|
|
Expression initExpression = null;
|
|
if (initializer != null)
|
|
{
|
|
initExpression = initializer;
|
|
}
|
|
else
|
|
{
|
|
if (v.ArrayType == null || v.ArrayType.Arguments.Count == 0)
|
|
{
|
|
initExpression = Expression.Constant(null);
|
|
}
|
|
else
|
|
{
|
|
List<Expression> args = GenerateArgumentList(v.ArrayType.Arguments, scope);
|
|
Expression converted = ConvertToIntegerArrayExpression(args);
|
|
|
|
initExpression = Expression.Call(
|
|
typeof(HelperFunctions).GetMethod("Redim"),
|
|
Expression.Constant(typeof(object)),
|
|
converted
|
|
);
|
|
//initExpression = Expression.Convert(
|
|
// Expression.NewArrayBounds(
|
|
// typeof(object),
|
|
// GenerateArgumentList(v.ArrayType.Arguments, scope)
|
|
// ),
|
|
// typeof(object)
|
|
// );
|
|
}
|
|
}
|
|
|
|
ParameterExpression p;
|
|
if (scope.IsModule)
|
|
{
|
|
//Module variable already put into scope in the analysis phase
|
|
p = scope.Names[name];
|
|
}
|
|
else
|
|
{
|
|
p = Expression.Parameter(typeof(object), name);
|
|
scope.VariableScope.Names.Add(name, p);
|
|
}
|
|
expressions.Add(Expression.Assign(p, Expression.Convert(initExpression, p.Type)));
|
|
}
|
|
}
|
|
if (expressions.Count > 0)
|
|
return Expression.Block(expressions);
|
|
else
|
|
return Expression.Empty();
|
|
}
|
|
|
|
public static Expression GenerateRedimExpr(VB.ReDimStatement redim, AnalysisScope scope)
|
|
{
|
|
List<Expression> expressions = new List<Expression>();
|
|
foreach (VB.Expression variable in redim.Variables)
|
|
{
|
|
VB.CallOrIndexExpression vd = (VB.CallOrIndexExpression)variable;
|
|
List<Expression> args = GenerateArgumentList(vd.Arguments, scope);
|
|
Expression converted = ConvertToIntegerArrayExpression(args);
|
|
|
|
Expression arrayExp;
|
|
|
|
if (!redim.IsPreserve)
|
|
{
|
|
//arrayExp = Expression.Convert(
|
|
// Expression.NewArrayBounds(
|
|
// typeof(object),
|
|
// converted
|
|
// ),
|
|
// typeof(object)
|
|
//);
|
|
arrayExp = Expression.Call(
|
|
typeof(HelperFunctions).GetMethod("Redim"),
|
|
Expression.Constant(typeof(object)),
|
|
converted
|
|
);
|
|
|
|
}
|
|
else
|
|
{
|
|
arrayExp = Expression.Call(
|
|
typeof(HelperFunctions).GetMethod("RedimPreserve"),
|
|
GenerateExpr(vd.TargetExpression, scope),
|
|
converted
|
|
);
|
|
}
|
|
|
|
Expression initExpression = GenerateSimpleNameAssignExpr(
|
|
(VB.SimpleNameExpression)vd.TargetExpression,
|
|
arrayExp,
|
|
scope
|
|
);
|
|
expressions.Add(initExpression);
|
|
}
|
|
return Expression.Block(expressions);
|
|
}
|
|
|
|
// Returns a dynamic InvokeMember or Invoke expression, depending on the
|
|
// Function expression.
|
|
//
|
|
public static Expression GenerateCallStmtExpr(
|
|
VB.CallStatement expr, AnalysisScope scope)
|
|
{
|
|
return GenerateCallOrIndexExpr(
|
|
new VB.CallOrIndexExpression(
|
|
expr.TargetExpression,
|
|
expr.Arguments,
|
|
expr.Span
|
|
),
|
|
scope
|
|
);
|
|
}
|
|
|
|
// Returns a chain of GetMember and InvokeMember dynamic expressions for
|
|
// the dotted expr.
|
|
//
|
|
public static Expression GenerateDottedExpr(VB.QualifiedExpression expr,
|
|
AnalysisScope scope)
|
|
{
|
|
Expression curExpr = null;
|
|
if (expr.Qualifier != null)
|
|
{
|
|
curExpr = GenerateExpr(expr.Qualifier, scope);
|
|
}
|
|
else
|
|
{
|
|
curExpr = scope.NearestWithExpression;
|
|
}
|
|
|
|
curExpr = Expression.Dynamic(
|
|
scope.GetRuntime()
|
|
.GetGetMemberBinder(expr.Name.Name),
|
|
typeof(object),
|
|
curExpr
|
|
);
|
|
// }
|
|
// else if (e is SymplFunCallExpr)
|
|
// {
|
|
// var call = (SymplFunCallExpr)e;
|
|
// List<Expression> args = new List<Expression>();
|
|
// args.Add(curExpr);
|
|
// args.AddRange(call.Arguments.Select(a => GenerateExpr(a, scope)));
|
|
|
|
// curExpr = Expression.Dynamic(
|
|
// // Dotted exprs must be simple invoke members, a.b.(c ...)
|
|
// scope.GetRuntime().GetInvokeMemberBinder(
|
|
// new InvokeMemberBinderKey(
|
|
// ((SymplIdExpr)call.Function).IdToken.Name,
|
|
// new CallInfo(call.Arguments.Length))),
|
|
// typeof(object),
|
|
// args
|
|
// );
|
|
// }
|
|
// else
|
|
// {
|
|
// throw new InvalidOperationException(
|
|
// "Internal: dotted must be IDs or Funs.");
|
|
// }
|
|
//}
|
|
return curExpr;
|
|
}
|
|
|
|
public static Expression GenerateQualifiedNameExpr(VB.QualifiedName expr,
|
|
AnalysisScope scope)
|
|
{
|
|
Expression curExpr;
|
|
if (expr.Qualifier is VB.SimpleName)
|
|
curExpr = GenerateSimpleNameExpr((VB.SimpleName)expr.Qualifier, scope);
|
|
else
|
|
curExpr = GenerateQualifiedNameExpr((VB.QualifiedName)expr.Qualifier, scope);
|
|
|
|
return Expression.Dynamic(
|
|
scope.GetRuntime()
|
|
.GetGetMemberBinder(expr.Name.Name),
|
|
typeof(object),
|
|
curExpr
|
|
);
|
|
}
|
|
|
|
// GenerateAssignExpr handles IDs, indexing, and member sets. IDs are either
|
|
// lexical or dynamic exprs on the module scope. Everything
|
|
// else is dynamic.
|
|
//
|
|
public static Expression GenerateAssignExpr(VB.AssignmentStatement expr,
|
|
AnalysisScope scope)
|
|
{
|
|
var val = GenerateExpr(expr.SourceExpression, scope);
|
|
|
|
if (!expr.IsSetStatement)
|
|
{
|
|
val = RuntimeHelpers.GetDefaultValue(val);
|
|
}
|
|
|
|
if (expr.TargetExpression is VB.SimpleNameExpression)
|
|
{
|
|
var idExpr = (VB.SimpleNameExpression)expr.TargetExpression;
|
|
return GenerateSimpleNameAssignExpr(idExpr, val, scope);
|
|
}
|
|
else if (expr.TargetExpression is VB.CallOrIndexExpression)
|
|
{
|
|
//If LHS is CallOrIndexExpression, it must be an index expression
|
|
var callOrIndex = (VB.CallOrIndexExpression)(expr.TargetExpression);
|
|
var args = new List<Expression>();
|
|
args.Add(GenerateExpr(callOrIndex.TargetExpression, scope));
|
|
args.AddRange(callOrIndex.Arguments.Select(e => GenerateExpr(e.Expression, scope)));
|
|
args.Add(Expression.Convert(val, typeof(object)));
|
|
// Trusting MO convention to return stored values.
|
|
return Expression.Dynamic(
|
|
scope.GetRuntime().GetSetIndexBinder(
|
|
new CallInfo(callOrIndex.Arguments.Count)),
|
|
typeof(object),
|
|
args);
|
|
}
|
|
else if (expr.TargetExpression is VB.QualifiedExpression)
|
|
{
|
|
// For now, one dot only. Later, pick oflast dotted member
|
|
// access (like GenerateFunctionCall), and use a temp and block.
|
|
var dottedExpr = (VB.QualifiedExpression)(expr.TargetExpression);
|
|
var id = (VB.SimpleName)(dottedExpr.Name);
|
|
|
|
Expression qualifier = null;
|
|
if (dottedExpr.Qualifier != null)
|
|
{
|
|
qualifier = GenerateExpr(dottedExpr.Qualifier, scope);
|
|
}
|
|
else
|
|
{
|
|
qualifier = scope.NearestWithExpression;
|
|
}
|
|
|
|
|
|
// Trusting MOs convention to return stored values.
|
|
return Expression.Dynamic(
|
|
scope.GetRuntime().GetSetMemberBinder(id.Name),
|
|
typeof(object),
|
|
qualifier,
|
|
val
|
|
);
|
|
}
|
|
|
|
throw new InvalidOperationException("Invalid left hand side type.");
|
|
}
|
|
|
|
public static Expression GenerateCallOrIndexExpr(VB.CallOrIndexExpression expr, AnalysisScope scope)
|
|
{
|
|
List<Expression> args = GenerateArgumentList(expr.Arguments, scope);
|
|
if (expr.TargetExpression is VB.SimpleNameExpression)
|
|
{
|
|
string name = ((VB.SimpleNameExpression)expr.TargetExpression).Name.Name;
|
|
//Call the built-in function it is one
|
|
if (IsBuiltInFunction(name))
|
|
{
|
|
int argCount = expr.Arguments == null? 0 : expr.Arguments.Count;
|
|
args.Insert(0, Expression.Constant(new TypeModel(typeof(BuiltInFunctions))));
|
|
return Expression.Dynamic(
|
|
scope.GetRuntime().GetInvokeMemberBinder(
|
|
new InvokeMemberBinderKey(
|
|
name,
|
|
new CallInfo(argCount))
|
|
),
|
|
typeof(object),
|
|
args
|
|
);
|
|
}
|
|
else if (scope.FunctionTable.Contains(name))
|
|
{
|
|
var fun = GenerateSimpleNameExpr(((VB.SimpleNameExpression)expr.TargetExpression).Name, scope);
|
|
List<Type> argTypes = new List<Type>();
|
|
foreach (Expression arg in args)
|
|
{
|
|
argTypes.Add(arg.Type.MakeByRefType());
|
|
}
|
|
argTypes.Insert(0, typeof(object)); //delegate itself
|
|
argTypes.Add(typeof(object)); //return type
|
|
|
|
args.Insert(0, fun);
|
|
// Use DynExpr so that I don't always have to have a delegate to call,
|
|
// such as what happens with IPy interop.
|
|
int argCount = expr.Arguments == null ? 0 : expr.Arguments.Count;
|
|
|
|
return Expression.MakeDynamic(
|
|
AspClassic.Scripting.Actions.DynamicSiteHelpers.MakeCallSiteDelegate(argTypes.ToArray()),
|
|
scope.GetRuntime().GetInvokeBinder(new CallInfo(argCount)),
|
|
args
|
|
);
|
|
}
|
|
else
|
|
{
|
|
////If not a function, it must be an array
|
|
args.Insert(0, GenerateSimpleNameExpr(((VB.SimpleNameExpression)expr.TargetExpression).Name, scope));
|
|
return Expression.Dynamic(
|
|
scope.GetRuntime().GetGetIndexBinder(
|
|
new CallInfo(args.Count)
|
|
),
|
|
typeof(object),
|
|
args
|
|
);
|
|
}
|
|
}
|
|
else if (expr.TargetExpression is VB.CallOrIndexExpression)
|
|
{
|
|
Expression objExpr = GenerateCallOrIndexExpr((VB.CallOrIndexExpression)expr.TargetExpression, scope);
|
|
////What followed must be an array as VBScript does not have function return a delegate
|
|
args.Insert(0, objExpr);
|
|
return Expression.Dynamic(
|
|
scope.GetRuntime().GetGetIndexBinder(
|
|
new CallInfo(args.Count)
|
|
),
|
|
typeof(object),
|
|
args
|
|
);
|
|
}
|
|
else //Qualified Expression
|
|
{
|
|
VB.QualifiedExpression qualifiedExpr = (VB.QualifiedExpression)expr.TargetExpression;
|
|
Expression objExpr;
|
|
if (qualifiedExpr.Qualifier is VB.QualifiedExpression)
|
|
{
|
|
objExpr = GenerateDottedExpr(
|
|
(VB.QualifiedExpression)qualifiedExpr.Qualifier,
|
|
scope
|
|
);
|
|
}
|
|
else if (qualifiedExpr.Qualifier is VB.SimpleNameExpression)
|
|
{
|
|
objExpr = GenerateSimpleNameExpr(((VB.SimpleNameExpression)qualifiedExpr.Qualifier).Name, scope);
|
|
}
|
|
else if (qualifiedExpr.Qualifier is VB.CallOrIndexExpression)
|
|
{
|
|
objExpr = GenerateCallOrIndexExpr((VB.CallOrIndexExpression)qualifiedExpr.Qualifier, scope);
|
|
}
|
|
else //null
|
|
{
|
|
objExpr = scope.NearestWithExpression;
|
|
if (objExpr == null)
|
|
throw new Exception("Missing With statement");
|
|
}
|
|
|
|
//LC 12/16/2009 Try the byref type
|
|
List<Type> argTypes = new List<Type>();
|
|
foreach (Expression arg in args)
|
|
{
|
|
argTypes.Add(arg.Type.MakeByRefType());
|
|
}
|
|
argTypes.Insert(0, typeof(object)); //target itself
|
|
argTypes.Add(typeof(object)); //return type
|
|
|
|
args.Insert(0, objExpr);
|
|
|
|
// last expr must be an id
|
|
var lastExpr = qualifiedExpr.Name;
|
|
int argCount = 0;
|
|
if (expr.Arguments != null)
|
|
argCount = expr.Arguments.Count;
|
|
|
|
return Expression.MakeDynamic(
|
|
AspClassic.Scripting.Actions.DynamicSiteHelpers.MakeCallSiteDelegate(argTypes.ToArray()),
|
|
scope.GetRuntime().GetInvokeMemberBinder(
|
|
new InvokeMemberBinderKey(
|
|
lastExpr.Name,
|
|
new CallInfo(argCount))),
|
|
args
|
|
);
|
|
|
|
//return Expression.Dynamic(
|
|
// scope.GetRuntime().GetInvokeMemberBinder(
|
|
// new InvokeMemberBinderKey(
|
|
// lastExpr.Name,
|
|
// new CallInfo(argCount))),
|
|
// typeof(object),
|
|
// args
|
|
//);
|
|
}
|
|
}
|
|
|
|
// Return an Expression for referencing the ID. If we find the name in the
|
|
// scope chain, then we just return the stored ParamExpr. Otherwise, the
|
|
// reference is a dynamic member lookup on the root scope, a module object.
|
|
//
|
|
public static Expression GenerateIdExpr(VB.SimpleNameExpression expr,
|
|
AnalysisScope scope)
|
|
{
|
|
string name = expr.Name.Name;
|
|
|
|
if (IsBuiltInFunction(name) || scope.FunctionTable.Contains(name))
|
|
{
|
|
return GenerateCallOrIndexExpr(
|
|
new VB.CallOrIndexExpression(
|
|
expr,
|
|
null,
|
|
expr.Span),
|
|
scope
|
|
);
|
|
}
|
|
else
|
|
{
|
|
return GenerateSimpleNameExpr(expr.Name, scope);
|
|
}
|
|
}
|
|
|
|
public static Expression GenerateOnErrorStatement(VB.OnErrorStatement onError, AnalysisScope scope)
|
|
{
|
|
if (onError.OnErrorType == VB.OnErrorType.Next)
|
|
{
|
|
scope.VariableScope.IsOnErrorResumeNextOn = true;
|
|
}
|
|
else if(onError.OnErrorType == VB.OnErrorType.Zero)
|
|
{
|
|
scope.VariableScope.IsOnErrorResumeNextOn = false;
|
|
}
|
|
return Expression.Call(
|
|
scope.ErrExpression,
|
|
typeof(ErrObject).GetMethod("Clear")
|
|
);
|
|
}
|
|
|
|
// GenerateLetStar returns a Block with vars, each initialized in the order
|
|
// they appear. Each var's init expr can refer to vars initialized before it.
|
|
// The Block's body is the Let*'s body.
|
|
//
|
|
//public static Expression GenerateLetStarExpr(SymplLetStarExpr expr,
|
|
// AnalysisScope scope)
|
|
//{
|
|
// var letscope = new AnalysisScope(scope, "let*");
|
|
// // Generate bindings.
|
|
// List<Expression> inits = new List<Expression>();
|
|
// List<ParameterExpression> varsInOrder = new List<ParameterExpression>();
|
|
// foreach (var b in expr.Bindings)
|
|
// {
|
|
// // Need richer logic for mvbind
|
|
// var v = Expression.Parameter(typeof(object), b.Variable.Name);
|
|
// varsInOrder.Add(v);
|
|
// inits.Add(
|
|
// Expression.Assign(
|
|
// v,
|
|
// Expression.Convert(GenerateExpr(b.Value, letscope), v.Type))
|
|
// );
|
|
// // Add var to scope after analyzing init value so that init value
|
|
// // references to the same ID do not bind to his uninitialized var.
|
|
// letscope.Names[b.Variable.Name.ToLower()] = v;
|
|
// }
|
|
// List<Expression> body = new List<Expression>();
|
|
// foreach (var e in expr.Body)
|
|
// {
|
|
// body.Add(GenerateExpr(e, letscope));
|
|
// }
|
|
// // Order of vars to BlockExpr don't matter semantically, but may as well
|
|
// // keep them in the order the programmer specified in case they look at the
|
|
// // Expr Trees in the debugger or for meta-programming.
|
|
// inits.AddRange(body);
|
|
// return Expression.Block(typeof(object), varsInOrder.ToArray(), inits);
|
|
//}
|
|
|
|
// GenerateBlockExpr returns a Block with the body exprs.
|
|
//
|
|
public static Expression GenerateBlockExpr(VB.StatementCollection stmts,
|
|
AnalysisScope scope)
|
|
{
|
|
List<Expression> body = new List<Expression>();
|
|
if (stmts != null)
|
|
{
|
|
foreach (VB.Statement s in stmts)
|
|
{
|
|
Expression stmt = VBScriptGenerator.GenerateExpr(s, scope);
|
|
if (scope.VariableScope.IsOnErrorResumeNextOn)
|
|
{
|
|
stmt = WrapTryCatchExpression(stmt, scope);
|
|
}
|
|
Expression debugInfo = null;
|
|
Expression clearDebugInfo = null;
|
|
if (scope.GetRuntime().Trace && s is VB.Statement && !(s is VB.BlockStatement))
|
|
{
|
|
debugInfo = VBScriptGenerator.GenerateDebugInfo(s, scope, out clearDebugInfo);
|
|
body.Add(debugInfo);
|
|
}
|
|
|
|
body.Add(stmt);
|
|
|
|
if (clearDebugInfo != null)
|
|
{
|
|
body.Add(clearDebugInfo);
|
|
}
|
|
}
|
|
//return Expression.Block(typeof(object), body);
|
|
return Expression.Block(typeof(void), body);
|
|
}
|
|
else
|
|
{
|
|
//return Expression.Constant(null);
|
|
return Expression.Empty();
|
|
}
|
|
}
|
|
|
|
// GenerateQuoteExpr converts a list, literal, or id expr to a runtime quoted
|
|
// literal and returns the Constant expression for it.
|
|
//
|
|
//public static Expression GenerateQuoteExpr(SymplQuoteExpr expr,
|
|
// AnalysisScope scope)
|
|
//{
|
|
// return Expression.Constant(MakeQuoteConstant(
|
|
// expr.Expr, scope.GetRuntime()));
|
|
//}
|
|
|
|
//private static object MakeQuoteConstant(object expr, Sympl symplRuntime)
|
|
//{
|
|
// if (expr is SymplListExpr)
|
|
// {
|
|
// SymplListExpr listexpr = (SymplListExpr)expr;
|
|
// int len = listexpr.Elements.Length;
|
|
// var exprs = new object[len];
|
|
// for (int i = 0; i < len; i++)
|
|
// {
|
|
// exprs[i] = MakeQuoteConstant(listexpr.Elements[i], symplRuntime);
|
|
// }
|
|
// return Cons._List(exprs);
|
|
// }
|
|
// else if (expr is IdOrKeywordToken)
|
|
// {
|
|
// return symplRuntime.MakeSymbol(((IdOrKeywordToken)expr).Name);
|
|
// }
|
|
// else if (expr is LiteralToken)
|
|
// {
|
|
// return ((LiteralToken)expr).Value;
|
|
// }
|
|
// else
|
|
// {
|
|
// throw new InvalidOperationException(
|
|
// "Internal: quoted list has -- " + expr.ToString());
|
|
// }
|
|
//}
|
|
|
|
//public static Expression GenerateEqExpr(SymplEqExpr expr,
|
|
// AnalysisScope scope)
|
|
//{
|
|
// var mi = typeof(RuntimeHelpers).GetMethod("SymplEq");
|
|
// return Expression.Call(mi, Expression.Convert(
|
|
// GenerateExpr(expr.Left, scope),
|
|
// typeof(object)),
|
|
// Expression.Convert(
|
|
// GenerateExpr(expr.Right, scope),
|
|
// typeof(object)));
|
|
//}
|
|
|
|
//public static Expression GenerateConsExpr(SymplConsExpr expr,
|
|
// AnalysisScope scope)
|
|
//{
|
|
// var mi = typeof(RuntimeHelpers).GetMethod("MakeCons");
|
|
// return Expression.Call(mi, Expression.Convert(
|
|
// GenerateExpr(expr.Left, scope),
|
|
// typeof(object)),
|
|
// Expression.Convert(
|
|
// GenerateExpr(expr.Right, scope),
|
|
// typeof(object)));
|
|
//}
|
|
|
|
//public static Expression GenerateListCallExpr(SymplListCallExpr expr,
|
|
// AnalysisScope scope)
|
|
//{
|
|
// var mi = typeof(Cons).GetMethod("_List");
|
|
// int len = expr.Elements.Length;
|
|
// var args = new Expression[len];
|
|
// for (int i = 0; i < len; i++)
|
|
// {
|
|
// args[i] = Expression.Convert(GenerateExpr(expr.Elements[i], scope),
|
|
// typeof(object));
|
|
// }
|
|
// return Expression.Call(mi, Expression
|
|
// .NewArrayInit(typeof(object), args));
|
|
//}
|
|
|
|
public static Expression GenerateIfExpr(VB.IfBlockStatement ifBlock, AnalysisScope scope)
|
|
{
|
|
Expression elseBlock = null;
|
|
if (ifBlock.ElseBlockStatement != null)
|
|
{
|
|
elseBlock = GenerateExpr((VB.BlockStatement)ifBlock.ElseBlockStatement, scope);
|
|
}
|
|
else
|
|
{
|
|
elseBlock = Expression.Empty();
|
|
}
|
|
|
|
if (ifBlock.ElseIfBlockStatements != null && ifBlock.ElseIfBlockStatements.Count > 0)
|
|
{
|
|
for (int i = ifBlock.ElseIfBlockStatements.Count - 1; i > -1; i--)
|
|
{
|
|
VB.ElseIfBlockStatement elseifBock = (VB.ElseIfBlockStatement)ifBlock.ElseIfBlockStatements.get_Item(i);
|
|
elseBlock = Expression.Condition(
|
|
WrapBooleanTest(GenerateExpr(elseifBock.ElseIfStatement.Expression, scope)),
|
|
GenerateBlockExpr(elseifBock.Statements, scope),
|
|
elseBlock,
|
|
typeof(void));
|
|
}
|
|
}
|
|
Expression test = WrapBooleanTest(GenerateExpr(ifBlock.Expression, scope));
|
|
Expression ifblock = GenerateBlockExpr(ifBlock.Statements, scope);
|
|
|
|
return Expression.Condition(
|
|
test,
|
|
ifblock,
|
|
elseBlock,
|
|
typeof(void));
|
|
}
|
|
|
|
public static Expression GenerateIfExpr(VB.LineIfStatement ifstmt, AnalysisScope scope)
|
|
{
|
|
Expression elseBlock = null;
|
|
if (ifstmt.ElseStatements != null)
|
|
{
|
|
elseBlock = GenerateBlockExpr(ifstmt.ElseStatements, scope);
|
|
}
|
|
else
|
|
{
|
|
elseBlock = Expression.Empty();
|
|
}
|
|
|
|
Expression test = WrapBooleanTest(GenerateExpr(ifstmt.Expression, scope));
|
|
Expression ifblock = GenerateBlockExpr(ifstmt.IfStatements, scope);
|
|
|
|
return Expression.Condition(
|
|
test,
|
|
ifblock,
|
|
elseBlock,
|
|
typeof(void));
|
|
}
|
|
|
|
|
|
public static Expression GenerateSelectBlockExpr(VB.SelectBlockStatement selectBlock,
|
|
AnalysisScope scope)
|
|
{
|
|
ParameterExpression tmp = Expression.Parameter(typeof(object));
|
|
Expression alt = null;
|
|
if (selectBlock.CaseElseBlockStatement != null)
|
|
{
|
|
alt = GenerateExpr((VB.BlockStatement)selectBlock.CaseElseBlockStatement, scope);
|
|
}
|
|
else
|
|
{
|
|
alt = Expression.Constant(false);
|
|
}
|
|
|
|
if (selectBlock.CaseBlockStatements != null && selectBlock.CaseBlockStatements.Count > 0)
|
|
{
|
|
for (int i = selectBlock.CaseBlockStatements.Count - 1; i >= 0; i--)
|
|
{
|
|
VB.CaseBlockStatement caseStmt = (VB.CaseBlockStatement)selectBlock.CaseBlockStatements.get_Item(i);
|
|
Expression condition = null;
|
|
for (int j = caseStmt.CaseStatement.CaseClauses.Count - 1; j >= 0; j--)
|
|
{
|
|
VB.RangeCaseClause caseClause = (VB.RangeCaseClause)caseStmt.CaseStatement.CaseClauses.get_Item(j);
|
|
//Expression oneCase = Expression.Equal(
|
|
// tmp,
|
|
// Expression.Convert(
|
|
// GenerateExpr(caseClause.RangeExpression, scope),
|
|
// typeof(object)
|
|
// )
|
|
//);
|
|
Expression oneCase = WrapBooleanTest(
|
|
Expression.Dynamic(
|
|
scope.GetRuntime().GetBinaryOperationBinder(ExpressionType.Equal),
|
|
typeof(object),
|
|
new Expression[] {
|
|
tmp,
|
|
GenerateExpr(caseClause.RangeExpression, scope)
|
|
}
|
|
)
|
|
);
|
|
|
|
if (condition == null)
|
|
{
|
|
condition = oneCase;
|
|
}
|
|
else
|
|
{
|
|
condition = Expression.OrElse(oneCase, condition);
|
|
}
|
|
}
|
|
alt = Expression.Condition(
|
|
condition,
|
|
GenerateBlockExpr(caseStmt.Statements, scope),
|
|
alt,
|
|
typeof(void));
|
|
}
|
|
|
|
return Expression.Block(
|
|
new ParameterExpression[] {tmp},
|
|
Expression.Assign(
|
|
tmp,
|
|
RuntimeHelpers.EnsureObjectResult(
|
|
GenerateExpr(selectBlock.Expression, scope)
|
|
)
|
|
),
|
|
alt
|
|
);
|
|
}
|
|
else
|
|
{
|
|
throw new Exception("At least one case block needed.");
|
|
}
|
|
}
|
|
|
|
public static Expression GenerateForBlockExpr(VB.ForBlockStatement forBlock,
|
|
AnalysisScope scope)
|
|
{
|
|
var loopscope = new AnalysisScope(scope, "loop ");
|
|
loopscope.IsForLoop = true; // needed for exit
|
|
loopscope.LoopBreak = Expression.Label("loop break");
|
|
|
|
Expression loopVariable;
|
|
if (forBlock.ControlExpression is VB.SimpleNameExpression)
|
|
{
|
|
loopVariable = FindIdDef(((VB.SimpleNameExpression)forBlock.ControlExpression).Name.Name, scope, true);
|
|
}
|
|
else
|
|
{
|
|
loopVariable = GenerateExpr(forBlock.ControlExpression, loopscope);
|
|
}
|
|
Expression body = GenerateBlockExpr(forBlock.Statements, loopscope);
|
|
|
|
LoopExpression myLoop = Expression.Loop(
|
|
Expression.Block(
|
|
Expression.IfThen(
|
|
WrapBooleanTest(
|
|
GenerateExpr(
|
|
new VB.BinaryOperatorExpression(
|
|
forBlock.ControlExpression,
|
|
VB.OperatorType.GreaterThan,
|
|
forBlock.ToLocation,
|
|
forBlock.UpperBoundExpression,
|
|
forBlock.UpperBoundExpression.Span
|
|
),
|
|
loopscope
|
|
)
|
|
),
|
|
Expression.Goto(loopscope.LoopBreak)
|
|
),
|
|
body,
|
|
GenerateExpr(
|
|
new VB.AssignmentStatement(
|
|
forBlock.ControlExpression,
|
|
forBlock.LowerBoundExpression.Span.Start,
|
|
new VB.BinaryOperatorExpression(
|
|
forBlock.ControlExpression,
|
|
VB.OperatorType.Plus,
|
|
forBlock.ToLocation,
|
|
forBlock.StepExpression ?? new VB.IntegerLiteralExpression(1, VB.IntegerBase.Decimal, VB.TypeCharacter.None, forBlock.UpperBoundExpression.Span),
|
|
forBlock.LowerBoundExpression.Span
|
|
),
|
|
forBlock.LowerBoundExpression.Span,
|
|
null
|
|
),
|
|
loopscope
|
|
)
|
|
),
|
|
loopscope.LoopBreak
|
|
);
|
|
|
|
return Expression.Block(
|
|
GenerateExpr(
|
|
new VB.AssignmentStatement(
|
|
forBlock.ControlExpression,
|
|
forBlock.EqualsLocation,
|
|
forBlock.LowerBoundExpression,
|
|
forBlock.LowerBoundExpression.Span,
|
|
null
|
|
),
|
|
loopscope
|
|
),
|
|
myLoop
|
|
);
|
|
}
|
|
|
|
public static Expression GenerateForEachBlockExpr(VB.ForEachBlockStatement forBlock,
|
|
AnalysisScope scope)
|
|
{
|
|
var loopscope = new AnalysisScope(scope, "loop ");
|
|
loopscope.IsForLoop = true; // needed for break and continue
|
|
loopscope.LoopBreak = Expression.Label("loop break");
|
|
Expression body = GenerateBlockExpr(forBlock.Statements, loopscope);
|
|
Expression variable;
|
|
if (forBlock.ControlExpression is VB.SimpleNameExpression)
|
|
{
|
|
variable = FindIdDef(((VB.SimpleNameExpression)forBlock.ControlExpression).Name.Name, scope, true);
|
|
}
|
|
else
|
|
{
|
|
variable = GenerateExpr(forBlock.ControlExpression, loopscope);
|
|
}
|
|
Expression enumerable = GenerateExpr(forBlock.CollectionExpression, scope);
|
|
ParameterExpression temp = Expression.Variable(typeof(IEnumerator), "$enumerator");
|
|
|
|
return Expression.Block(
|
|
new ParameterExpression[] { temp},
|
|
Expression.Assign(temp,
|
|
Expression.Call(
|
|
Expression.Convert(
|
|
enumerable,
|
|
typeof(IEnumerable)
|
|
),
|
|
typeof(IEnumerable).GetMethod("GetEnumerator")
|
|
)
|
|
),
|
|
Expression.Loop(
|
|
Expression.Block(
|
|
Expression.Condition(
|
|
Expression.Call(
|
|
temp,
|
|
typeof(IEnumerator).GetMethod("MoveNext")
|
|
),
|
|
Expression.Empty(),
|
|
Expression.Break(loopscope.LoopBreak),
|
|
typeof(void)
|
|
),
|
|
GenerateExpr(
|
|
new VB.AssignmentStatement(
|
|
forBlock.ControlExpression,
|
|
forBlock.InLocation,
|
|
new ExpressionExpression(
|
|
Expression.Convert(
|
|
Expression.Property(
|
|
temp,
|
|
typeof(IEnumerator).GetProperty("Current")
|
|
),
|
|
variable.Type
|
|
),
|
|
forBlock.ControlExpression.Span
|
|
),
|
|
forBlock.ControlExpression.Span,
|
|
null,
|
|
true
|
|
),
|
|
loopscope
|
|
),
|
|
body
|
|
),
|
|
loopscope.LoopBreak)
|
|
);
|
|
}
|
|
|
|
public static Expression GenerateWhileBlockExpr(VB.WhileBlockStatement whileBlock,
|
|
AnalysisScope scope)
|
|
{
|
|
var breakTarget = Expression.Label("loop break");
|
|
var body = GenerateBlockExpr(whileBlock.Statements, scope);
|
|
return Expression.Loop(
|
|
Expression.Block(
|
|
Expression.IfThenElse(
|
|
WrapBooleanTest(
|
|
GenerateExpr(whileBlock.Expression, scope)
|
|
),
|
|
Expression.Empty(),
|
|
Expression.Goto(breakTarget)
|
|
),
|
|
body
|
|
),
|
|
breakTarget
|
|
);
|
|
}
|
|
|
|
public static Expression GenerateWithBlockExpr(VB.WithBlockStatement withBlock,
|
|
AnalysisScope scope)
|
|
{
|
|
var withScope = new AnalysisScope(scope, "with");
|
|
withScope.IsWith = true;
|
|
withScope.WithExpression = GenerateExpr(withBlock.Expression, scope);
|
|
return GenerateBlockExpr(withBlock.Statements, withScope);
|
|
}
|
|
|
|
public static Expression GenerateDoBlockExpr(VB.DoBlockStatement doBlock,
|
|
AnalysisScope scope)
|
|
{
|
|
//Todo: Need to take care While/Until at beginning/end
|
|
var loopscope = new AnalysisScope(scope, "loop ");
|
|
loopscope.IsDoLoop = true; // needed for break and continue
|
|
loopscope.LoopBreak = Expression.Label("loop break");
|
|
loopscope.LoopContinue = Expression.Label("loop continue");
|
|
var body = GenerateBlockExpr(doBlock.Statements, loopscope);
|
|
|
|
if (doBlock.Expression != null)
|
|
{
|
|
if (doBlock.IsWhile) //do while ... loop
|
|
{
|
|
return Expression.Loop(
|
|
Expression.Block(
|
|
Expression.IfThenElse(
|
|
WrapBooleanTest(GenerateExpr(doBlock.Expression, loopscope)),
|
|
Expression.Empty(),
|
|
Expression.Goto(loopscope.LoopBreak)
|
|
),
|
|
body
|
|
),
|
|
loopscope.LoopBreak,
|
|
loopscope.LoopContinue
|
|
);
|
|
}
|
|
else // do until ... loop
|
|
{
|
|
return Expression.Loop(
|
|
Expression.Block(
|
|
Expression.IfThen(
|
|
WrapBooleanTest(GenerateExpr(doBlock.Expression, loopscope)),
|
|
Expression.Goto(loopscope.LoopBreak)
|
|
),
|
|
body
|
|
),
|
|
loopscope.LoopBreak,
|
|
loopscope.LoopContinue
|
|
);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (doBlock.EndStatement.IsWhile) //do ... loop while
|
|
{
|
|
return Expression.Loop(
|
|
Expression.Block(
|
|
body,
|
|
Expression.IfThenElse(
|
|
WrapBooleanTest(GenerateExpr(doBlock.EndStatement.Expression, loopscope)),
|
|
Expression.Empty(),
|
|
Expression.Goto(loopscope.LoopBreak)
|
|
)
|
|
),
|
|
loopscope.LoopBreak,
|
|
loopscope.LoopContinue
|
|
);
|
|
}
|
|
else // do until ... loop
|
|
{
|
|
return Expression.Loop(
|
|
Expression.Block(
|
|
body,
|
|
Expression.IfThen(
|
|
WrapBooleanTest(GenerateExpr(doBlock.EndStatement.Expression, loopscope)),
|
|
Expression.Goto(loopscope.LoopBreak)
|
|
)
|
|
),
|
|
loopscope.LoopBreak,
|
|
loopscope.LoopContinue
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static Expression GenerateBreakExpr(VB.ExitStatement expr,
|
|
AnalysisScope scope)
|
|
{
|
|
var exitScope = _findFirstScope(scope, expr.ExitType);
|
|
if (exitScope == null)
|
|
throw new InvalidOperationException("Cannot find exit target.");
|
|
LabelTarget target = null;
|
|
switch (expr.ExitType)
|
|
{
|
|
case AspClassic.Parser.BlockType.Function:
|
|
string name = exitScope.Name;
|
|
return Expression.Break(exitScope.MethodExit, FindIdDef(name, scope));
|
|
|
|
case AspClassic.Parser.BlockType.Sub:
|
|
target = exitScope.MethodExit;
|
|
break;
|
|
case AspClassic.Parser.BlockType.Do:
|
|
case AspClassic.Parser.BlockType.For:
|
|
target = exitScope.LoopBreak;
|
|
break;
|
|
}
|
|
return Expression.Break(target);
|
|
}
|
|
|
|
public static Expression GenerateNewExpr(VB.NewExpression expr,
|
|
AnalysisScope scope)
|
|
{
|
|
|
|
VB.Name target = ((VB.NamedTypeName)expr.Target).Name;
|
|
Expression targetExpr;
|
|
if (target is VB.QualifiedName)
|
|
{
|
|
targetExpr = GenerateQualifiedNameExpr((VB.QualifiedName)target, scope);
|
|
}
|
|
else
|
|
{
|
|
targetExpr = GenerateSimpleNameExpr((VB.SimpleName)target, scope);
|
|
}
|
|
//args.Add(targetExpr);
|
|
//args.AddRange(expr.Arguments.Select(a => GenerateExpr(a, scope)));
|
|
List<Expression> args = GenerateArgumentList(expr.Arguments, scope);
|
|
args.Insert(0, targetExpr);
|
|
|
|
return Expression.Dynamic(
|
|
scope.GetRuntime().GetCreateInstanceBinder(
|
|
new CallInfo(expr.Arguments.Count)),
|
|
typeof(object),
|
|
args
|
|
);
|
|
}
|
|
|
|
public static Expression GenerateBinaryExpr(VB.BinaryOperatorExpression expr,
|
|
AnalysisScope scope)
|
|
{
|
|
|
|
// The language has the following special logic to handle And and Or
|
|
// x And y == if x then y
|
|
// x Or y == if x then x else (if y then y)
|
|
ExpressionType op;
|
|
switch(expr.Operator)
|
|
{
|
|
case VB.OperatorType.Concatenate:
|
|
return Expression.Call(
|
|
typeof(HelperFunctions).GetMethod("Concatenate"),
|
|
RuntimeHelpers.EnsureObjectResult(GenerateExpr(expr.LeftOperand, scope)),
|
|
RuntimeHelpers.EnsureObjectResult(GenerateExpr(expr.RightOperand, scope))
|
|
);
|
|
case VB.OperatorType.Plus:
|
|
op = ExpressionType.Add;
|
|
break;
|
|
case VB.OperatorType.Minus:
|
|
op = ExpressionType.Subtract;
|
|
break;
|
|
case VB.OperatorType.Multiply:
|
|
op = ExpressionType.Multiply;
|
|
break;
|
|
case VB.OperatorType.Divide:
|
|
op = ExpressionType.Divide;
|
|
break;
|
|
case VB.OperatorType.IntegralDivide:
|
|
op = ExpressionType.Divide;
|
|
break;
|
|
case VB.OperatorType.Modulus:
|
|
op = ExpressionType.Modulo;
|
|
break;
|
|
case VB.OperatorType.Equals:
|
|
op = ExpressionType.Equal;
|
|
break;
|
|
case VB.OperatorType.NotEquals:
|
|
op = ExpressionType.NotEqual;
|
|
break;
|
|
case VB.OperatorType.LessThan:
|
|
op = ExpressionType.LessThan;
|
|
break;
|
|
case VB.OperatorType.GreaterThan:
|
|
op = ExpressionType.GreaterThan;
|
|
break;
|
|
case VB.OperatorType.LessThanEquals:
|
|
op = ExpressionType.LessThanOrEqual;
|
|
break;
|
|
case VB.OperatorType.GreaterThanEquals:
|
|
op = ExpressionType.GreaterThanOrEqual;
|
|
break;
|
|
case VB.OperatorType.Is:
|
|
op = ExpressionType.Equal;
|
|
break;
|
|
case VB.OperatorType.And:
|
|
op = ExpressionType.And;
|
|
break;
|
|
case VB.OperatorType.Or:
|
|
op = ExpressionType.Or;
|
|
break;
|
|
case VB.OperatorType.Xor:
|
|
op = ExpressionType.ExclusiveOr;
|
|
break;
|
|
case VB.OperatorType.Power:
|
|
op = ExpressionType.Power;
|
|
break;
|
|
default:
|
|
throw new InvalidOperationException("Unknown binary operator " + expr.Operator);
|
|
}
|
|
return Expression.Dynamic(
|
|
scope.GetRuntime().GetBinaryOperationBinder(op),
|
|
typeof(object),
|
|
GenerateExpr(expr.LeftOperand, scope),
|
|
GenerateExpr(expr.RightOperand, scope)
|
|
);
|
|
}
|
|
|
|
public static Expression GenerateUnaryExpr(VB.UnaryOperatorExpression expr,
|
|
AnalysisScope scope)
|
|
{
|
|
ExpressionType op;
|
|
switch (expr.Operator)
|
|
{
|
|
case VB.OperatorType.Negate:
|
|
op = ExpressionType.Negate;
|
|
break;
|
|
case VB.OperatorType.Not:
|
|
op = ExpressionType.Not;
|
|
break;
|
|
default:
|
|
throw new InvalidOperationException("Unknown unary operator " + expr.Operator);
|
|
|
|
}
|
|
return Expression.Dynamic(
|
|
scope.GetRuntime().GetUnaryOperationBinder(op),
|
|
typeof(object),
|
|
GenerateExpr(expr.Operand, scope)
|
|
);
|
|
}
|
|
|
|
private static void ensureBuiltinConstants()
|
|
{
|
|
lock (typeof(VBScriptGenerator))
|
|
if (_builtinConstants == null)
|
|
{
|
|
_builtinConstants = new Set<string>(StringComparer.InvariantCultureIgnoreCase);
|
|
FieldInfo[] fis = typeof(BuiltInConstants).GetFields(BindingFlags.Public | BindingFlags.Static);
|
|
foreach (FieldInfo fi in fis)
|
|
{
|
|
_builtinConstants.Add(fi.Name);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static object getBuiltinConstant(string name)
|
|
{
|
|
FieldInfo fi = typeof(BuiltInConstants).GetField(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.IgnoreCase);
|
|
return fi.GetValue(null);
|
|
}
|
|
|
|
private static void ensureBuiltinFunctions()
|
|
{
|
|
lock (typeof(VBScriptGenerator))
|
|
if (_builtinFunctions == null)
|
|
{
|
|
_builtinFunctions = new Set<string>(StringComparer.InvariantCultureIgnoreCase);
|
|
MethodInfo[] mis = typeof(BuiltInFunctions).GetMethods(BindingFlags.Public | BindingFlags.Static);
|
|
foreach (MethodInfo mi in mis)
|
|
{
|
|
_builtinFunctions.Add(mi.Name);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static Expression GenerateSimpleNameExpr(VB.SimpleName simpleName, AnalysisScope scope)
|
|
{
|
|
string name = simpleName.Name;
|
|
if (IsBuiltInConstants(name))
|
|
{
|
|
return Expression.Constant(getBuiltinConstant(name));
|
|
}
|
|
|
|
var param = FindIdDef(name, scope);
|
|
if (param != null)
|
|
{
|
|
return param;
|
|
}
|
|
else
|
|
{
|
|
return Expression.Dynamic(
|
|
scope.GetRuntime().GetGetMemberBinder(name),
|
|
typeof(object),
|
|
scope.GetModuleExpr()
|
|
);
|
|
}
|
|
}
|
|
|
|
private static Expression FindIdDef(string name, AnalysisScope scope)
|
|
{
|
|
return FindIdDef(name, scope, false);
|
|
}
|
|
|
|
// FindIdDef returns the ParameterExpr for the name by searching the scopes,
|
|
// or it returns None.
|
|
//
|
|
private static Expression FindIdDef(string name, AnalysisScope scope, bool generateIfNotFound)
|
|
{
|
|
var curscope = scope;
|
|
name = name.ToLower();
|
|
ParameterExpression res;
|
|
while (curscope != null)
|
|
{
|
|
if (curscope.Names.TryGetValue(name, out res))
|
|
{
|
|
return res;
|
|
}
|
|
else
|
|
{
|
|
curscope = curscope.Parent;
|
|
}
|
|
}
|
|
|
|
if (generateIfNotFound)
|
|
{
|
|
if (scope.ModuleScope.IsOptionExplicitOn)
|
|
throw new InvalidOperationException(string.Format("Must declare variable {0}.", name));
|
|
|
|
res = Expression.Parameter(typeof(object), name);
|
|
scope.ModuleScope.Names.Add(name, res);
|
|
return res;
|
|
}
|
|
|
|
if (scope == null)
|
|
{
|
|
throw new InvalidOperationException(
|
|
"Got bad AnalysisScope chain with no module at end.");
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private static List<Expression> GenerateArgumentList(VB.ArgumentCollection vbargs, AnalysisScope scope)
|
|
{
|
|
List<Expression> args = new List<Expression>();
|
|
if (vbargs != null)
|
|
{
|
|
//args.AddRange(vbargs.Select(a => GenerateExpr(a.Expression, scope)));
|
|
foreach (VB.Argument a in vbargs)
|
|
{
|
|
Expression argExp;
|
|
if (a != null && a.Expression != null)
|
|
{
|
|
argExp = GenerateExpr(a.Expression, scope);
|
|
}
|
|
else
|
|
{
|
|
argExp = Expression.Constant(null);
|
|
}
|
|
args.Add(argExp);
|
|
}
|
|
}
|
|
return args;
|
|
}
|
|
|
|
private static Expression GenerateSimpleNameAssignExpr(VB.SimpleNameExpression idExpr, Expression val, AnalysisScope scope)
|
|
{
|
|
string varName = idExpr.Name.Name;
|
|
var param = FindIdDef(varName, scope, true);
|
|
return Expression.Assign(
|
|
param,
|
|
Expression.Convert(val, param.Type));
|
|
}
|
|
|
|
private static LambdaExpression GenerateLambdaDef
|
|
(VB.ParameterCollection parms, VB.StatementCollection body,
|
|
AnalysisScope scope, string name, bool isSub)
|
|
{
|
|
var funscope = new AnalysisScope(scope, name);
|
|
var bodyscope = new AnalysisScope(funscope, name + " body");
|
|
bodyscope.IsLambdaBody = true;
|
|
funscope.IsLambda = true; // needed for return support.
|
|
var returnParameter = Expression.Parameter(typeof(object), name); //to support assign return value to func name
|
|
if (isSub)
|
|
{
|
|
funscope.MethodExit = Expression.Label();
|
|
}
|
|
else
|
|
{
|
|
funscope.MethodExit = Expression.Label(typeof(object));
|
|
bodyscope.Names[name] = returnParameter;
|
|
}
|
|
|
|
var paramsInOrder = new List<ParameterExpression>();
|
|
int parmCount = 0;
|
|
if (parms != null)
|
|
{
|
|
parmCount = parms.Count;
|
|
foreach (var p in parms)
|
|
{
|
|
Type paramType;
|
|
if (p.Modifiers != null && p.Modifiers.get_Item(0).ModifierType == AspClassic.Parser.ModifierTypes.ByVal)
|
|
{
|
|
paramType = typeof(object);
|
|
}
|
|
else
|
|
{
|
|
paramType = typeof(object).MakeByRefType();
|
|
}
|
|
var pe = Expression.Parameter(paramType, p.VariableName.Name.Name);
|
|
paramsInOrder.Add(pe);
|
|
funscope.Names[p.VariableName.Name.Name.ToLower()] = pe;
|
|
}
|
|
}
|
|
|
|
Expression bodyexpr = GenerateBlockExpr(body, bodyscope);
|
|
|
|
// Set up the Type arg array for the delegate type. Must include
|
|
// the return type as the last Type, which is object for Sympl defs.
|
|
var funcTypeArgs = new List<Type>();
|
|
for (int i = 0; i < parmCount + 1; i++)
|
|
{
|
|
funcTypeArgs.Add(typeof(object));
|
|
}
|
|
|
|
Expression lastExpression;
|
|
if (isSub)
|
|
{
|
|
//Return void
|
|
lastExpression = Expression.Empty();
|
|
//funcTypeArgs.Add(typeof(void));
|
|
}
|
|
else
|
|
{
|
|
lastExpression = returnParameter; //to return the function value
|
|
}
|
|
|
|
List<ParameterExpression> locals = new List<ParameterExpression>();
|
|
foreach (ParameterExpression local in bodyscope.Names.Values)
|
|
{
|
|
locals.Add(local);
|
|
}
|
|
|
|
LambdaExpression lambda;
|
|
//if (scope.GetRuntime().Debug)
|
|
//{
|
|
// Expression registerRuntimeVariables = GenerateRuntimeVariablesExpression(bodyscope);
|
|
|
|
// lambda = Expression.Lambda(
|
|
// Expression.Label(
|
|
// funscope.MethodExit,
|
|
// Expression.Block(locals, registerRuntimeVariables, bodyexpr, lastExpression)
|
|
// ),
|
|
// paramsInOrder);
|
|
//}
|
|
//else
|
|
//{
|
|
lambda = Expression.Lambda(
|
|
Expression.Label(
|
|
funscope.MethodExit,
|
|
Expression.Block(locals, bodyexpr, lastExpression)
|
|
),
|
|
paramsInOrder);
|
|
//}
|
|
//if (scope.GetRuntime().Debug)
|
|
//{
|
|
// return scope.GetRuntime().DebugContext.TransformLambda(lambda);
|
|
//}
|
|
//else
|
|
//{
|
|
return lambda;
|
|
//}
|
|
}
|
|
|
|
//public static Expression GenerateRuntimeVariablesExpression(AnalysisScope bodyscope)
|
|
//{
|
|
// List<string> namesInScope = new List<string>();
|
|
// List<ParameterExpression> parametersInScope = new List<ParameterExpression>();
|
|
// bodyscope.GetVariablesInScope(namesInScope, parametersInScope);
|
|
|
|
// //Expression
|
|
// RuntimeVariablesExpression runtimeVariables = Expression.RuntimeVariables(parametersInScope);
|
|
// Expression traceHelper = getTraceHelper(bodyscope);
|
|
// Expression registerRuntimeVariables = Expression.Call(
|
|
// traceHelper,
|
|
// typeof(ITrace).GetMethod("RegisterRuntimeVariables"),
|
|
// Expression.Constant(namesInScope.ToArray()),
|
|
// runtimeVariables
|
|
// );
|
|
// return registerRuntimeVariables;
|
|
//}
|
|
|
|
private static Expression WrapBooleanTest(Expression expr)
|
|
{
|
|
var tmp = Expression.Parameter(typeof(object), "testtmp");
|
|
return Expression.Block(
|
|
new ParameterExpression[] { tmp },
|
|
new Expression[]
|
|
{Expression.Assign(tmp, Expression
|
|
.Convert(expr, typeof(object))),
|
|
Expression.Condition(
|
|
Expression.TypeIs(tmp, typeof(bool)),
|
|
Expression.Convert(tmp, typeof(bool)),
|
|
Expression.Call(typeof(BuiltInFunctions).GetMethod("CBool"), tmp))});
|
|
}
|
|
|
|
private static Expression ConvertToIntegerArrayExpression(List<Expression> args)
|
|
{
|
|
List<Expression> converted = new List<Expression>();
|
|
foreach (Expression arg in args)
|
|
{
|
|
if (arg.Type == typeof(int))
|
|
{
|
|
converted.Add(arg);
|
|
}
|
|
else
|
|
{
|
|
converted.Add(Expression.Convert(arg, typeof(int)));
|
|
}
|
|
}
|
|
//return converted;
|
|
return Expression.NewArrayInit(typeof(int), converted);
|
|
}
|
|
|
|
internal static Expression WrapTryCatchExpression(Expression stmt, Dlrsoft.VBScript.Compiler.AnalysisScope scope)
|
|
{
|
|
ParameterExpression exception = Expression.Parameter(typeof(Exception));
|
|
|
|
return Expression.TryCatch(
|
|
Expression.Block(
|
|
stmt,
|
|
Expression.Empty()
|
|
),
|
|
Expression.Catch(
|
|
exception,
|
|
Expression.Call(
|
|
typeof(HelperFunctions).GetMethod("SetError"),
|
|
scope.ErrExpression,
|
|
exception
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
internal static Expression GenerateDebugInfo(VB.Tree stmt, AnalysisScope scope, out Expression clearDebugInfo)
|
|
{
|
|
ISourceMapper mapper = scope.ModuleScope.SourceMapper;
|
|
DocSpan docSpan = mapper.Map(SourceUtil.ConvertSpan(stmt.Span));
|
|
SourceLocation start = docSpan.Span.Start;
|
|
SourceLocation end = docSpan.Span.End;
|
|
SymbolDocumentInfo docInfo = scope.ModuleScope.GetDocumentInfo(docSpan.Uri);
|
|
|
|
//Expression debugInfo = Expression.DebugInfo(docInfo, start.Line, start.Column, end.Line, end.Column);
|
|
//clearDebugInfo = Expression.ClearDebugInfo(docInfo);
|
|
//return debugInfo;
|
|
|
|
Expression traceHelper = getTraceHelper(scope);
|
|
|
|
Expression debugInfo = Expression.Call(
|
|
traceHelper,
|
|
typeof(ITrace).GetMethod("TraceDebugInfo"),
|
|
Expression.Constant(docInfo.FileName),
|
|
Expression.Constant(start.Line),
|
|
Expression.Constant(start.Column),
|
|
Expression.Constant(end.Line),
|
|
Expression.Constant(end.Column)
|
|
);
|
|
clearDebugInfo = Expression.Empty();
|
|
return debugInfo;
|
|
}
|
|
|
|
private static Expression getTraceHelper(AnalysisScope scope)
|
|
{
|
|
Expression traceHelper = scope.TraceExpression;
|
|
return traceHelper;
|
|
}
|
|
|
|
// _findFirstLoop returns the first loop AnalysisScope or None.
|
|
//
|
|
private static AnalysisScope _findFirstScope(AnalysisScope scope, VB.BlockType blockType)
|
|
{
|
|
var curscope = scope;
|
|
while (curscope != null)
|
|
{
|
|
switch (blockType)
|
|
{
|
|
case AspClassic.Parser.BlockType.Sub:
|
|
case AspClassic.Parser.BlockType.Function:
|
|
if (curscope.IsLambda)
|
|
{
|
|
return curscope;
|
|
}
|
|
break;
|
|
case AspClassic.Parser.BlockType.Do:
|
|
if (curscope.IsDoLoop)
|
|
{
|
|
return curscope;
|
|
}
|
|
break;
|
|
case AspClassic.Parser.BlockType.For:
|
|
if (curscope.IsForLoop)
|
|
{
|
|
return curscope;
|
|
}
|
|
break;
|
|
}
|
|
curscope = curscope.Parent;
|
|
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Wrap around a DLR expression to inject into the VB expression tree
|
|
/// </summary>
|
|
class ExpressionExpression : VB.Expression
|
|
{
|
|
Expression _expression;
|
|
|
|
public ExpressionExpression(Expression expr, VB.Span span)
|
|
: base(VB.TreeType.AddressOfExpression, span)
|
|
{
|
|
_expression = expr;
|
|
}
|
|
|
|
public Expression Expression
|
|
{
|
|
get { return _expression; }
|
|
}
|
|
}
|
|
}
|
|
}
|