private ExternMethodSymbol FindAlternateInvocation(AbstractPhaseContext context, MethodSymbol originalSymbol, BoundExpression instanceExpression, BoundExpression[] parameterExpressions) { if (originalSymbol.IsStatic || originalSymbol.IsConstructor) { return(null); } List <TypeSymbol> candidates = new List <TypeSymbol>(); FindCandidateInvocationTypes(context, candidates, instanceExpression.ValueType); TypeSymbol[] paramTypes = parameterExpressions.Select(ex => ex.ValueType).ToArray(); foreach (TypeSymbol candidate in candidates) { ExternMethodSymbol externMethodSymbol = new ExternSynthesizedMethodSymbol(context, originalSymbol.Name, candidate, paramTypes, originalSymbol.ReturnType, false, false); if (CompilerUdonInterface.IsExposedToUdon(externMethodSymbol.ExternSignature)) { return(externMethodSymbol); } } return(null); }
private static PropertySymbol BuildProperty(AbstractPhaseContext context, BoundExpression sourceExpression) { TypeSymbol arrayType = sourceExpression.ValueType; TypeSymbol elementType = arrayType.ElementType; Type systemType = arrayType.ElementType.UdonType.SystemType; if (systemType == typeof(UnityEngine.Object) || systemType.IsSubclassOf(typeof(UnityEngine.Object))) { arrayType = context.GetTypeSymbol(SpecialType.System_Object).MakeArrayType(context); } string arrayTypeName = CompilerUdonInterface.GetMethodTypeName(arrayType.UdonType); string arrayElementTypeName = CompilerUdonInterface.GetUdonTypeName(arrayType.UdonType.ElementType); TypeSymbol intType = context.GetTypeSymbol(SpecialType.System_Int32); MethodSymbol setMethod = new ExternSynthesizedMethodSymbol(context, $"{arrayTypeName}.__Set__SystemInt32_{arrayElementTypeName}__SystemVoid", new[] { intType, elementType }, null, false); MethodSymbol getMethod = new ExternSynthesizedMethodSymbol(context, $"{arrayTypeName}.__Get__SystemInt32__{arrayElementTypeName}", new[] { intType }, elementType, false); return(new SynthesizedPropertySymbol(context, getMethod, setMethod)); }
public static bool IsUserDefinedEnum(System.Type type) { return((type.IsEnum && !CompilerUdonInterface.IsExternType(type)) || (type.IsArray && type.GetElementType().IsEnum&& !CompilerUdonInterface.IsExternType(type.GetElementType()))); }
protected override TreeViewItem BuildRoot() { BuildExposedTypeList(); itemMetadatas.Clear(); hiearchyItems.Clear(); TreeViewItem root = new TreeViewItem(0, -1); itemMetadatas.Add(root, new TypeItemMetadata()); int currentID = 1; // Build the namespace sections first foreach (Type type in _exposedTypes) { string typeNamespace = type.Namespace; if (string.IsNullOrEmpty(typeNamespace)) { if (type.GetElementType() != null && type.GetElementType().Namespace != null) { typeNamespace = type.GetElementType().Namespace; } } TreeViewItem namespaceItem = GetNamespaceParent(typeNamespace, root, ref currentID); } int currentTypeCount = 0; foreach (Type type in _exposedTypes.OrderBy(e => e.Name)) { if (currentTypeCount % 30 == 0) { EditorUtility.DisplayProgressBar("Adding types...", $"Adding type {type}", currentTypeCount / (float)_exposedTypes.Count); } currentTypeCount++; // if (ShouldHideTypeTopLevel(type, true)) // continue; string typeNamespace = type.Namespace; if (string.IsNullOrEmpty(typeNamespace)) { if (type.GetElementType() != null && type.GetElementType().Namespace != null) { typeNamespace = type.GetElementType().Namespace; } } TreeViewItem namespaceParent = GetNamespaceParent(typeNamespace, root, ref currentID); string typeTypeName = ""; if (type.IsEnum) { typeTypeName = " <enum>"; } else if (type.IsValueType) { typeTypeName = " <struct>"; } else if (type.IsArray) { typeTypeName = " <array>"; } else { typeTypeName = " <class>"; } TreeViewItem typeParent = new TreeViewItem(currentID++, namespaceParent.depth + 1, type.Name + typeTypeName); namespaceParent.AddChild(typeParent); itemMetadatas.Add(typeParent, new TypeItemMetadata() { isType = true }); //if (!type.IsEnum) //{ // // Variable definition // TreeViewItem variableDef = new TreeViewItem(currentID++, typeParent.depth + 1, "<variable> " + type.Name); // typeParent.AddChild(variableDef); // itemMetadatas.Add(variableDef, new TypeItemMetadata() { exposed = resolver.ValidateUdonTypeName(resolver.GetUdonTypeName(type), UdonReferenceType.Variable) }); //} // Type definition //TreeViewItem typeDef = new TreeViewItem(currentID++, typeParent.depth + 1, "<type> " + type.Name); //typeParent.AddChild(typeDef); //itemMetadatas.Add(typeDef, new TypeItemMetadata() { exposed = resolver.ValidateUdonTypeName(resolver.GetUdonTypeName(type), UdonReferenceType.Type) }); // Internal type TreeViewItem internalTypeDef = new TreeViewItem(currentID++, typeParent.depth + 1, "<Type> " + type.Name); typeParent.AddChild(internalTypeDef); itemMetadatas.Add(internalTypeDef, new TypeItemMetadata() { exposed = UdonEditorManager.Instance.GetTypeFromTypeString(CompilerUdonInterface.GetUdonTypeName(type)) != null }); // Const definition //if (!type.IsArray && !type.IsEnum) //{ // TreeViewItem constDef = new TreeViewItem(currentID++, typeParent.depth + 1, "<const> " + type.Name); // typeParent.AddChild(constDef); // itemMetadatas.Add(constDef, new TypeItemMetadata() { exposed = resolver.ValidateUdonTypeName(resolver.GetUdonTypeName(type), UdonReferenceType.Const) }); //} BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static; if (!showBaseTypeMembers) { bindingFlags |= BindingFlags.DeclaredOnly; } foreach (ConstructorInfo constructor in type.GetConstructors(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly)) { AddChildNode(typeParent, constructor, ref currentID); } foreach (FieldInfo field in type.GetFields(bindingFlags)) { AddChildNode(typeParent, field, ref currentID); } foreach (PropertyInfo property in type.GetProperties(bindingFlags)) { AddChildNode(typeParent, property, ref currentID); } if (!type.IsEnum) { foreach (MethodInfo method in type.GetMethods(bindingFlags).Where(e => (!type.IsArray || e.Name != "Address"))) { if (method.IsSpecialName && !method.Name.StartsWith("op_")) { continue; } AddChildNode(typeParent, method, ref currentID); } } } EditorUtility.ClearProgressBar(); BuildDrawInfo(root); //foreach (string exposedExtern in exposedUdonExterns) //{ // Debug.Log(exposedExtern); //} return(root); }
private void BuildExposedTypeList() { if (_exposedTypes != null) { return; } // Stopwatch timer = Stopwatch.StartNew(); try { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); object typeSetLock = new object(); HashSet <Type> exposedTypeSet = new HashSet <Type>(); int mainThreadID = Thread.CurrentThread.ManagedThreadId; _assemblyCounter = 0; int totalAssemblies = assemblies.Length; Parallel.ForEach(assemblies, new ParallelOptions { MaxDegreeOfParallelism = 3 }, assembly => { if (assembly.FullName.Contains("UdonSharp") || assembly.FullName.Contains("CodeAnalysis")) { return; } Interlocked.Increment(ref _assemblyCounter); if (Thread.CurrentThread.ManagedThreadId == mainThreadID) // Can only be called from the main thread, since Parallel.ForEach uses the calling thread for some loops we just only run this in that thread. { EditorUtility.DisplayProgressBar("Processing methods and types...", $"{_assemblyCounter}/{totalAssemblies}", _assemblyCounter / (float)totalAssemblies); } Type[] assemblyTypes = assembly.GetTypes(); List <Type> types = new List <Type>(); foreach (Type assemblyType in assemblyTypes) { types.Add(assemblyType); types.AddRange(GetNestedTypes(assemblyType)); } types = types.Distinct().ToList(); HashSet <Type> localExposedTypeSet = new HashSet <Type>(); foreach (Type type in types) { if (type.IsByRef) { continue; } string typeName = CompilerUdonInterface.GetUdonTypeName(type); if (UdonEditorManager.Instance.GetTypeFromTypeString(typeName) != null) { localExposedTypeSet.Add(type); if (!type.IsGenericType && !type.IsGenericTypeDefinition) { localExposedTypeSet.Add(type.MakeArrayType()); } } MethodInfo[] methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static); foreach (MethodInfo method in methods) { if (CompilerUdonInterface.IsExposedToUdon(CompilerUdonInterface.GetUdonMethodName(method))) { localExposedTypeSet.Add(method.DeclaringType); // We also want to highlight types that can be returned or taken as parameters if (method.ReturnType != typeof(void) && method.ReturnType.Name != "T" && method.ReturnType.Name != "T[]") { localExposedTypeSet.Add(method.ReturnType); if (!method.ReturnType.IsArray && !method.ReturnType.IsGenericType && !method.ReturnType.IsGenericTypeDefinition) { localExposedTypeSet.Add(method.ReturnType.MakeArrayType()); } } foreach (ParameterInfo parameterInfo in method.GetParameters()) { if (!parameterInfo.ParameterType.IsByRef) { localExposedTypeSet.Add(parameterInfo.ParameterType); if (!parameterInfo.ParameterType.IsArray) { localExposedTypeSet.Add(parameterInfo.ParameterType.MakeArrayType()); } } } } } foreach (PropertyInfo property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)) { MethodInfo propertyGetter = property.GetGetMethod(); if (propertyGetter == null) { continue; } if (CompilerUdonInterface.IsExposedToUdon(CompilerUdonInterface.GetUdonMethodName(propertyGetter))) { Type returnType = propertyGetter.ReturnType; localExposedTypeSet.Add(property.DeclaringType); if (returnType != typeof(void) && returnType.Name != "T" && returnType.Name != "T[]") { localExposedTypeSet.Add(returnType); if (!returnType.IsArray && !returnType.IsGenericType && !returnType.IsGenericTypeDefinition) { localExposedTypeSet.Add(returnType.MakeArrayType()); } } } } foreach (FieldInfo field in type.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)) { if (field.DeclaringType?.FullName == null) // Fix some weird types in Odin that don't have a name for their declaring type { continue; } if (CompilerUdonInterface.IsExposedToUdon(CompilerUdonInterface.GetUdonAccessorName(field, CompilerUdonInterface.FieldAccessorType.Get))) { Type returnType = field.FieldType; localExposedTypeSet.Add(field.DeclaringType); if (returnType != typeof(void) && returnType.Name != "T" && returnType.Name != "T[]") { localExposedTypeSet.Add(returnType); if (!returnType.IsArray && !returnType.IsGenericType && !returnType.IsGenericTypeDefinition) { localExposedTypeSet.Add(returnType.MakeArrayType()); } } } } } if (localExposedTypeSet.Count == 0) { return; } lock (typeSetLock) { exposedTypeSet.UnionWith(localExposedTypeSet); } }); _exposedTypes = exposedTypeSet.ToList(); } finally { EditorUtility.ClearProgressBar(); } _exposedTypes.RemoveAll(e => e.Name == "T" || e.Name == "T[]"); // Debug.Log($"Elapsed time {timer.Elapsed.TotalSeconds * 1000.0}ms"); }
private void AddChildNode(TreeViewItem parentItem, MemberInfo memberInfo, ref int currentID) { var obsoleteAttribute = memberInfo.GetCustomAttribute <System.ObsoleteAttribute>(); if (obsoleteAttribute != null) { return; } if (memberInfo.MemberType == MemberTypes.Property && (!((PropertyInfo)memberInfo).GetGetMethod()?.IsPublic ?? false)) { return; } if (memberInfo.DeclaringType.IsEnum) { return; } string staticStr = ""; { if ((memberInfo is FieldInfo fieldInfo && fieldInfo.IsStatic) || (memberInfo is PropertyInfo propertyInfo && (propertyInfo.GetGetMethod()?.IsStatic ?? false)) || (memberInfo is MethodInfo methodInfo && methodInfo.IsStatic)) { staticStr = "<Static>"; } } TreeViewItem memberItem = new TreeViewItem(currentID++, parentItem.depth + 1, $"<{memberInfo.MemberType}>{staticStr} {memberInfo}"); TypeItemMetadata itemMetadata = new TypeItemMetadata(); itemMetadata.member = memberInfo; switch (memberInfo.MemberType) { case MemberTypes.Constructor: case MemberTypes.Method: itemMetadata.exposed = CompilerUdonInterface.IsExposedToUdon(CompilerUdonInterface.GetUdonMethodName((MethodBase)memberInfo)); break; case MemberTypes.Field: string getAccessor = CompilerUdonInterface.GetUdonAccessorName((FieldInfo)memberInfo, CompilerUdonInterface.FieldAccessorType.Get); // string setAccessor = resolver.GetUdonFieldAccessorName((FieldInfo)memberInfo, CompilerUdonInterface.FieldAccessorType.Set, false); itemMetadata.exposed = CompilerUdonInterface.IsExposedToUdon(getAccessor); break; case MemberTypes.Property: MethodInfo getMethod = ((PropertyInfo)memberInfo).GetGetMethod(); if (getMethod == null) { return; } string getProperty = CompilerUdonInterface.GetUdonMethodName(getMethod); // if (((PropertyInfo)memberInfo).GetSetMethod() != null) // { // string setProperty = resolver.GetUdonMethodName(((PropertyInfo)memberInfo).GetSetMethod(), false); // } itemMetadata.exposed = CompilerUdonInterface.IsExposedToUdon(getProperty); break; } parentItem.AddChild(memberItem); itemMetadatas.Add(memberItem, itemMetadata); }
public UdonSharpBehaviourMethodSymbol(IMethodSymbol sourceSymbol, AbstractPhaseContext context) : base(sourceSymbol, context) { IsUdonEvent = CompilerUdonInterface.IsUdonEvent(sourceSymbol.Name); ExportedMethodAddress = new ExportAddress(ExportAddress.AddressKind.String, this); }
private static void Compile(CompilationContext compilationContext, IReadOnlyDictionary<string, ProgramAssetInfo> rootProgramLookup, IEnumerable<string> allSourcePaths, string[] scriptingDefines) { Stopwatch setupTimer = Stopwatch.StartNew(); CompilerUdonInterface.CacheInit(); CompilerUdonInterface.AssemblyCacheInit(); compilationContext.CurrentPhase = CompilationContext.CompilePhase.Setup; ModuleBinding[] syntaxTrees = compilationContext.LoadSyntaxTreesAndCreateModules(allSourcePaths, scriptingDefines); foreach (ModuleBinding binding in syntaxTrees) { foreach (Diagnostic diag in binding.tree.GetDiagnostics()) { if (diag.Severity != Microsoft.CodeAnalysis.DiagnosticSeverity.Error) continue; compilationContext.AddDiagnostic(DiagnosticSeverity.Error, diag.Location, $"{diag.Severity.ToString().ToLower()} {diag.Id}: {diag.GetMessage()}"); } } if (compilationContext.ErrorCount > 0) return; List<ModuleBinding> rootTrees = new List<ModuleBinding>(); foreach (ModuleBinding treeBinding in syntaxTrees) { if (rootProgramLookup.ContainsKey(treeBinding.filePath)) { ProgramAssetInfo info = rootProgramLookup[treeBinding.filePath]; treeBinding.programAsset = info.programAsset; treeBinding.programClass = info.scriptClass; // ReSharper disable once Unity.NoNullPropagation treeBinding.programScript = treeBinding?.programAsset?.sourceCsScript; rootTrees.Add(treeBinding); } } PrintStageTime("U# Setup", setupTimer); Stopwatch roslynCompileTimer = Stopwatch.StartNew(); compilationContext.CurrentPhase = CompilationContext.CompilePhase.RoslynCompile; // Run compilation for the semantic views CSharpCompilation compilation = CSharpCompilation.Create( $"UdonSharpRoslynCompileAssembly{_assemblyCounter++}", syntaxTrees.Select(e => e.tree), CompilationContext.GetMetadataReferences(), new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, concurrentBuild: compilationContext.Options.ConcurrentBuild)); PrintStageTime("Roslyn Compile", roslynCompileTimer); compilationContext.RoslynCompilation = compilation; byte[] builtAssembly = null; Stopwatch roslynEmitTimer = Stopwatch.StartNew(); using (MemoryStream memoryStream = new MemoryStream()) { EmitResult emitResult = compilation.Emit(memoryStream); if (emitResult.Success) { builtAssembly = memoryStream.ToArray(); } else { foreach (Diagnostic diag in emitResult.Diagnostics) { if (diag.Severity == Microsoft.CodeAnalysis.DiagnosticSeverity.Error) { compilationContext.AddDiagnostic(DiagnosticSeverity.Error, diag.Location, $"{diag.Severity.ToString().ToLower()} {diag.Id}: {diag.GetMessage()}"); } } } } PrintStageTime("Roslyn Emit", roslynEmitTimer); if (compilationContext.ErrorCount > 0) return; foreach (ModuleBinding tree in syntaxTrees) tree.semanticModel = compilation.GetSemanticModel(tree.tree); ConcurrentBag<(INamedTypeSymbol, ModuleBinding)> rootUdonSharpTypes = new ConcurrentBag<(INamedTypeSymbol, ModuleBinding)>(); HashSet<INamedTypeSymbol> typesWithAssociatedProgramAssets = new HashSet<INamedTypeSymbol>(); object programTypesLock = new object(); Parallel.ForEach(rootTrees, new ParallelOptions { MaxDegreeOfParallelism = MAX_PARALLELISM}, module => { SemanticModel model = module.semanticModel; SyntaxTree tree = model.SyntaxTree; INamedTypeSymbol udonSharpBehaviourDeclaration = null; foreach (ClassDeclarationSyntax classDecl in tree.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>()) { if (model.GetDeclaredSymbol(classDecl) is INamedTypeSymbol classType && classType.IsUdonSharpBehaviour()) { if (!classType.IsAbstract && classType.Name != Path.GetFileNameWithoutExtension(module.filePath)) { compilationContext.AddDiagnostic(DiagnosticSeverity.Error, classType.DeclaringSyntaxReferences.First().GetSyntax(), "UdonSharpBehaviour classes must have the same name as their containing .cs file"); return; } if (!ReferenceEquals(module.programScript, null) && ReferenceEquals(module.programClass, null)) { compilationContext.AddDiagnostic(DiagnosticSeverity.Error, classType.DeclaringSyntaxReferences.First().GetSyntax(), "Could not retrieve C# class, make sure the MonoBehaviour class name matches the name of the .cs file and Unity has had a chance to compile the C# script."); return; } if (!ReferenceEquals(module.programAsset, null) && classType.Name == Path.GetFileNameWithoutExtension(module.filePath)) { if (classType.IsAbstract) { compilationContext.AddDiagnostic(DiagnosticSeverity.Error, classType.DeclaringSyntaxReferences.First().GetSyntax(), "Abstract U# behaviours cannot have an associated U# program asset"); return; } if (classType.IsGenericType) { compilationContext.AddDiagnostic(DiagnosticSeverity.Error, classType.DeclaringSyntaxReferences.First().GetSyntax(), "Generic U# behaviours cannot have an associated U# program asset"); return; } } if (classType.IsAbstract || classType.IsGenericType) continue; // If there are multiple UdonSharpBehaviours declared in the same behaviour, they need to be partial classes of the same class // We'll skip adding them as roots in that case if (udonSharpBehaviourDeclaration == null) { udonSharpBehaviourDeclaration = classType; rootUdonSharpTypes.Add((classType, module)); } } }
public static BoundInvocationExpression CreateBoundInvocation(AbstractPhaseContext context, SyntaxNode node, MethodSymbol symbol, BoundExpression instanceExpression, BoundExpression[] parameterExpressions) { if (TryCreateShimInvocation(context, node, symbol, instanceExpression, parameterExpressions, out var boundShimInvocation)) { return(boundShimInvocation); } if (symbol.IsExtern) { if (CompilerUdonInterface.IsUdonEvent(symbol.Name) && symbol.ContainingType == context.GetTypeSymbol(typeof(UdonSharpBehaviour))) // Pass through for making base calls on the U# behaviour type return noop { return(new BoundUdonSharpBehaviourInvocationExpression(node, symbol, instanceExpression, parameterExpressions)); } if (symbol.IsOperator) { // Enum equality/inequality if (symbol.ContainingType?.IsEnum ?? false) { MethodSymbol objectEqualsMethod = context.GetTypeSymbol(SpecialType.System_Object) .GetMember <MethodSymbol>("Equals", context); var boundEqualsInvocation = CreateBoundInvocation(context, node, objectEqualsMethod, parameterExpressions[0], new[] { parameterExpressions[1] }); if (symbol.Name == "op_Equality") { return(boundEqualsInvocation); } MethodSymbol boolNotOperator = new ExternSynthesizedOperatorSymbol( BuiltinOperatorType.UnaryNegation, context.GetTypeSymbol(SpecialType.System_Boolean), context); return(new BoundExternInvocation(node, context, boolNotOperator, null, new BoundExpression[] { boundEqualsInvocation })); } if (node is AssignmentExpressionSyntax) { return(new BoundCompoundAssignmentExpression(context, node, (BoundAccessExpression)parameterExpressions[0], symbol, parameterExpressions[1])); } if (symbol is ExternBuiltinOperatorSymbol externBuiltinOperatorSymbol && externBuiltinOperatorSymbol.OperatorType == BuiltinOperatorType.BitwiseNot) { return(new BoundBitwiseNotExpression(node, parameterExpressions[0])); } if (parameterExpressions.Length == 2 || symbol.Name == "op_UnaryNegation" || symbol.Name == "op_LogicalNot") { return(new BoundBuiltinOperatorInvocationExpression(node, context, symbol, parameterExpressions)); } throw new NotSupportedException("Operator expressions must have either 1 or 2 parameters", node.GetLocation()); } return(new BoundExternInvocation(node, context, symbol, instanceExpression, parameterExpressions)); } if (symbol.IsStatic) { return(new BoundStaticUserMethodInvocation(node, symbol, parameterExpressions)); } if (symbol is UdonSharpBehaviourMethodSymbol udonSharpBehaviourMethodSymbol) { if (instanceExpression != null && !instanceExpression.IsThis) { udonSharpBehaviourMethodSymbol.MarkNeedsReferenceExport(); } return(new BoundUdonSharpBehaviourInvocationExpression(node, symbol, instanceExpression, parameterExpressions)); } throw new System.NotImplementedException(); }
protected override bool HandlesTypeSerialization(TypeSerializationMetadata typeMetadata) { VerifyTypeCheckSanity(); return(typeMetadata.cSharpType.IsEnum && !CompilerUdonInterface.IsExternType(typeMetadata.cSharpType)); }