public override JsExpression VisitIdentifierName(IdentifierNameSyntax node) { var key = node.Identifier.ToString(); var symbol = model.GetSymbolInfo(node).Symbol; if (parameterVariables.ContainsKey(key)) { return(Js.Reference(parameterVariables[key])); } else { var constantMethod = Context.Instance.Expression.GetMethod("Constant", Context.Instance.ObjectType, Context.Instance.TypeType); var typeInfo = model.GetTypeInfo(node); JsExpression keyReference; if (symbol is ILocalSymbol) { keyReference = Js.Reference(key); } else { keyReference = idioms.MemberReference(Js.This(), symbol); } return(idioms.InvokeStatic(constantMethod, keyReference, idioms.TypeOf(typeInfo.ConvertedType))); } }
private void ProcessNamespace(string ns) { if (processedNamespaces.Contains(ns)) { return; } processedNamespaces.Add(ns); var lastDotIndex = ns.LastIndexOf('.'); var parentNamespace = lastDotIndex != -1 ? ns.Substring(0, lastDotIndex) : null; if (parentNamespace != null) { ProcessNamespace(parentNamespace); } JsBinaryExpression result; if (lastDotIndex == -1) { result = Js.Assign(Js.Reference("window").Member(ns), Js.Object()); } else { result = Js.Assign(Js.Reference(ns), Js.Object()); } result.Right = Js.Binary(JsBinaryOperator.LogicalOr, result.Left, result.Right); body.Express(result); }
private JsExpression VisitLambdaExpression(ExpressionSyntax node, ParameterSyntax[] parameters, CSharpSyntaxNode body) { var expressionType = (INamedTypeSymbol)model.GetTypeInfo(node).ConvertedType; INamedTypeSymbol delegateType; if (Equals(expressionType.OriginalDefinition, Context.Instance.ExpressionGeneric)) { delegateType = (INamedTypeSymbol)expressionType.TypeArguments[0]; } else { delegateType = expressionType; } var lambdaParameters = parameters.ToArray(); var delegateMethod = (IMethodSymbol)delegateType.GetMembers("Invoke")[0]; // Compiler.CsCompilation.FindType() var lambdaMethods = Context.Instance.Expression.GetMembers("Lambda").OfType <IMethodSymbol>().ToArray(); var lambdaMethods2 = lambdaMethods.Where(x => x.TypeParameters.Count() == 1 && x.Parameters.Count() == 2 && Equals(x.Parameters[0].Type, Context.Instance.Expression) && Equals(x.Parameters[1].Type, Context.Instance.ParameterExpressionArray)).ToArray(); var lambdaMethod = lambdaMethods2.Single(); var parameterMethod = Context.Instance.Expression.GetMembers("Parameter").OfType <IMethodSymbol>().Single(x => x.Parameters.Count() == 2 && Equals(x.Parameters[0].Type, Context.Instance.TypeType) && Equals(x.Parameters[1].Type, Context.Instance.String)); lambdaMethod = lambdaMethod.Construct(delegateType); var jsLambda = idioms.InvokeStatic(lambdaMethod); // Convert parameters var workspace = new JsBlockStatement(); var jsParameters = Js.Array(); for (var i = 0; i < delegateMethod.Parameters.Count(); i++) { var delegateParameter = delegateMethod.Parameters[i]; var lambdaParameter = lambdaParameters[i]; var jsParameter = idioms.InvokeStatic(parameterMethod, idioms.TypeOf(delegateParameter.Type), Js.Primitive(lambdaParameter.Identifier.ToString())); var parameterVariableName = "$lambdaparam$" + lambdaParameter.Identifier; parameterVariables[lambdaParameter.Identifier.ToString()] = parameterVariableName; // Declare a variable to hold the parameter object in the parenthetical's scope workspace.Local(parameterVariableName, jsParameter); // Add a reference to this variable (a ParameterExpression) as one of the // parameters to the lambda. jsParameters.Elements.Add(Js.Reference(parameterVariableName)); } var jsBody = body.Accept(this); jsLambda.AddArgument(jsBody); jsLambda.AddArgument(jsParameters); workspace.Return(jsLambda); return(idioms.Wrap(workspace)); }
public void SetResult(ExpressionSyntax result = null) { var setResult = Js.Reference(builder).Member("SetResult"); if (result != null) { var statement = setResult.Invoke((JsExpression)result.Accept(Transformer)).Express(); CurrentState.Add(statement); } else { CurrentState.Add(setResult.Invoke().Express()); } CurrentState.Add(Js.Return()); }
public override JsExpression VisitIdentifierName(IdentifierNameSyntax node) { var key = node.Identifier.ToString(); if (parameterVariables.ContainsKey(key)) { return(Js.Reference(parameterVariables[key])); } else { var constantMethod = Context.Instance.Expression.GetMethod("Constant", Context.Instance.ObjectType, Context.Instance.TypeType); var typeInfo = model.GetTypeInfo(node); return(idioms.InvokeStatic(constantMethod, Js.Reference(key), idioms.TypeOf(typeInfo.ConvertedType))); } }
public override void VisitYieldStatement(YieldStatementSyntax node) { var nextState = GetNextState(); if (node.ReturnOrBreakKeyword.IsKind(SyntaxKind.BreakKeyword)) { CurrentState.Add(ChangeState(nextState)); CurrentState.Add(Js.Primitive(false).Return()); } else { CurrentState.Add(ChangeState(nextState)); CurrentState.Add(Js.Reference(stateMachine).Member("set_Current").Invoke((JsExpression)node.Expression.Accept(Transformer)).Express()); CurrentState.Add(Js.Primitive(true).Return()); } CurrentState = nextState; }
public JsStatement GenerateSwitch(State state) { var sections = new List <JsSwitchSection>(); if (state.Parent != null) { var stateSection = Js.Section(Js.Primitive(state.Index)); stateSection.Statements.AddRange(GotoStateStatements(state.Substates.First())); sections.Add(stateSection); } sections.AddRange(state.Substates.Select(substate => { var section = Js.Section(substate.GetAllIndices().Select(index => Js.Primitive(index)).ToArray()); section.Statements.AddRange(substate.Statements); return(section); })); return(state.Wrap(Js.Switch(Js.Reference(BaseStateGenerator.state), sections.ToArray()))); }
public override void VisitYieldStatement(YieldStatementSyntax node) { var nextState = GetNextState(); if (node.ReturnOrBreakKeyword.IsKind(SyntaxKind.BreakKeyword)) { CurrentState.Add(ChangeState(nextState)); CurrentState.Add(Js.Primitive(false).Return()); } else { CurrentState.Add(ChangeState(nextState)); // if (Transformer.Model.Compilation.Assembly.AreAutoPropertiesMinimized()) // CurrentState.Add(Js.Reference(stateMachine).Member("Current").Assign((JsExpression)node.Expression.Accept(Transformer)).Express()); // else CurrentState.Add(Js.Reference(stateMachine).Member("set_Current").Invoke((JsExpression)node.Expression.Accept(Transformer)).Express()); CurrentState.Add(Js.Primitive(true).Return()); } CurrentState = nextState; }
public override void VisitAnonymousObjectCreationExpression(AnonymousObjectCreationExpressionSyntax node) { var jsBlock = new JsBlockStatement(); var model = Context.Instance.Compilation.GetSemanticModel(node.SyntaxTree); var classType = (INamedTypeSymbol)ModelExtensions.GetDeclaredSymbol(model, node); if (processedTypes.Contains(classType)) { return; } processedTypes.Add(classType); JsBlockStatement typeInitializer; JsBlockStatement staticInitializer; jsBlock.Aggregate(idioms.CreateTypeFunction(classType, out typeInitializer, out staticInitializer)); // Create default constructor var constructorBlock = new JsBlockStatement(); constructorBlock.Express(idioms.InvokeMethodAsThis(classType.BaseType.InstanceConstructors.Single(x => x.Parameters.Count() == 0))); var constructor = classType.InstanceConstructors.Single(); typeInitializer.Add(idioms.StoreInPrototype(constructor.GetMemberName(), Js.Reference(SpecialNames.DefineConstructor).Invoke( Js.Reference(SpecialNames.TypeInitializerTypeFunction), Js.Function().Body(constructorBlock)))); foreach (var property in classType.GetMembers().OfType <IPropertySymbol>()) { typeInitializer.Aggregate(CreateProperty(property)); } Action action = () => { body.Aggregate(jsBlock); }; actions.Add(Tuple.Create(classType, action)); }
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); }
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()); }
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)); }
public JsExpression SetReference() { return(Js.Reference(Name)); }
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; }
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; }
public JsStatement ChangeState(State newState) { return(Js.Reference(state).Assign(Js.Primitive(newState.Index)).Express()); }