private static void GenerateNewExpression(ScriptGenerator generator, MemberSymbol symbol, NewExpression expression) { ScriptTextWriter writer = generator.Writer; if (expression.IsSpecificType) { string type = expression.AssociatedType.FullGeneratedName; if (type.Equals("Array")) { if (expression.Parameters == null) { writer.Write("[]"); } else if ((expression.Parameters.Count == 1) && (expression.Parameters[0].EvaluatedType == symbol.SymbolSet.ResolveIntrinsicType(IntrinsicType.Integer))) { writer.Write("new Array("); GenerateExpression(generator, symbol, expression.Parameters[0]); writer.Write(")"); } else { writer.Write("["); GenerateExpressionList(generator, symbol, expression.Parameters); writer.Write("]"); } return; } else if (type.Equals("Object")) { if (expression.Parameters == null) { writer.Write("{}"); } else { writer.WriteTrimmed("{ "); GenerateExpressionListAsNameValuePairs(generator, symbol, expression.Parameters); writer.WriteTrimmed(" }"); } return; } else if (expression.AssociatedType.Type == SymbolType.Record) { if (expression.AssociatedType.IgnoreNamespace == false) { writer.Write(expression.AssociatedType.GeneratedNamespace); writer.Write("."); } writer.Write("$create_"); writer.Write(expression.AssociatedType.GeneratedName); writer.Write("("); if (expression.Parameters != null) { GenerateExpressionList(generator, symbol, expression.Parameters); } writer.Write(")"); return; } } writer.Write("new "); if (expression.IsSpecificType) { writer.Write(expression.AssociatedType.FullGeneratedName); } else { GenerateExpression(generator, symbol, expression.TypeExpression); } writer.Write("("); if (expression.Parameters != null) { GenerateExpressionList(generator, symbol, expression.Parameters); } writer.Write(")"); }
private Expression ProcessOpenParenExpressionNode(BinaryExpressionNode node) { bool isDelegateInvoke = false; Expression leftExpression = BuildExpression(node.LeftChild); if (leftExpression is LocalExpression) { Debug.Assert(leftExpression.EvaluatedType.Type == SymbolType.Delegate); // Handle the implicit delegate invoke scenario by turning the expression // into an explicit call into the delegate's Invoke method MemberSymbol invokeMethodSymbol = leftExpression.EvaluatedType.GetMember("Invoke"); Debug.Assert(invokeMethodSymbol != null); leftExpression = new MemberExpression(leftExpression, invokeMethodSymbol); isDelegateInvoke = true; } if (leftExpression.Type != ExpressionType.Member) { // A method call was evaluated into a non-member expression as part of building // the left node. For example, Nullable<T>.GetValueOrDefault() becomes a logical or expression. return leftExpression; } MemberExpression memberExpression = (MemberExpression)leftExpression; ExpressionListNode argNodes = null; List<Expression> args = null; if (node.RightChild != null) { Debug.Assert(node.RightChild is ExpressionListNode); argNodes = (ExpressionListNode)node.RightChild; args = BuildExpressionList(argNodes); } MethodExpression methodExpression = null; if (memberExpression.Member.Type != SymbolType.Method) { // A non-method member is being used in a method call; the member must be // a delegate... Debug.Assert(memberExpression.EvaluatedType.Type == SymbolType.Delegate); Expression instanceExpression = TransformMemberExpression(memberExpression); MethodSymbol invokeMethod = (MethodSymbol)memberExpression.EvaluatedType.GetMember("Invoke"); Debug.Assert(invokeMethod != null); methodExpression = new MethodExpression(instanceExpression, invokeMethod); isDelegateInvoke = true; } else { // The member being accessed is a method... // REVIEW: Uggh... this has become too complex over time with all the transformations // added over time. Refactoring needed... MethodSymbol method = (MethodSymbol)memberExpression.Member; TypeSymbol objectType = _symbolSet.ResolveIntrinsicType(IntrinsicType.Object); TypeSymbol typeType = _symbolSet.ResolveIntrinsicType(IntrinsicType.Type); TypeSymbol dictionaryType = _symbolSet.ResolveIntrinsicType(IntrinsicType.Dictionary); TypeSymbol genericDictionaryType = _symbolSet.ResolveIntrinsicType(IntrinsicType.GenericDictionary); TypeSymbol numberType = _symbolSet.ResolveIntrinsicType(IntrinsicType.Number); TypeSymbol stringType = _symbolSet.ResolveIntrinsicType(IntrinsicType.String); TypeSymbol scriptType = _symbolSet.ResolveIntrinsicType(IntrinsicType.Script); TypeSymbol argsType = _symbolSet.ResolveIntrinsicType(IntrinsicType.Arguments); if ((method.Parent == objectType) && method.Name.Equals("GetType", StringComparison.Ordinal)) { // Since we can't extend object's prototype, we need to transform the // natural c# syntax into a static method. // Object.GetType instance method becomes Type.GetInstanceType static method TypeSymbol typeSymbol = typeType; method = (MethodSymbol)typeSymbol.GetMember("GetInstanceType"); Debug.Assert(method != null); methodExpression = new MethodExpression(new TypeExpression(typeSymbol, SymbolFilter.Public | SymbolFilter.StaticMembers), method); methodExpression.AddParameterValue(memberExpression.ObjectReference); return methodExpression; } else if ((method.Parent == objectType) && (method.Name.Equals("ToString", StringComparison.Ordinal) || method.Name.Equals("ToLocaleString", StringComparison.Ordinal)) && (memberExpression.ObjectReference.EvaluatedType == stringType)) { // No-op ToString calls on strings (this happens when performing a ToString // on a named enum value. return memberExpression.ObjectReference; } else if ((method.Parent == objectType) && (method.Name.Equals("ToString", StringComparison.Ordinal) || method.Name.Equals("ToLocaleString", StringComparison.Ordinal)) && (memberExpression.ObjectReference.EvaluatedType is EnumerationSymbol)) { EnumerationSymbol enumSymbol = (EnumerationSymbol)memberExpression.ObjectReference.EvaluatedType; if (enumSymbol.UseNamedValues) { // If the enum value is a named enum, then it is already a string. return memberExpression.ObjectReference; } return new MethodExpression(memberExpression.ObjectReference, method); } else if (((method.Parent == dictionaryType) || (method.Parent == genericDictionaryType)) && method.Name.Equals("ContainsKey", StringComparison.Ordinal)) { // Switch the instance ContainsKey method on Dictionary to // calls to the static KeyExists method. Debug.Assert(args.Count == 1); MethodSymbol keyExistsMethod = (MethodSymbol)dictionaryType.GetMember("KeyExists"); Debug.Assert(keyExistsMethod != null); methodExpression = new MethodExpression(new TypeExpression(dictionaryType, SymbolFilter.Public | SymbolFilter.StaticMembers), keyExistsMethod); methodExpression.AddParameterValue(memberExpression.ObjectReference); methodExpression.AddParameterValue(args[0]); return methodExpression; } else if (((method.Parent == dictionaryType) || (method.Parent == genericDictionaryType)) && method.Name.Equals("Remove", StringComparison.Ordinal)) { // Switch the instance Remove method on Dictionary to // calls to the static DeleteKey method. Debug.Assert(args.Count == 1); return new LateBoundExpression(memberExpression.ObjectReference, args[0], LateBoundOperation.DeleteField, objectType); } else if (((method.Parent == dictionaryType) || (method.Parent == genericDictionaryType)) && method.Name.Equals("Clear")) { // Switch the instance Clear method on Dictionary to // calls to the static ClearKeys method. MethodSymbol clearKeysMethod = (MethodSymbol)dictionaryType.GetMember("ClearKeys"); Debug.Assert(clearKeysMethod != null); methodExpression = new MethodExpression(new TypeExpression(dictionaryType, SymbolFilter.Public | SymbolFilter.StaticMembers), clearKeysMethod); methodExpression.AddParameterValue(memberExpression.ObjectReference); return methodExpression; } else if (((method.Parent == dictionaryType) || (((TypeSymbol)method.Parent).GenericType == genericDictionaryType)) && ((method.Visibility & MemberVisibility.Static) != 0) && method.Name.Equals("GetDictionary", StringComparison.Ordinal)) { // Dictionary.GetDictionary is a no-op method; we're just interested // in the object being passed in. // However we'll re-evaluate the argument to be of dictionary type // so that subsequent use of this expression sees it as a dictionary. Debug.Assert(args.Count == 1); args[0].Reevaluate((TypeSymbol)method.Parent); return args[0]; } else if ((method.Parent == stringType) && (method.Name.Equals("Escape", StringComparison.Ordinal) || method.Name.Equals("Unescape", StringComparison.Ordinal) || method.Name.Equals("EncodeUri", StringComparison.Ordinal) || method.Name.Equals("DecodeUri", StringComparison.Ordinal) || method.Name.Equals("EncodeUriComponent", StringComparison.Ordinal) || method.Name.Equals("DecodeUriComponent", StringComparison.Ordinal))) { // String.<Method> becomes Script.<Method>, as its a global method in script MethodSymbol scriptMethod = (MethodSymbol)scriptType.GetMember(method.Name); methodExpression = new MethodExpression(new TypeExpression(scriptType, SymbolFilter.Public | SymbolFilter.StaticMembers), scriptMethod); methodExpression.AddParameterValue(memberExpression.ObjectReference); return methodExpression; } else if ((method.Parent == scriptType) && method.Name.Equals("Literal", StringComparison.Ordinal)) { // Convert a call to Script.Literal into a literal expression Debug.Assert(args.Count >= 1); string script = null; if (args[0].Type == ExpressionType.Field) { Debug.Assert(args[0].EvaluatedType == stringType); FieldSymbol field = ((FieldExpression)args[0]).Field; if (field.IsConstant) { Debug.Assert(field.Value is string); script = (string)field.Value; } } else if (args[0].Type == ExpressionType.Literal) { Debug.Assert(((LiteralExpression)args[0]).Value is string); script = (string)((LiteralExpression)args[0]).Value; } if (script == null) { // TODO: When we start raising errors at the expression level instead of the statement // level, we should return an ErrorExpression instead of a dummy expression. Token argToken = argNodes.Expressions[0].Token; _errorHandler.ReportError("The argument to Script.Literal must be a constant string.", argToken.Location); return new InlineScriptExpression("", objectType); } InlineScriptExpression scriptExpression = new InlineScriptExpression(script, objectType); for (int i = 1; i < args.Count; i++) { scriptExpression.AddParameterValue(args[i]); } return scriptExpression; } else if ((method.Parent == scriptType) && method.Name.Equals("Boolean", StringComparison.Ordinal)) { Debug.Assert(args.Count == 1); return new UnaryExpression(Operator.LogicalNot, new UnaryExpression(Operator.LogicalNot, args[0])); } else if ((method.Parent == scriptType) && method.Name.Equals("Value", StringComparison.Ordinal)) { Debug.Assert(args.Count >= 2); Expression expr = args[0]; for (int i = 1; i < args.Count; i++) { expr = new BinaryExpression(Operator.LogicalOr, expr, args[i]); } expr.Reevaluate(args[0].EvaluatedType); expr.AddParenthesisHint(); return expr; } else if (method.Parent == argsType) { if (method.Name.Equals("GetArgument", StringComparison.Ordinal)) { // Switch Arguments.GetArgument into Arguments[] Debug.Assert(args.Count == 1); IndexerExpression indexExpression = new IndexerExpression(new LiteralExpression(typeType, method.Parent), ((ClassSymbol)method.Parent).GetIndexer()); indexExpression.AddIndexParameterValue(args[0]); return indexExpression; } else if (method.Name.Equals("ToArray", StringComparison.Ordinal)) { // Switch Arguments.ToArray into Array.ToArray(arguments) TypeSymbol arrayType = _symbolSet.ResolveIntrinsicType(IntrinsicType.Array); MethodSymbol toArrayMethod = (MethodSymbol)arrayType.GetMember("ToArray"); InlineScriptExpression argsExpression = new InlineScriptExpression("arguments", objectType, /* parenthesize */ false); MethodExpression toArrayExpression = new MethodExpression(new TypeExpression(arrayType, SymbolFilter.Public | SymbolFilter.StaticMembers), toArrayMethod); toArrayExpression.AddParameterValue(argsExpression); return toArrayExpression; } } else { bool lateBound = false; if (method.Parent == scriptType) { LateBoundOperation lateBoundOperation = LateBoundOperation.InvokeMethod; if (method.Name.Equals("InvokeMethod", StringComparison.Ordinal)) { lateBound = true; } else if (method.Name.Equals("DeleteField", StringComparison.Ordinal)) { lateBound = true; lateBoundOperation = LateBoundOperation.DeleteField; } else if (method.Name.Equals("GetField", StringComparison.Ordinal)) { lateBound = true; lateBoundOperation = LateBoundOperation.GetField; } else if (method.Name.Equals("SetField", StringComparison.Ordinal)) { lateBound = true; lateBoundOperation = LateBoundOperation.SetField; } else if (method.Name.Equals("GetScriptType", StringComparison.Ordinal)) { lateBound = true; lateBoundOperation = LateBoundOperation.GetScriptType; } else if (method.Name.Equals("HasField", StringComparison.Ordinal)) { lateBound = true; lateBoundOperation = LateBoundOperation.HasField; } else if (method.Name.Equals("HasMethod", StringComparison.Ordinal)) { lateBound = true; lateBoundOperation = LateBoundOperation.HasMethod; } else if (method.Name.Equals("CreateInstance", StringComparison.Ordinal)) { Debug.Assert(args.Count >= 1); if ((args[0].Type == ExpressionType.MethodInvoke) || (args[0].Type == ExpressionType.PropertyGet)) { // When using the result of a method call/property access directly // with Type.CreateInstance, the following script would be generated: // // new method()() // which is invalid. Instead we need to generate the following: // var type = method(); // new type() _errorHandler.ReportError("You must store the type returned from a method or property into a local variable to use with Type.CreateInstance.", node.Token.Location); } NewExpression newExpression = new NewExpression(args[0], objectType); if (args.Count > 1) { bool first = true; foreach (Expression paramExpr in args) { if (first) { first = false; continue; } newExpression.AddParameterValue(paramExpr); } } return newExpression; } if (lateBound) { // Switch explicit late-bound calls into implicit late-bound expressions // in script Debug.Assert((args != null) && (((lateBoundOperation == LateBoundOperation.GetScriptType) && (args.Count == 1)) || (args.Count >= 2))); LateBoundExpression lateBoundExpression = null; Expression instanceExpression = null; Expression nameExpression = null; foreach (Expression paramExpr in args) { if (instanceExpression == null) { instanceExpression = paramExpr; if (lateBoundOperation == LateBoundOperation.GetScriptType) { // GetScriptType only takes an instance return new LateBoundExpression(instanceExpression, null, lateBoundOperation, objectType); } continue; } if (nameExpression == null) { nameExpression = paramExpr; Expression objectExpression = instanceExpression; if (lateBoundOperation == LateBoundOperation.InvokeMethod) { if ((instanceExpression.Type == ExpressionType.Literal) && (((LiteralExpression)instanceExpression).Value == null)) { objectExpression = null; LiteralExpression literalExpression = nameExpression as LiteralExpression; if (literalExpression == null) { _errorHandler.ReportError("The name of a global method must be a constant string known at compile time.", argNodes.Expressions[0].Token.Location); } else if (!Utility.IsValidIdentifier((string)literalExpression.Value)) { _errorHandler.ReportError("The name of a global method must be a valid identifer.", argNodes.Expressions[0].Token.Location); } } } lateBoundExpression = new LateBoundExpression(objectExpression, nameExpression, lateBoundOperation, objectType); continue; } lateBoundExpression.AddParameterValue(paramExpr); } Debug.Assert(lateBoundExpression != null); return lateBoundExpression; } } if (!method.MatchesConditions(_options.Defines)) { return null; } methodExpression = new MethodExpression(memberExpression.ObjectReference, method); } } if (args != null) { foreach (Expression paramExpr in args) { methodExpression.AddParameterValue(paramExpr); } } if (isDelegateInvoke) { return new DelegateInvokeExpression(methodExpression); } return methodExpression; }
private Expression ProcessArrayNewNode(ArrayNewNode node) { TypeSymbol itemTypeSymbol = _symbolSet.ResolveType(node.TypeReference, _symbolTable, _symbolContext); Debug.Assert(itemTypeSymbol != null); TypeSymbol arrayTypeSymbol = _symbolSet.CreateArrayTypeSymbol(itemTypeSymbol); if (node.InitializerExpression == null) { if (node.ExpressionList == null) { return new LiteralExpression(arrayTypeSymbol, new Expression[0]); } else { Debug.Assert(node.ExpressionList.NodeType == ParseNodeType.ExpressionList); ExpressionListNode argsList = (ExpressionListNode)node.ExpressionList; Debug.Assert(argsList.Expressions.Count == 1); Expression sizeExpression = BuildExpression(argsList.Expressions[0]); if (sizeExpression is MemberExpression) { sizeExpression = TransformMemberExpression((MemberExpression)sizeExpression); } NewExpression newExpr = new NewExpression(arrayTypeSymbol); newExpr.AddParameterValue(sizeExpression); return newExpr; } } else { ArrayInitializerNode initializerNode = (ArrayInitializerNode)node.InitializerExpression; Expression[] values = new Expression[initializerNode.Values.Count]; int i = 0; foreach (ParseNode valueNode in initializerNode.Values) { values[i] = BuildExpression(valueNode); if (values[i] is MemberExpression) { values[i] = TransformMemberExpression((MemberExpression)values[i]); } i++; } return new LiteralExpression(arrayTypeSymbol, values); } }
private Expression ProcessNewNode(NewNode node) { TypeSymbol type = _symbolSet.ResolveType(node.TypeReference, _symbolTable, _symbolContext); Debug.Assert(type != null); if (type.Type == SymbolType.Delegate) { // If its an instantiation of a delegate, return the argument itself // which is already a delegate expression. // The delegate type itself does not have a runtime representation that // is instantiated. Debug.Assert(node.Arguments is ExpressionListNode); Debug.Assert(((ExpressionListNode)node.Arguments).Expressions.Count == 1); return BuildExpression(((ExpressionListNode)node.Arguments).Expressions[0]); } NewExpression newExpression = new NewExpression(type); if (node.Arguments != null) { Debug.Assert(node.Arguments is ExpressionListNode); List<Expression> args = BuildExpressionList((ExpressionListNode)node.Arguments); if ((type == _symbolSet.ResolveIntrinsicType(IntrinsicType.Function)) && (args.Count > 1)) { // The function ctor in javascript takes parameters in // reverse order, with the params array effectively at // the start of the param list... in c# we must keep the params // list at the end, so skip the first parameter, add the rest, // and then add back the first parameter (the body of the // function). Uggh! Expression functionBodyParam = null; foreach (Expression paramExpr in args) { if (functionBodyParam == null) { functionBodyParam = paramExpr; continue; } newExpression.AddParameterValue(paramExpr); } newExpression.AddParameterValue(functionBodyParam); } else if (type.GenericType == _symbolSet.ResolveIntrinsicType(IntrinsicType.Nullable)) { // Creating a new Nullable<T> ... if there is a value specified as a ctor param, // just use the value; otherwise use undefined. if (args.Count == 1) { return args[0]; } return new InlineScriptExpression("undefined", type); } else { foreach (Expression paramExpr in args) { newExpression.AddParameterValue(paramExpr); } } } if (String.IsNullOrEmpty(type.DependencyName) == false) { _options.AddExecutionDependency(type.DependencyName); } return newExpression; }
private static void GenerateNewExpression(ScriptGenerator generator, MemberSymbol symbol, NewExpression expression) { ScriptTextWriter writer = generator.Writer; if (expression.IsSpecificType) { string type = expression.AssociatedType.FullGeneratedName; if (type.Equals("Array")) { if (expression.Parameters == null) { writer.Write("[]"); } else if ((expression.Parameters.Count == 1) && (expression.Parameters[0].EvaluatedType == symbol.SymbolSet.ResolveIntrinsicType(IntrinsicType.Integer))) { writer.Write("new Array("); GenerateExpression(generator, symbol, expression.Parameters[0]); writer.Write(")"); } else { writer.Write("["); GenerateExpressionList(generator, symbol, expression.Parameters); writer.Write("]"); } return; } else if (type.Equals("Object")) { if (expression.Parameters == null) { writer.Write("{}"); } else { writer.Write("{ "); GenerateExpressionListAsNameValuePairs(generator, symbol, expression.Parameters); writer.Write(" }"); } return; } else if (type.Equals("Tuple")) { if ((expression.Parameters == null) || (expression.Parameters.Count == 0)) { writer.Write("{ }"); } else { writer.Write("{ "); for (int i = 0; i < expression.Parameters.Count; i++) { if (i != 0) { writer.Write(", "); } writer.Write("item"); writer.Write(i + 1); writer.Write(": "); GenerateExpression(generator, symbol, expression.Parameters[i]); } writer.Write(" }"); } return; } else if (expression.AssociatedType.Type == SymbolType.Record) { if (expression.AssociatedType.IsApplicationType && ((RecordSymbol)expression.AssociatedType).Constructor == null) { writer.Write("{ }"); return; } writer.Write(expression.AssociatedType.FullGeneratedName); writer.Write("("); if (expression.Parameters != null) { GenerateExpressionList(generator, symbol, expression.Parameters); } writer.Write(")"); return; } } writer.Write("new "); if (expression.IsSpecificType) { writer.Write(expression.AssociatedType.FullGeneratedName); } else { GenerateExpression(generator, symbol, expression.TypeExpression); } writer.Write("("); if (expression.Parameters != null) { GenerateExpressionList(generator, symbol, expression.Parameters); } writer.Write(")"); }