Пример #1
0
        public JsVariableDeclarator HoistVariable(LiftedVariableKey symbol)
        {
            var identifier = symbol.Identifier;

            if (hoistedVariables.ContainsKey(symbol) || hoistedVariables.ContainsKey(new LiftedVariableKey(symbol.Identifier)))
            {
                identifier = GenerateNewNamePrivate(symbol);
                symbol     = new LiftedVariableKey(identifier, symbol.Symbol);
            }

            // Register the variable so we avoid collisions.
            hoistedVariables[symbol] = identifier;
            if (symbol.Symbol == null)
            {
                hoistedVariables[new LiftedVariableKey(symbol.Identifier)] = identifier;
            }

            // Declare a local variable (of the top-level function so available as closures to the state
            // machine) to store the symbol.
            var declaration = stateMachineBody.Local(identifier, Js.Null());

            // If we have a true symbol associated with the key, then declare it in the base transformer
            if (symbol.Symbol != null)
            {
                transformer.DeclareInCurrentScope(symbol.Symbol, declaration);
            }

            return(declaration);
        }
Пример #2
0
 public JsNode VisitNullNode(AstNode node)
 {
     if (node == Statement.Null)
     {
         return(Js.Null().Statement());
     }
     return(Js.Null());
 }
Пример #3
0
        public static JsExpression EntityTypeRefToMember(IType typeRef, bool isGenericArgument)
        {
            if (typeRef == null)
            {
                return(null);
            }
            if (typeRef.Kind == TypeKind.Anonymous)
            {
                return(Js.Null());
            }
            if (isGenericArgument)
            {
                var code = Sk.GetGenericArugmentJsCode(typeRef.GetEntityType());
                if (code != null)
                {
                    return(Js.CodeExpression(code));
                }
            }
            var name = GetEntityJsName(typeRef);

            if (Sk.IsJsonMode(typeRef.GetEntityType()))
            {
                return(Js.String(name));
            }
            var member = Js.Members(name);

            if (typeRef.IsGenericTypeParameter())
            {
                if (member.PreviousMember == null)
                {
                    member.PreviousMember = Js.This();
                }
                else
                {
                    throw new Exception();
                }
            }
            var def = typeRef.GetDefinitionOrArrayType();

            if (def != null && Sk.IsClrType(def))
            {
                member = member.Member("ctor");
            }
            return(member);
        }
Пример #4
0
 protected JsExpression Serialize(object obj)
 {
     if (obj == null)
     {
         return(Js.Null());
     }
     if (obj is JsExpression)
     {
         return((JsExpression)obj);
     }
     else if (obj is Dictionary <string, object> )
     {
         var obj2 = Js.Json();
         var dic  = (Dictionary <string, object>)obj;
         dic.ForEach(pair => obj2.Add(pair.Key, Serialize(pair.Value)));
         return(obj2);
     }
     else if (obj is IList)
     {
         var list  = (IList)obj;
         var array = Js.NewJsonArray(list.Cast <object>().Select(Serialize).ToArray());
         return(array);
     }
     else if (obj is Enum)
     {
         return(Js.String(obj.ToString()));
     }
     else if (obj is string || obj is bool || obj is int)
     {
         return(Js.Value(obj));
     }
     else
     {
         var json = Js.Json();
         obj.GetType().GetProperties().ForEach(pe =>
         {
             var value = pe.GetValue(obj, null);
             if (value != null)
             {
                 json.Add(pe.Name, Serialize(value));
             }
         });
         return(json);
     }
 }
Пример #5
0
        private JsBlockStatement CreateProperty(IPropertySymbol property)
        {
            var block        = new JsBlockStatement();
            var propertyName = property.GetMemberName();

            var backingField   = property.GetBackingFieldName();
            var valueParameter = Js.Parameter("value");

            block.Add(idioms.StoreInPrototype(backingField, Js.Null()));
            if (property.GetMethod != null)
            {
                block.Add(idioms.StoreInPrototype(property.GetMethod.GetMemberName(), Js.Function().Body(Js.Return(Js.This().Member(backingField)))));
            }
            if (property.SetMethod != null)
            {
                block.Add(idioms.StoreInPrototype(property.SetMethod.GetMemberName() + propertyName, Js.Function(valueParameter).Body(
                                                      Js.Assign(Js.This().Member(backingField), valueParameter.GetReference()))));
            }

            return(block);
        }
Пример #6
0
 public JsNode VisitResolveResult(ResolveResult res)
 {
     if (res.Type == SpecialType.NullType)
     {
         return(Js.Null());
     }
     else if (res.Type.IsVoid())
     {
         Log.Warn("void");
         return(Js.CodeExpression("void"));
     }
     else if (res.Type.Kind == TypeKind.Dynamic)
     {
         var info = res.GetInfo();
         if (info == null || info.Nodes.Count != 1)
         {
             throw new NotImplementedException("Dynamics");
         }
         var node2 = Visit(info.Nodes[0]);
         return(node2);
     }
     throw new NotImplementedException();
 }
Пример #7
0
        public async Task Compile()
        {
            projectName = project.AssemblyName;
            Compilation compilation = await Profiler.Time("Getting initial project compilation", async() => await project.GetCompilationAsync());

            Context.Update(project.Solution, project, compilation, new ReflectionCache(project, compilation));

            // If this is the runtime prjoect, declare the array to hold all the GetAssembly functions (this .js file
            // will be loaded first, and we only want to bother creating the array once.)
            if (projectName == "mscorlib")
            {
                var global = new JsBlockStatement();
                jsCompilationUnit.Global = global;

                var assemblies = Js.Variable(SpecialNames.Assemblies, Js.Array());
                global.Local(assemblies);

                // This ensures that Function.$typeName returns `Function` -- this is important when using
                // a type function as a generic argument, since otherwise when we try to get a
                // unique key for the permuatation of type args including a type function, we would get
                // an empty string for that arg, which would break the cache.
                jsCompilationUnit.Body.Assign(Js.Reference("Function").Member(SpecialNames.TypeName), Js.Primitive("Function"));
            }

            // Declare assembly variable
            var assemblyVariable = Js.Variable("$" + projectName.MaskSpecialCharacters() + "$Assembly", Js.Null());

            jsCompilationUnit.Body.Local(assemblyVariable);

            // Declare array to store all anonymous types
            var anonymousTypes = Js.Variable(compilation.Assembly.GetAssemblyAnonymousTypesArray(), Js.Array());

            jsCompilationUnit.Body.Local(anonymousTypes);

            // Declare array to store all the type functions in the assembly
            var assemblyTypes = Js.Variable(compilation.Assembly.GetAssemblyTypesArray(), Js.Array());

            jsCompilationUnit.Body.Local(assemblyTypes);

            // Build $GetAssemblyMethod, which lazily creates a new Assembly instance
            var globalIdioms = new Idioms(null);
            var getAssembly  = Js.Function();

            getAssembly.Body.If(
                assemblyVariable.GetReference().EqualTo(Js.Null()),
                assemblyVariable.GetReference().Assign(globalIdioms.CreateAssembly(compilation.Assembly, assemblyTypes.GetReference()))
                );
            getAssembly.Body.Return(assemblyVariable.GetReference());
            jsCompilationUnit.Body.Assign(
                Js.Reference(compilation.Assembly.GetAssemblyMethodName()),
                getAssembly);

            // Declare $assembly variable
            jsCompilationUnit.Body.Local(SpecialNames.Assembly, Js.Reference(compilation.Assembly.GetAssemblyMethodName()));
            jsCompilationUnit.Body.Assign(Js.Reference(SpecialNames.Assembly).Member(SpecialNames.AssemblyTypesArray), assemblyTypes.GetReference());

            // Add $GetAssemblyMethod to global assemblies array
            jsCompilationUnit.Body.Express(Js.Reference(SpecialNames.Assemblies).Member("push").Invoke(Js.Reference(SpecialNames.Assembly)));

            // Builds out all the namespace objects.  Types live inside namepsaces, which are represented as
            // nested Javascript objects.  For example, System.Text.StringBuilder is represented (in part) as:
            //
            // System = {};
            // System.Text = {};
            // System.Text.StringBuilder = function() { ... }
            //
            // This allows access to classes using dot notation in the expected way.
            Profiler.Time("Transforming namespaces", () =>
            {
                var namespaceTransformer = new NamespaceTransformer(jsCompilationUnit.Body);
                foreach (var syntaxTree in compilation.SyntaxTrees)
                {
                    var compilationUnit = (CompilationUnitSyntax)syntaxTree.GetRoot();
                    compilationUnit.Accept(namespaceTransformer);
                }
            });

            var actions = new List <Tuple <INamedTypeSymbol, Action> >();

            Profiler.Time("Get diagnostics", () =>
            {
                var diagnostics = compilation.GetDiagnostics();
                foreach (var diagnostic in diagnostics)
                {
                    if (diagnostic.Severity == DiagnosticSeverity.Error)
                    {
                        Console.WriteLine("// " + diagnostic);
                    }
                }
            });

            // Check for partial classes
            Profiler.Time("Reassemble partial classes", () =>
            {
                var partialClassReassembler = new PartialClassReassembler(project, compilation);
                compilation = partialClassReassembler.UnifyPartialTypes();
            });

/*
 *          // Write out all type functions in inheritance order.  This allows for complex references between types and
 *          // nested types.
 *          Profiler.Time("Write out type function declarations", () =>
 *          {
 *              var allTypeDeclarations = new List<INamedTypeSymbol>();
 *              foreach (var syntaxTree in compilation.SyntaxTrees)
 *              {
 *                  var semanticModel = compilation.GetSemanticModel(syntaxTree);
 *                  var compilationUnit = (CompilationUnitSyntax)syntaxTree.GetRoot();
 *                  var typeDeclarations = GetTypeDeclarations(compilationUnit);
 *                  var types = typeDeclarations.Select(x => semanticModel.GetDeclaredSymbol(x)).ToArray();
 *                  allTypeDeclarations.AddRange(types);
 *              }
 *              SweepSort(allTypeDeclarations, x => x);
 *
 *              jsCompilationUnit.Body.Express(Js.Reference(Context.Instance.SymbolNames[classType.ContainingNamespace, classType.ContainingNamespace.GetFullName()]).Member(classType.GetShortTypeName()),
 *                      Js.Reference(SpecialNames.Define).Invoke(Js.Primitive(displayName), baseType));
 *              jsCompilationUnit.Assign(Js.Reference(Context.Instance.SymbolNames[classType.ContainingNamespace, classType.ContainingNamespace.GetFullName()]).Member(classType.GetShortTypeName()),
 *                      Js.Reference(SpecialNames.Define).Invoke(Js.Primitive(displayName), baseType));
 *          });
 */

            // Scan all syntax trees for anonymous type creation expressions.  We transform them into class
            // declarations with a series of auto implemented properties.
            Profiler.Time("Running AnonymousTypeTransformer", () =>
            {
                var anonymousTypeTransformer = new AnonymousTypeTransformer(jsCompilationUnit.Body, actions);
                foreach (var syntaxTree in compilation.SyntaxTrees)
                {
                    var compilationUnit = (CompilationUnitSyntax)syntaxTree.GetRoot();
                    compilationUnit.Accept(anonymousTypeTransformer);
                }
            });

            // Iterate through all the syntax trees and add entries into `actions` that correspond to type
            // declarations.
            Profiler.Time("Preparing for core transformation process", () =>
            {
                foreach (var syntaxTree in compilation.SyntaxTrees)
                {
                    var semanticModel   = compilation.GetSemanticModel(syntaxTree);
                    var compilationUnit = (CompilationUnitSyntax)syntaxTree.GetRoot();
                    var transformer     = new JsTransformer(syntaxTree, semanticModel, jsCompilationUnit);

                    var typeCollector = new TypeCollector();
                    compilationUnit.Accept(typeCollector);
                    var typeDeclarations     = typeCollector.TypeDeclarations.Where(x => x.GetContainingTypeDeclaration() == null);
                    var delegateDeclarations = typeCollector.DelegateDeclarations.Where(x => x.GetContainingTypeDeclaration() == null);

                    foreach (var type in typeDeclarations)
                    {
                        var _type      = type;
                        var typeSymbol = semanticModel.GetDeclaredSymbol(type);
                        Action action  = () =>
                        {
                            var statements = (JsBlockStatement)_type.Accept(transformer);
                            jsCompilationUnit.Body.Aggregate(statements);
                        };
                        actions.Add(Tuple.Create(typeSymbol, action));
                    }
                    foreach (var type in delegateDeclarations)
                    {
                        var _type     = type;
                        Action action = () =>
                        {
                            var statements = (JsBlockStatement)_type.Accept(transformer);
                            jsCompilationUnit.Body.Aggregate(statements);
                        };
                        actions.Add(Tuple.Create((INamedTypeSymbol)ModelExtensions.GetDeclaredSymbol(semanticModel, type), action));
                    }
                }
            });

            // Sort all the type declarations such that base types always come before subtypes.
            Profiler.Time("Sorting transformers", () => SweepSort(actions, x => x.Item1));

            var transformationActions = actions.Select(x => x.Item2).ToArray();

            Profiler.Time("Applying core transformation", () =>
            {
                foreach (var item in transformationActions)
                {
                    item();
                }
            });

            // Create cultures based on installed .NET cultures.  Presumably this is the same regardless
            // of the platform that compiled this assembly.  Only do this for the standard library.
            if (projectName == "mscorlib" && !Context.Instance.Compilation.Assembly.IsCultureInfoExportDisabled())
            {
                foreach (var culture in CultureInfo.GetCultures(CultureTypes.AllCultures))
                {
                    JsExpression target = new JsVariableReferenceExpression(Context.Instance.CultureInfo.GetTypeName()).Invoke().Member("RegisterCulture");
                    jsCompilationUnit.Body.Add(target.Invoke(new[]
                    {
                        Js.Literal(culture.Name),
                        Js.Literal(culture.DateTimeFormat.ShortDatePattern),
                        Js.Literal(culture.DateTimeFormat.LongDatePattern),
                        Js.Literal(culture.DateTimeFormat.ShortTimePattern),
                        Js.Literal(culture.DateTimeFormat.LongTimePattern),
                        Js.Literal(culture.DateTimeFormat.FullDateTimePattern),
                        Js.Literal(culture.DateTimeFormat.YearMonthPattern),
                        Js.Array(culture.DateTimeFormat.MonthNames.Select(x => Js.Literal(x)).ToArray()),
                        Js.Array(culture.DateTimeFormat.AbbreviatedMonthNames.Select(x => Js.Literal(x)).ToArray()),
                        Js.Array(culture.DateTimeFormat.DayNames.Select(x => Js.Literal(x)).ToArray())
                    }).Express());
                }
            }

            // If the project type is a console application, then invoke the Main method at the very
            // end of the file.
            var entryPoint = Context.Instance.Compilation.GetEntryPoint(CancellationToken.None);

            if (entryPoint != null)
            {
                jsCompilationUnit.Body.Express(globalIdioms.InvokeStatic(entryPoint));
            }

            // Test minification
//            var minifier = new JsMinifier();
//            jsCompilationUnit.Accept(minifier);
        }
Пример #8
0
            public override JsNode VisitAwaitExpression(AwaitExpressionSyntax node)
            {
                var operand = (JsExpression)node.Expression.Accept(this);

/*
 *              var expressionInfo = stateGenerator.Transformer.model.GetAwaitExpressionInfo(node);
 *              if (expressionInfo.GetResultMethod == null)
 *              {
 *                  var classText = node.FirstAncestorOrSelf<ClassDeclarationSyntax>().NormalizeWhitespace().ToString();
 *                  var diagnostics = model.GetDiagnostics().Select(x => x.ToString()).ToArray();
 *              }
 *
 *              var returnsVoid = expressionInfo.GetResultMethod.ReturnsVoid;
 */
                var expressionInfo    = stateGenerator.Transformer.model.GetTypeInfo(node).ConvertedType;
                var returnsVoid       = expressionInfo.SpecialType == SpecialType.System_Void;
                var operandType       = model.GetTypeInfo(node.Expression).ConvertedType;
                var awaiterMethodName = ((INamedTypeSymbol)operandType).GetMethodByName("GetAwaiter").GetMemberName();

                // Store the awaiter in a field
                var awaiterIdentifier = stateGenerator.HoistVariable(new LiftedVariableKey("$awaiter"));
                var awaiter           = awaiterIdentifier.GetReference();

                stateGenerator.CurrentState.Add(awaiter.Assign(operand.Member(awaiterMethodName).Invoke()).Express());

                var nextState = stateGenerator.InsertState();

                JsExpression result = null;

                if (!returnsVoid)
                {
                    // If the await returns a value, store it in a field
                    var resultIdentifier = stateGenerator.HoistVariable(new LiftedVariableKey("$result"));
                    result = resultIdentifier.GetReference();

                    // Make sure the field gets set from the awaiter at the beginning of the next state.
                    nextState.Add(result.Assign(awaiter.Member("GetResult").Invoke()).Express());
                }
                else
                {
                    // We still need to call GetResult even if void in order to propagate exceptions
                    nextState.Add(awaiter.Member("GetResult").Invoke().Express());
                }

                // Set the state to the next state
                stateGenerator.CurrentState.Add(stateGenerator.ChangeState(nextState));

                stateGenerator.CurrentState.Add(Js.If(
                                                    awaiter.Member("get_IsCompleted").Invoke(),

                                                    // If the awaiter is already completed, go to the next state
                                                    stateGenerator.GotoTop(),

                                                    // Otherwise await for completion
                                                    Js.Block(
                                                        // Start the async process
                                                        Js.Reference(builder)
                                                        .Member("TrueAwaitOnCompleted")
                                                        .Invoke(awaiterIdentifier.GetReference(), Js.Reference(stateMachine))
                                                        .Express(),
                                                        Js.Return()
                                                        )
                                                    ));

                stateGenerator.CurrentState = nextState;

                return(result ?? Js.Null());
            }
Пример #9
0
        private JsNode VisitConversion(ResolveResult input, Conversion conversion, IType conversionType)
        {
            ////TODO: HACK: https://github.com/icsharpcode/NRefactory/issues/183
            //var isImplicit = res.Conversion.IsImplicit;
            //if (!isImplicit && res.Conversion.IsExplicit && res.Conversion.Method != null && res.Conversion.Method.Name != null && res.Conversion.Method.Name.Contains("Implicit"))
            //    isImplicit = true;
            if (conversion.IsMethodGroupConversion)
            {
            }
            else if (conversion.IsUserDefined)
            {
                ITypeDefinition typeDef;
                if (conversion.Method != null && conversion.Method.DeclaringType != null)
                {
                    typeDef = conversion.Method.DeclaringType.GetDefinitionOrArrayType();
                }
                else
                {
                    typeDef = conversionType.GetDefinitionOrArrayType();
                }
                var nativeOverloads = Sk.UseNativeOperatorOverloads(typeDef);
                if (nativeOverloads)
                {
                    return(Visit(input));
                }
                ////TODO: Check if OmitCalls is found on conversion method, if so - return Visit(input);
                //if (Sk.IsOmitCalls(conversion.Method))
                //{
                //}
                var fake  = conversion.Method.InvokeMethod(null, input);
                var node2 = Visit(fake);
                return(node2);
            }
            else if (conversion.IsTryCast || conversion.IsExplicit)
            {
                if (ForceIntegers && conversion.IsNumericConversion && IsInteger(conversionType))
                {
                    return(ForceInteger(Visit(input)));
                }

                //Skip enum casts
                if ((conversionType.Kind == TypeKind.Enum && IsInteger(input.Type)) || (input.Type.Kind == TypeKind.Enum && IsInteger(conversionType)))
                {
                    return(Visit(input));
                }

                var omitCasts = Sk.OmitCasts(conversionType);
                if (omitCasts)
                {
                    return(Visit(input));
                }
                if (Sk.NativeCasts(conversionType))
                {
                    var exp2  = VisitExpression(input);
                    var type2 = SkJs.EntityTypeRefToMember(conversionType);
                    if (conversion.IsTryCast)
                    {
                        var node2 = exp2.InstanceOf(type2).Conditional(exp2, Js.Null());
                        return(node2);
                    }
                    else
                    {
                        var node2 = Js.Conditional(exp2.InstanceOf(type2).Or(exp2.Equal(Js.Null())), exp2,
                                                   Js.Parentheses(Js.Function().Add(Js.ThrowNewError("InvalidCastException")).Invoke()));
                        //var node2 = Js.Parentheses(Js.Function().AddStatements(
                        //Js.If(exp2.InstanceOf(type2).Or(exp2.Equal(Js.Null())), Js.Return(exp2)),
                        //Js.ThrowNewError("InvalidCastException"))).Member("call").Invoke(Js.This());
                        return(node2);
                    }
                }
                else
                {
                    var cast  = conversion.IsTryCast ? "As" : "Cast";
                    var node2 = Js.Member(cast).Invoke(VisitExpression(input), SkJs.EntityTypeRefToMember(conversionType));
                    return(node2);
                }
            }
            return(Visit(input));
        }
Пример #10
0
        private JsStatement TransformBody(JsStatement body)
        {
            var block = body is JsBlockStatement ? (JsBlockStatement)body : Js.Block(body);

            if (!escapeStatements.Any())
            {
                return(Js.Express(idioms.Wrap(block)));
            }
            else
            {
                if (!(block.Statements.Last() is JsReturnStatement))
                {
                    block.Return(Js.Object(Js.Item(EscapeTypeField, Js.Primitive(0))).Compact());
                }
                var wrapped    = idioms.Wrap(block);
                var outerBlock = new JsBlockStatement();
                var loopResult = outerBlock.Local("$loopResult", wrapped);
                if (escapeStatements.Any(x => x.Type == Return))
                {
                    outerBlock.If(loopResult.GetReference().Member(EscapeTypeField).EqualTo(Js.Primitive(Return)),
                                  Js.Return(loopResult.GetReference().Member(EscapeValueField)));
                }
                if (escapeStatements.Any(x => x.Type == Continue))
                {
                    var         escapes = escapeStatements.Where(x => x.Type == Continue).Distinct();
                    JsStatement ifTrue;
                    if (!escapes.Any(x => x.Label != null))
                    {
                        ifTrue = Js.Continue();
                    }
                    else
                    {
                        ifTrue = Js.Switch(
                            loopResult.GetReference().Member(EscapeLabelField),
                            escapes
                            .Where(x => x.Label == null)
                            .Select(x => Js.Section(Js.Null()).Statement(Js.Continue()))
                            .Concat(escapes
                                    .Where(x => x.Label != null && transformer.GetLabelDepth(x.Label) == loopDepth)
                                    .Select(x => Js.Section(Js.Primitive(x.Label)).Statement(Js.Continue(x.Label))))
                            .Concat(new[] { Js.Section(Js.DefaultLabel()).Statements(Js.Return(loopResult.GetReference())) })
                            .ToArray());
                    }
                    outerBlock.If(loopResult.GetReference().Member(EscapeTypeField).EqualTo(Js.Primitive(Continue)),
                                  ifTrue);
                }
                if (escapeStatements.Any(x => x.Type == Break))
                {
                    var         escapes = escapeStatements.Where(x => x.Type == Break).Distinct();
                    JsStatement ifTrue;
                    if (!escapes.Any(x => x.Label != null))
                    {
                        ifTrue = Js.Break();
                    }
                    else
                    {
                        ifTrue = Js.Switch(loopResult.GetReference().Member(EscapeLabelField),
                                           escapes.Select(x => Js.Section(Js.Primitive(x.Label)).Statement(Js.Break(x.Label))).ToArray());
                    }
                    outerBlock.If(loopResult.GetReference().Member(EscapeTypeField).EqualTo(Js.Primitive(Break)),
                                  ifTrue);
                }
                return(outerBlock);
            }
        }
Пример #11
0
 public override JsNode Visit(JsReturnStatement node)
 {
     escapeStatements.Add(new EscapeStatement {
         Type = Return
     });
     return(Js.Return(Js.Object(
                          Js.Item(EscapeTypeField, Js.Primitive(Return)),
                          Js.Item(EscapeValueField, node.Expression != null ? node.Expression : Js.Null()),
                          Js.Item(EscapeDepthField, Js.Primitive(loopDepth))
                          ).Compact()));
 }
Пример #12
0
 public override JsNode Visit(JsBreakStatement node)
 {
     escapeStatements.Add(new EscapeStatement {
         Type = Break, Label = node.Label
     });
     return(Js.Return(Js.Object(
                          Js.Item(EscapeTypeField, Js.Primitive(Break)),
                          Js.Item(EscapeLabelField, node.Label != null ? Js.Primitive(node.Label) : Js.Null()),
                          Js.Item(EscapeDepthField, node.Label != null ? Js.Primitive(transformer.GetLabelDepth(node.Label)) : Js.Primitive(0))
                          ).Compact()));
 }
Пример #13
0
 public JsNode VisitNullReferenceExpression(NullReferenceExpression node)
 {
     return(Js.Null());
 }
Пример #14
0
        public async Task <Tuple <string, Project> > Compile(string projectFile)
        {
            var projectFileInfo = new FileInfo(projectFile);
            var projectFolder   = projectFileInfo.Directory.FullName;

            // These two lines are just a weird hack because you get no files back from compilation.SyntaxTrees
            // if the user file isn't modified.  Not sure why that's happening.
            var projectUserFile = projectFolder + "\\" + projectFileInfo.Name + ".user";

            if (File.Exists(projectUserFile))
            {
                File.SetLastWriteTime(projectUserFile, DateTime.Now);
            }

            var project = await MSBuildWorkspace.Create().OpenProjectAsync(projectFile);

            var         projectName = project.AssemblyName;
            Compilation compilation = await project.GetCompilationAsync();

            Context.Update(project.Solution, project, compilation);

            // Check for yield
            foreach (var syntaxTree in compilation.SyntaxTrees)
            {
                var compilationUnit = (CompilationUnitSyntax)syntaxTree.GetRoot();
                var semanticModel   = compilation.GetSemanticModel(syntaxTree);
                var yieldGenerator  = new YieldGenerator(compilation, syntaxTree, semanticModel);
                compilationUnit = (CompilationUnitSyntax)compilationUnit.Accept(yieldGenerator);
                compilation     = compilation.ReplaceSyntaxTree(syntaxTree, SyntaxFactory.SyntaxTree(compilationUnit, syntaxTree.FilePath));
            }
            compilation = compilation.Clone();
            Context.Update(project.Solution, project, compilation);

            // After the basic transformation happens, we need to fix up some references afterward
            foreach (var syntaxTree in compilation.SyntaxTrees)
            {
                var compilationUnit = (CompilationUnitSyntax)syntaxTree.GetRoot();
                var semanticModel   = compilation.GetSemanticModel(syntaxTree);
                var yieldFixer      = new YieldGeneratorFixer(compilation, syntaxTree, semanticModel);
                compilationUnit = (CompilationUnitSyntax)compilationUnit.Accept(yieldFixer);
                compilation     = compilation.ReplaceSyntaxTree(syntaxTree, SyntaxFactory.SyntaxTree(compilationUnit, syntaxTree.FilePath));
            }
            Context.Update(project.Solution, project, compilation);

            // Check for async
            foreach (var syntaxTree in compilation.SyntaxTrees)
            {
                var compilationUnit = (CompilationUnitSyntax)syntaxTree.GetRoot();
                var semanticModel   = compilation.GetSemanticModel(syntaxTree);
                var asyncGenerator  = new AsyncGenerator(compilation, syntaxTree, semanticModel);
                compilationUnit = (CompilationUnitSyntax)compilationUnit.Accept(asyncGenerator);
                compilation     = compilation.ReplaceSyntaxTree(syntaxTree, SyntaxFactory.SyntaxTree(compilationUnit, syntaxTree.FilePath));
            }
            Context.Update(project.Solution, project, compilation);

            var jsCompilationUnit = new JsCompilationUnit {
                UseStrict = true
            };

            // If this is the runtime prjoect, declare the array to hold all the GetAssembly functions (this .js file
            // will be loaded first, and we only want to bother creating the array once.
            if (projectName == "mscorlib")
            {
                var assemblies = Js.Variable(SpecialNames.Assemblies, Js.Array());
                jsCompilationUnit.Body.Local(assemblies);

                // This ensures that Function.$typeName returns `Function` -- this is important when using
                // a type function as a generic argument, since otherwise when we try to assembly a
                // unique key for the permuatation of type args including a type function, we would get
                // an empty string for that arg, which would break the cache.
                jsCompilationUnit.Body.Assign(Js.Reference("Function").Member(SpecialNames.TypeName), Js.Primitive("Function"));
            }

            // Declare assembly variable
            var assemblyVariable = Js.Variable("$" + projectName.MaskSpecialCharacters() + "$Assembly", Js.Null());

            jsCompilationUnit.Body.Local(assemblyVariable);

            // Declare array to store all anonymous types
            var anonymousTypes = Js.Variable(compilation.Assembly.GetAssemblyAnonymousTypesArray(), Js.Array());

            jsCompilationUnit.Body.Local(anonymousTypes);

            // Declare array to store all the type functions in the assembly
            var assemblyTypes = Js.Variable(compilation.Assembly.GetAssemblyTypesArray(), Js.Array());

            jsCompilationUnit.Body.Local(assemblyTypes);

            // Build $GetAssemblyMethod, which lazily creates a new Assembly instance
            var globalIdioms = new Idioms(null);
            var getAssembly  = Js.Function();

            getAssembly.Body.If(
                assemblyVariable.GetReference().EqualTo(Js.Null()),
                assemblyVariable.GetReference().Assign(globalIdioms.CreateAssembly(compilation.Assembly, assemblyTypes.GetReference()))
                );
            getAssembly.Body.Return(assemblyVariable.GetReference());
            jsCompilationUnit.Body.Assign(
                Js.Reference(compilation.Assembly.GetAssemblyMethodName()),
                getAssembly);

            // Add $GetAssemblyMethod to global assemblies array
            jsCompilationUnit.Body.Express(Js.Reference("$assemblies").Member("push").Invoke(Js.Reference(compilation.Assembly.GetAssemblyMethodName())));

            // Builds out all the namespace objects.  Types live inside namepsaces, which are represented as
            // nested Javascript objects.  For example, System.Text.StringBuilder is represented (in part) as:
            //
            // System = {};
            // System.Text = {};
            // System.Text.StringBuilder = function() { ... }
            //
            // This allows access to classes using dot notation in the expected way.
            var namespaceTransformer = new NamespaceTransformer(jsCompilationUnit.Body);

            foreach (var syntaxTree in compilation.SyntaxTrees)
            {
                var compilationUnit = (CompilationUnitSyntax)syntaxTree.GetRoot();
                compilationUnit.Accept(namespaceTransformer);
            }

            var actions = new List <Tuple <INamedTypeSymbol, Action> >();

            // Scan all syntax trees for anonymous type creation expressions.  We transform them into class
            // declarations with a series of auto implemented properties.
            var anonymousTypeTransformer = new AnonymousTypeTransformer(jsCompilationUnit.Body, actions);

            foreach (var syntaxTree in compilation.SyntaxTrees)
            {
                var compilationUnit = (CompilationUnitSyntax)syntaxTree.GetRoot();
                compilationUnit.Accept(anonymousTypeTransformer);
            }

            var diagnostics = compilation.GetDiagnostics();

            foreach (var diagnostic in diagnostics)
            {
                Console.WriteLine("// " + diagnostic);
            }

            // Iterate through all the syntax trees and add entries into `actions` that correspond to type
            // declarations.
            foreach (var syntaxTree in compilation.SyntaxTrees)
            {
                var semanticModel   = compilation.GetSemanticModel(syntaxTree);
                var compilationUnit = (CompilationUnitSyntax)syntaxTree.GetRoot();
                var transformer     = new JsTransformer(syntaxTree, semanticModel);

                var typeDeclarations = GetTypeDeclarations(compilationUnit);
                foreach (var type in typeDeclarations)
                {
                    Action action = () =>
                    {
                        var statements = (JsBlockStatement)type.Accept(transformer);
                        jsCompilationUnit.Body.Aggregate(statements);
                    };
                    actions.Add(Tuple.Create((INamedTypeSymbol)ModelExtensions.GetDeclaredSymbol(semanticModel, type), action));
                }
                var delegateDeclarations = GetDelegates(compilationUnit);
                foreach (var type in delegateDeclarations)
                {
                    Action action = () =>
                    {
                        var statements = (JsBlockStatement)type.Accept(transformer);
                        jsCompilationUnit.Body.Aggregate(statements);
                    };
                    actions.Add(Tuple.Create((INamedTypeSymbol)ModelExtensions.GetDeclaredSymbol(semanticModel, type), action));
                }
            }

            // Sort all the type declarations such that base types always come before subtypes.
            SweepSort(actions);
            foreach (var item in actions)
            {
                item.Item2();
            }

            // If the project type is a console application, then invoke the Main method at the very
            // end of the file.
            var entryPoint = compilation.GetEntryPoint(CancellationToken.None);

            if (entryPoint != null)
            {
                jsCompilationUnit.Body.Express(globalIdioms.InvokeStatic(entryPoint));
            }

            // Test minification
//            var minifier = new JsMinifier();
//            jsCompilationUnit.Accept(minifier);

            // Write out the compiled Javascript file to the target location.
            var renderer = new JsRenderer();

            jsCompilationUnit.Accept(renderer);
            return(Tuple.Create(renderer.Output, project));
        }
Пример #15
0
        public override void VisitUsingStatement(UsingStatementSyntax node)
        {
            var afterTry        = GetNextState();
            var newTryStatement = Js.Try();

            // Keep track of exception, if any, so we can rethrow
            var exceptionIdentifier = HoistVariable(new LiftedVariableKey("$usingex"));

            // Identifier for caught exception
            var caughtExceptionIdentifier = UniqueName("$caughtex");

            // Hoist the variable into a field
            var disposables = new List <JsExpression>();

            if (node.Declaration != null)
            {
                foreach (var variable in node.Declaration.Variables)
                {
                    var symbol     = (ILocalSymbol)Transformer.Model.GetDeclaredSymbol(variable);
                    var identifier = HoistVariable(new LiftedVariableKey(variable.Identifier, symbol));
                    var name       = identifier.GetReference();
                    disposables.Add(name);
                    CurrentState.Add(name.Assign((JsExpression)variable.Initializer.Value.Accept(Transformer)).Express());
                }
            }
            if (node.Expression != null)
            {
                var identifier = Js.Reference(UniqueName("$using"));
                disposables.Add(identifier);
                CurrentState.Add(identifier.Assign((JsExpression)node.Expression.Accept(Transformer)).Express());
            }

            var tryState = NewSubstate();

            GotoState(tryState);

            var finallyState = GetNextState();

            CurrentState = finallyState;
            foreach (var disposable in disposables)
            {
                CurrentState.Add(disposable.Member("Dispose").Invoke().Express());
            }
            CurrentState.Add(Js.If(exceptionIdentifier.GetReference().NotEqualTo(Js.Null()), Js.Throw(exceptionIdentifier.GetReference())));
            GotoState(afterTry);
            newTryStatement.Catch      = Js.Catch(Js.Variable(caughtExceptionIdentifier));
            newTryStatement.Catch.Body = Js.Block(
                new[] { exceptionIdentifier.GetReference().Assign(Js.Reference(caughtExceptionIdentifier)).Express() }
                .Concat(GotoStateStatements(finallyState))
                .ToArray()
                );

            tryState.Wrap = switchStatement =>
            {
                newTryStatement.Body = Js.Block(switchStatement);
                return(newTryStatement);
            };

            StartSubstate(tryState);
            AcceptStatement(node.Statement);
            GotoState(finallyState);
            EndSubstate();

            CurrentState = afterTry;
        }
Пример #16
0
        public override void VisitTryStatement(TryStatementSyntax node)
        {
            var afterTry        = GetNextState();
            var newTryStatement = Js.Try();

            var tryState = NewSubstate();

            GotoState(tryState);

            // Keep track of exception, if any, so we can rethrow
            var exceptionIdentifier = HoistVariable(new LiftedVariableKey("$ex"));
            var exceptionVariable   = UniqueName("$caughtex");

            State finallyState = node.Finally == null ? null : GetNextState();

            // Declare a block to store all the catch statements the try statement's only catch clause. (No
            // type-specific catch clauses in Javascript
            var catchBlock = Js.Block();

            // Make sure that the exception is stored in a variable accessible to the entire state machine.
            catchBlock.Express(exceptionIdentifier.GetReference().Assign(Js.Reference(exceptionVariable)));

            foreach (var catchClause in node.Catches)
            {
                // Get the symbol that represents the exception declaration (identifier and type)
                var symbol        = Transformer.Model.GetDeclaredSymbol(catchClause.Declaration);
                var exceptionType = symbol == null ? null : symbol.Type;
                if (exceptionType == null && catchClause.Declaration != null && catchClause.Declaration.Type != null)
                {
                    exceptionType = (ITypeSymbol)Transformer.Model.GetSymbolInfo(catchClause.Declaration.Type).Symbol;
                }

                // True if it is actually declaring the variable (as opposed to a catch clause that specifies
                // merely an exception type
                var hasDeclaration = catchClause.Declaration.Identifier.Kind() != SyntaxKind.None;

                // A variable to store the new unique identifier to store the exception
                IJsDeclaration newIdentifier;

                // Hoist the variable into a field
                if (hasDeclaration)
                {
                    newIdentifier = HoistVariable(new LiftedVariableKey(catchClause.Declaration.Identifier, symbol));
                }
                else
                {
                    newIdentifier = HoistVariable(new LiftedVariableKey(SyntaxFactory.Identifier("ex")));
                }

                // Collect all the catch statements into the catchState by making that state current
                var catchState = GetNextState();
                CurrentState = catchState;
                AcceptStatement(catchClause.Block);

                // Add onto the catch state some commands to go to the next state.
                if (finallyState != null)
                {
                    GotoState(finallyState);
                }
                else
                {
                    GotoState(afterTry);
                }

                // Create the statements that will live in the actual catch handler, which directs the logic
                // to the actual catch state and also stores the exception in the correct identifier.
                var thisCatchStatements = Js.Block();
                thisCatchStatements.Express(newIdentifier.SetReference().Assign(exceptionIdentifier.GetReference()));

                // Apply filter if present
                if (catchClause.Filter != null)
                {
                    var filter = (JsExpression)catchClause.Filter.FilterExpression.Accept(Transformer);
                    thisCatchStatements.Add(Js.If(filter, Js.Block(GotoStateStatements(catchState))));
                }
                else
                {
                    thisCatchStatements.AddRange(GotoStateStatements(catchState));
                }

                // Only do the above if the current exception is of the type expected by the catch handler.
                var condition = Idioms.Is(exceptionIdentifier.GetReference(), exceptionType);
                catchBlock.Add(Js.If(condition, thisCatchStatements));
            }
            if (node.Finally != null)
            {
                // Collect the statements of the finally block into the finally state
                CurrentState = finallyState;
                AcceptStatement(node.Finally.Block);

                // If the exception object is not null, then rethrow it.  In other words, if this is a finally
                // clause that has responded to an exception, we need to propagate the exception rather than
                // continue after the try statement.  Otherwise, go to the code after the try block.
                CurrentState.Add(Js.If(exceptionIdentifier.GetReference().NotEqualTo(Js.Null()),
                                       Js.Throw(exceptionIdentifier.GetReference()),
                                       Js.Block(GotoStateStatements(afterTry))));

                // Finally, at the very end of the catch clause (and we can only get here if the logic didn't break
                // out as it would with the logic in the catch handlers) go to the finally state.
                catchBlock.AddRange(GotoStateStatements(finallyState).ToArray());
            }
            catchBlock.Add(Js.Throw(exceptionIdentifier.GetReference()));

            newTryStatement.Catch      = Js.Catch(Js.Variable(exceptionVariable));
            newTryStatement.Catch.Body = catchBlock;
            tryState.Wrap = switchStatement =>
            {
                newTryStatement.Body = Js.Block(switchStatement);
                return(newTryStatement);
            };

            StartSubstate(tryState);
            AcceptStatement(node.Block);
            if (node.Finally != null)
            {
                GotoState(finallyState);
            }
            else
            {
                GotoState(afterTry);
            }
            EndSubstate();

            CurrentState = afterTry;
        }