private TypeSymbol BuildType(UserTypeNode typeNode, NamespaceSymbol namespaceSymbol) { Debug.Assert(typeNode != null); Debug.Assert(namespaceSymbol != null); TypeSymbol typeSymbol = null; ParseNodeList attributes = typeNode.Attributes; if (typeNode.Type == TokenType.Class) { CustomTypeNode customTypeNode = (CustomTypeNode)typeNode; Debug.Assert(customTypeNode != null); NameNode baseTypeNameNode = null; if (customTypeNode.BaseTypes.Count != 0) { baseTypeNameNode = customTypeNode.BaseTypes[0] as NameNode; } if ((baseTypeNameNode != null) && (String.CompareOrdinal(baseTypeNameNode.Name, "Record") == 0)) { typeSymbol = new RecordSymbol(typeNode.Name, namespaceSymbol); } else { AttributeNode resourcesAttribute = AttributeNode.FindAttribute(attributes, "Resources"); if (resourcesAttribute != null) { typeSymbol = new ResourcesSymbol(typeNode.Name, namespaceSymbol); } else { typeSymbol = new ClassSymbol(typeNode.Name, namespaceSymbol); if ((baseTypeNameNode != null) && (String.CompareOrdinal(baseTypeNameNode.Name, "TestClass") == 0)) { ((ClassSymbol)typeSymbol).SetTestClass(); } } } } else if (typeNode.Type == TokenType.Interface) { typeSymbol = new InterfaceSymbol(typeNode.Name, namespaceSymbol); } else if (typeNode.Type == TokenType.Enum) { bool flags = false; AttributeNode flagsAttribute = AttributeNode.FindAttribute(typeNode.Attributes, "Flags"); if (flagsAttribute != null) { flags = true; } typeSymbol = new EnumerationSymbol(typeNode.Name, namespaceSymbol, flags); } else if (typeNode.Type == TokenType.Delegate) { typeSymbol = new DelegateSymbol(typeNode.Name, namespaceSymbol); typeSymbol.SetTransformedName("Function"); typeSymbol.SetIgnoreNamespace(); } Debug.Assert(typeSymbol != null, "Unexpected type node " + typeNode.Type); if (typeSymbol != null) { if ((typeNode.Modifiers & Modifiers.Public) != 0) { typeSymbol.SetPublic(); } BuildType(typeSymbol, typeNode); if (namespaceSymbol.Name.EndsWith(_options.TestsSubnamespace, StringComparison.Ordinal)) { typeSymbol.SetTestType(); } } return(typeSymbol); }
private void BuildType(TypeSymbol typeSymbol, UserTypeNode typeNode) { Debug.Assert(typeSymbol != null); Debug.Assert(typeNode != null); ParseNodeList attributes = typeNode.Attributes; if (AttributeNode.FindAttribute(attributes, "Imported") != null) { typeSymbol.SetImported(/* dependencyName */ null); } if (AttributeNode.FindAttribute(attributes, "IgnoreNamespace") != null) { typeSymbol.SetIgnoreNamespace(); } if (AttributeNode.FindAttribute(attributes, "PreserveName") != null) { typeSymbol.DisableNameTransformation(); } string scriptName = GetAttributeValue(attributes, "ScriptName"); if (scriptName != null) { typeSymbol.SetTransformedName(scriptName); } if (typeNode.Type == TokenType.Class) { bool globalizeMembers = false; string mixinRoot = null; AttributeNode globalMethodsAttribute = AttributeNode.FindAttribute(attributes, "GlobalMethods"); if (globalMethodsAttribute != null) { globalizeMembers = true; } else { AttributeNode mixinAttribute = AttributeNode.FindAttribute(attributes, "Mixin"); if (mixinAttribute != null) { Debug.Assert(mixinAttribute.Arguments[0] is LiteralNode); Debug.Assert(((LiteralNode)mixinAttribute.Arguments[0]).Value is string); mixinRoot = (string)((LiteralNode)mixinAttribute.Arguments[0]).Value; globalizeMembers = true; } } if (globalizeMembers) { ((ClassSymbol)typeSymbol).SetGlobalMethods(mixinRoot); } } if (typeNode.Type == TokenType.Enum) { if (AttributeNode.FindAttribute(attributes, "NamedValues") != null) { ((EnumerationSymbol)typeSymbol).SetNamedValues(); } else if (AttributeNode.FindAttribute(attributes, "NumericValues") != null) { ((EnumerationSymbol)typeSymbol).SetNumericValues(); } } }
public ICollection <TypeSymbol> BuildMetadata(ParseNodeList compilationUnits, SymbolSet symbols, CompilerOptions options) { Debug.Assert(compilationUnits != null); Debug.Assert(symbols != null); _symbols = symbols; _symbolTable = symbols; _options = options; string scriptName = GetAssemblyScriptName(compilationUnits); if (String.IsNullOrEmpty(scriptName)) { _errorHandler.ReportError("You must declare a ScriptAssembly attribute.", String.Empty); } else if (Utility.IsValidScriptName(scriptName) == false) { string errorMessage = "The ScriptAssembly attribute referenced an invalid name '{0}'. Script names must only contain letters, numbers, dots or underscores."; _errorHandler.ReportError(String.Format(errorMessage, scriptName), String.Empty); } symbols.ScriptName = scriptName; string scriptPrefix = GetAssemblyScriptPrefix(compilationUnits); if (String.IsNullOrEmpty(scriptPrefix) == false) { if (Utility.IsValidIdentifier(scriptPrefix) == false) { string errorMessage = "The ScriptQualifier attribute referenced an invalid prefix '{0}'. Script prefix must be valid identifiers."; _errorHandler.ReportError(String.Format(errorMessage, scriptPrefix), String.Empty); } } else { scriptPrefix = scriptName.Replace(".", String.Empty); } symbols.ScriptPrefix = scriptPrefix; string assemblyScriptNamespace = GetAssemblyScriptNamespace(compilationUnits); List <TypeSymbol> types = new List <TypeSymbol>(); // Build all the types first. // Types need to be loaded upfront so that they can be used in resolving types associated // with members. foreach (CompilationUnitNode compilationUnit in compilationUnits) { foreach (NamespaceNode namespaceNode in compilationUnit.Members) { string namespaceName = namespaceNode.Name; NamespaceSymbol namespaceSymbol = symbols.GetNamespace(namespaceName); List <string> imports = null; Dictionary <string, string> aliases = null; ParseNodeList usingClauses = namespaceNode.UsingClauses; if ((usingClauses != null) && (usingClauses.Count != 0)) { foreach (ParseNode usingNode in namespaceNode.UsingClauses) { if (usingNode is UsingNamespaceNode) { if (imports == null) { imports = new List <string>(usingClauses.Count); } string referencedNamespace = ((UsingNamespaceNode)usingNode).ReferencedNamespace; if (imports.Contains(referencedNamespace) == false) { imports.Add(referencedNamespace); } } else { Debug.Assert(usingNode is UsingAliasNode); if (aliases == null) { aliases = new Dictionary <string, string>(); } UsingAliasNode aliasNode = (UsingAliasNode)usingNode; aliases[aliasNode.Alias] = aliasNode.TypeName; } } } // Add parent namespaces as imports in reverse order since they // are searched in that fashion. string[] namespaceParts = namespaceName.Split('.'); for (int i = namespaceParts.Length - 2; i >= 0; i--) { string partialNamespace; if (i == 0) { partialNamespace = namespaceParts[0]; } else { partialNamespace = String.Join(".", namespaceParts, 0, i + 1); } if (imports == null) { imports = new List <string>(); } if (imports.Contains(partialNamespace) == false) { imports.Add(partialNamespace); } } // Build type symbols for all user-defined types foreach (TypeNode typeNode in namespaceNode.Members) { UserTypeNode userTypeNode = typeNode as UserTypeNode; if (userTypeNode == null) { continue; } // Check if we have overriding script namespace for this type. string typeScriptNamespace = GetScriptNamespace(userTypeNode.Attributes); if (String.IsNullOrEmpty(typeScriptNamespace)) { typeScriptNamespace = assemblyScriptNamespace; } ClassSymbol partialTypeSymbol = null; bool isPartial = false; if ((userTypeNode.Modifiers & Modifiers.Partial) != 0) { partialTypeSymbol = (ClassSymbol)((ISymbolTable)namespaceSymbol).FindSymbol(userTypeNode.Name, /* context */ null, SymbolFilter.Types); if ((partialTypeSymbol != null) && partialTypeSymbol.IsApplicationType) { // This class will be considered as a partial class isPartial = true; // Merge code model information for the partial class onto the code model node // for the primary partial class. Interesting bits of information include things // such as base class etc. that is yet to be processed. CustomTypeNode partialTypeNode = (CustomTypeNode)partialTypeSymbol.ParseContext; partialTypeNode.MergePartialType((CustomTypeNode)userTypeNode); // Merge interesting bits of information onto the primary type symbol as well // representing this partial class BuildType(partialTypeSymbol, userTypeNode); if (String.IsNullOrEmpty(typeScriptNamespace) == false) { partialTypeSymbol.ScriptNamespace = typeScriptNamespace; } } } TypeSymbol typeSymbol = BuildType(userTypeNode, namespaceSymbol); if (typeSymbol != null) { typeSymbol.SetParseContext(userTypeNode); typeSymbol.SetParentSymbolTable(symbols); if (imports != null) { typeSymbol.SetImports(imports); } if (aliases != null) { typeSymbol.SetAliases(aliases); } if (String.IsNullOrEmpty(typeScriptNamespace) == false) { typeSymbol.ScriptNamespace = typeScriptNamespace; } if (isPartial == false) { namespaceSymbol.AddType(typeSymbol); } else { // Partial types don't get added to the namespace, so we don't have // duplicated named items. However, they still do get instantiated // and processed as usual. // // The members within partial classes refer to the partial type as their parent, // and hence derive context such as the list of imports scoped to the // particular type. // However, the members will get added to the primary partial type's list of // members so they can be found. // Effectively the partial class here gets created just to hold // context of type-symbol level bits of information such as the list of // imports, that are consumed when generating code for the members defined // within a specific partial class. ((ClassSymbol)typeSymbol).SetPrimaryPartialClass(partialTypeSymbol); } types.Add(typeSymbol); } } } } // Build inheritance chains foreach (TypeSymbol typeSymbol in types) { if (typeSymbol.Type == SymbolType.Class) { BuildTypeInheritance((ClassSymbol)typeSymbol); } } // Import members foreach (TypeSymbol typeSymbol in types) { BuildMembers(typeSymbol); } // Associate interface members with interface member symbols foreach (TypeSymbol typeSymbol in types) { if (typeSymbol.Type == SymbolType.Class) { BuildInterfaceAssociations((ClassSymbol)typeSymbol); } } // Load resource values if (_symbols.HasResources) { foreach (TypeSymbol typeSymbol in types) { if (typeSymbol.Type == SymbolType.Resources) { BuildResources((ResourcesSymbol)typeSymbol); } } } // Load documentation if (_options.EnableDocComments) { Stream docCommentsStream = options.DocCommentFile.GetStream(); if (docCommentsStream != null) { try { XmlDocument docComments = new XmlDocument(); docComments.Load(docCommentsStream); symbols.SetComments(docComments); } finally { options.DocCommentFile.CloseStream(docCommentsStream); } } } return(types); }