Exemple #1
0
        private static void GenerateMethodStub(StringBuilder output, ClassDeclarationSyntax classSyntax, JSMethodGroup methodGroup, string returnType = "object")
        {
            output.AppendLine();
            output.AppendLine($"\t\tprivate static {returnType} {methodGroup.StubName}(ScriptEngine engine, object thisObj, object[] args)");
            output.AppendLine("\t\t{");

            if (!methodGroup.IsStatic)
            {
                output.AppendLine($"\t\t\tthisObj = TypeConverter.ToObject(engine, thisObj);");
                output.AppendLine($"\t\t\tif (!(thisObj is {classSyntax.Identifier.ToString()}))");
                output.AppendLine($"\t\t\t\tthrow new JavaScriptException(engine, ErrorType.TypeError, \"The method '{methodGroup.First().FunctionName}' is not generic.\");");
            }
            else if (methodGroup.Any(m => m.HasThisObject && m.ThisObjectParameterType != "object"))
            {
                output.AppendLine("\t\t\tif (thisObj == null || thisObj == Undefined.Value || thisObj == Null.Value)");
                output.AppendLine("\t\t\t\tthrow new JavaScriptException(engine, ErrorType.TypeError, \"Cannot convert undefined or null to object.\");");
            }

            int maxParameterCount = methodGroup.Max(mds => mds.Parameters.Count());

            if (maxParameterCount == 0)
            {
                output.Append("\t\t\t");
                output.AppendLine(GenerateMethodCall(classSyntax, methodGroup.Single(), 0));
            }
            else
            {
                output.AppendLine("\t\t\tswitch (args.Length)");
                output.AppendLine("\t\t\t{");
                for (int i = 0; i <= maxParameterCount; i++)
                {
                    // If the parameter count is X, then find the method with smallest number of
                    // parameters which has at least X parameters.
                    var method = methodGroup.Where(mds => mds.Parameters.Count() >= i).OrderBy(mds => mds.Parameters.Count()).First();
                    if (i < maxParameterCount)
                    {
                        output.AppendLine($"\t\t\t\tcase {i}:");
                    }
                    else
                    {
                        output.AppendLine($"\t\t\t\tdefault:");
                    }
                    output.Append("\t\t\t\t\t");
                    if (i < methodGroup.RequiredArgumentCount)
                    {
                        output.AppendLine($"throw new JavaScriptException(engine, ErrorType.TypeError, \"Required argument '{method.Parameters.Skip(i).First().Name}' was not specified.\");");
                    }
                    else
                    {
                        output.AppendLine(GenerateMethodCall(classSyntax, method, i));
                    }
                }
                output.AppendLine("\t\t\t}");
            }

            output.AppendLine("\t\t}");
        }
Exemple #2
0
        static void Main(string[] args)
        {
            IEnumerable <string> files = Directory.EnumerateFiles(@"..\..\..\..\Jurassic", "*.cs", SearchOption.AllDirectories);

            files = files.Union(Directory.EnumerateFiles(@"..\..\..\..\Jurassic.Extensions", "*.cs", SearchOption.AllDirectories));
            foreach (var csFilePath in files)
            {
                var syntaxTree     = CSharpSyntaxTree.ParseText(File.ReadAllText(csFilePath));
                var classCollector = new ClassCollector();
                classCollector.Visit(syntaxTree.GetRoot());

                // Construct the output file.
                var output = new StringBuilder();
                output.AppendLine("/*");
                output.AppendLine(" * This file is auto-generated, do not modify directly.");
                output.AppendLine(" */");
                output.AppendLine();
                output.AppendLine("using System.Collections.Generic;");
                output.AppendLine("using Jurassic;");
                if (classCollector.Classes.Any(classSyntax => ((NamespaceDeclarationSyntax)classSyntax.Parent).Name.ToString() != "Jurassic.Library"))
                {
                    output.AppendLine("using Jurassic.Library;");
                }
                output.AppendLine();

                bool outputFile = false;
                foreach (var classSyntax in classCollector.Classes)
                {
                    // Find all the methods with [JSInternalFunction], [JSCallFunction], [JSConstructorFunction], [JSProperty] or [JSField].
                    var memberCollector = new ClassMembersCollector();
                    memberCollector.Visit(classSyntax);
                    if (memberCollector.JSInternalFunctionMethods.Any() == false &&
                        memberCollector.JSCallFunctionMethods.Any() == false &&
                        memberCollector.JSConstructorFunctionMethods.Any() == false &&
                        memberCollector.JSProperties.Any() == false &&
                        memberCollector.JSFields.Any() == false)
                    {
                        continue;
                    }

                    Console.WriteLine($"Generating stubs for {classSyntax.Identifier.ToString()}");

                    outputFile = true;
                    var methodGroups = JSMethodGroup.FromMethods(memberCollector.JSInternalFunctionMethods);

                    output.AppendLine($"namespace {((NamespaceDeclarationSyntax)classSyntax.Parent).Name}");
                    output.AppendLine("{");
                    output.AppendLine();

                    output.AppendLine($"\t{classSyntax.Modifiers} class {classSyntax.Identifier}");
                    output.AppendLine("\t{");

                    // Output the PopulateStubs method.
                    if (memberCollector.JSInternalFunctionMethods.Any() ||
                        memberCollector.JSProperties.Any() ||
                        memberCollector.JSFields.Any())
                    {
                        output.AppendLine("\t\tprivate static List<PropertyNameAndValue> GetDeclarativeProperties(ScriptEngine engine)");
                        output.AppendLine("\t\t{");
                        output.AppendLine($"\t\t\treturn new List<PropertyNameAndValue>({memberCollector.JSInternalFunctionMethods.Count + memberCollector.JSFields.Count + 4})");
                        output.AppendLine("\t\t\t{");

                        foreach (var field in memberCollector.JSFields)
                        {
                            foreach (var variable in field.Declaration.Variables)
                            {
                                output.AppendLine($"\t\t\t\tnew PropertyNameAndValue(\"{variable.Identifier.ToString()}\", {variable.Identifier.ToString()}, PropertyAttributes.Sealed),");
                            }
                        }
                        foreach (var property in memberCollector.JSProperties.Select(p => new JSProperty(p)))
                        {
                            if (property.SetterStubName == null)
                            {
                                output.AppendLine($"\t\t\t\tnew PropertyNameAndValue({property.PropertyKey}, new PropertyDescriptor(" +
                                                  $"new ClrStubFunction(engine, \"get {property.FunctionName}\", 0, {property.GetterStubName}), " +
                                                  $"null, {property.JSPropertyAttributes})),");
                            }
                            else
                            {
                                output.AppendLine($"\t\t\t\tnew PropertyNameAndValue({property.PropertyKey}, new PropertyDescriptor(" +
                                                  $"new ClrStubFunction(engine, \"get {property.FunctionName}\", 0, {property.GetterStubName}), " +
                                                  $"new ClrStubFunction(engine, \"set {property.FunctionName}\", 0, {property.GetterStubName}), " +
                                                  $"{property.JSPropertyAttributes})),");
                            }
                        }
                        foreach (var methodGroup in methodGroups)
                        {
                            output.AppendLine($"\t\t\t\tnew PropertyNameAndValue({methodGroup.PropertyKey}, " +
                                              $"new ClrStubFunction(engine, \"{methodGroup.FunctionName}\", " +
                                              $"{methodGroup.JSLength}, {methodGroup.StubName}), {methodGroup.JSPropertyAttributes}),");
                        }
                        output.AppendLine("\t\t\t};");
                        output.AppendLine("\t\t}");
                    }

                    if (memberCollector.JSCallFunctionMethods.Any())
                    {
                        GenerateMethodStub(output, classSyntax, new JSMethodGroup(memberCollector.JSCallFunctionMethods.Select(mds => new JSMethod(mds))));
                    }
                    if (memberCollector.JSConstructorFunctionMethods.Any())
                    {
                        GenerateMethodStub(output, classSyntax, new JSMethodGroup(memberCollector.JSConstructorFunctionMethods.Select(mds => new JSMethod(mds))), "ObjectInstance");
                    }
                    foreach (var property in memberCollector.JSProperties.Select(p => new JSProperty(p)))
                    {
                        output.AppendLine();
                        output.AppendLine($"\t\tprivate static object {property.GetterStubName}(ScriptEngine engine, object thisObj, object[] args)");
                        output.AppendLine("\t\t{");
                        output.AppendLine($"\t\t\tthisObj = TypeConverter.ToObject(engine, thisObj);");
                        output.AppendLine($"\t\t\tif (!(thisObj is {classSyntax.Identifier.ToString()}))");
                        output.AppendLine($"\t\t\t\tthrow new JavaScriptException(engine, ErrorType.TypeError, \"The method 'get {property.FunctionName}' is not generic.\");");
                        output.AppendLine($"\t\t\treturn (({classSyntax.Identifier.ToString()})thisObj).{property.PropertyName};");
                        output.AppendLine("\t\t}");

                        if (property.SetterStubName != null)
                        {
                            output.AppendLine();
                            output.AppendLine($"\t\tprivate static object {property.SetterStubName}(ScriptEngine engine, object thisObj, object[] args)");
                            output.AppendLine("\t\t{");
                            output.AppendLine($"\t\t\tthisObj = TypeConverter.ToObject(engine, thisObj);");
                            output.AppendLine($"\t\t\tif (!(thisObj is {classSyntax.Identifier.ToString()}))");
                            output.AppendLine($"\t\t\t\tthrow new JavaScriptException(engine, ErrorType.TypeError, \"The method 'set {property.FunctionName}' is not generic.\");");
                            output.AppendLine($"\t\t\t(({classSyntax.Identifier.ToString()})thisObj).{property.PropertyName} = {ConvertTo("args.Length > 0 ? args[0] : Undefined.Value", property.ReturnType, null)};");
                            output.AppendLine("\t\t}");
                        }
                    }
                    foreach (var methodGroup in methodGroups)
                    {
                        GenerateMethodStub(output, classSyntax, methodGroup);
                    }

                    output.AppendLine("\t}");
                    output.AppendLine();
                    output.AppendLine("}");
                }

                if (outputFile)
                {
                    // Write the output file.
                    File.WriteAllText(Path.Combine(Path.GetDirectoryName(csFilePath), Path.GetFileNameWithoutExtension(csFilePath) + ".g.cs"), output.ToString());
                }
            }
        }