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); }
public JsNode VisitNullNode(AstNode node) { if (node == Statement.Null) { return(Js.Null().Statement()); } return(Js.Null()); }
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); }
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); } }
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); }
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(); }
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()); }
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)); }
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); } }
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())); }
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())); }
public JsNode VisitNullReferenceExpression(NullReferenceExpression node) { return(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 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; }