public void AddExternCall(string externCall, string comment = "") { externStringSet.Add(externCall); AppendCommentedLine($"EXTERN, \"{externCall}\"", comment); programCounter += UdonSharpUtils.GetUdonInstructionSize("EXTERN"); }
// Mostly copy paste of above adapted for just checking the types in `params` args private int ScoreMethodParamArgPair(System.Type methodParam, System.Type argType) { // This doesn't yet handle implicit user defined casts... there are probably other things this should handle too. int score = 1000000; if (methodParam == argType) { score = 0; } else if (methodParam.IsValidNumericImplicitCastTargetType() && argType.IsValidNumericImplictCastSourceType()) { score = UdonSharpUtils.GetImplicitNumericCastDistance(methodParam, argType); } else if (methodParam == typeof(object)) { score = 30; // We want to avoid object args as much as possible } else if (argType.IsSubclassOf(methodParam)) { // Count the distance in the inheritance System.Type currentType = argType; score = 0; while (currentType != methodParam && score < 20) { score++; currentType = currentType.BaseType; } } return(score); }
/// <summary> /// Verifies that Udon supports the given type and resolves the type name used to reference it in Udon /// </summary> /// <param name="externType">The found type</param> /// <param name="skipBaseTypeRemap">Skips remapping base VRCSDK types, this is primarily used for VRCSDK function return value types since they may point to the base type and we want to maintain that.</param> /// <returns>The Udon type name string if it is a valid Udon type, /// or null if it is not a valid Udon type.</returns> public string GetUdonTypeName(System.Type externType, bool skipBaseTypeRemap = false) { if (!skipBaseTypeRemap) { externType = UdonSharpUtils.RemapBaseType(externType); } string externTypeName = externType.GetNameWithoutGenericArity(); while (externType.IsArray || externType.IsByRef) { externType = externType.GetElementType(); } string typeNamespace = externType.Namespace; // Handle nested type names (+ sign in names) if (externType.DeclaringType != null) { string declaringTypeNamespace = ""; System.Type declaringType = externType.DeclaringType; while (declaringType != null) { declaringTypeNamespace = $"{externType.DeclaringType.Name}.{declaringTypeNamespace}"; declaringType = declaringType.DeclaringType; } typeNamespace += $".{declaringTypeNamespace}"; } if (externTypeName == "T" || externTypeName == "T[]") { typeNamespace = ""; } string fullTypeName = SanitizeTypeName($"{typeNamespace}.{externTypeName}"); foreach (System.Type genericType in externType.GetGenericArguments()) { fullTypeName += GetUdonTypeName(genericType); } // Seems like Udon does shortening for this specific type somewhere if (fullTypeName == "SystemCollectionsGenericListT") { fullTypeName = "ListT"; } else if (fullTypeName == "SystemCollectionsGenericIEnumerableT") { fullTypeName = "IEnumerableT"; } fullTypeName = fullTypeName.Replace("VRCUdonUdonBehaviour", "VRCUdonCommonInterfacesIUdonEventReceiver"); return(fullTypeName); }
private List <ClassDefinition> BuildClassDefinitions() { string[] udonSharpDataAssets = AssetDatabase.FindAssets($"t:{typeof(UdonSharpProgramAsset).Name}"); List <UdonSharpProgramAsset> udonSharpPrograms = new List <UdonSharpProgramAsset>(); foreach (string dataGuid in udonSharpDataAssets) { udonSharpPrograms.Add(AssetDatabase.LoadAssetAtPath <UdonSharpProgramAsset>(AssetDatabase.GUIDToAssetPath(dataGuid))); } List <ClassDefinition> classDefinitions = new List <ClassDefinition>(); foreach (UdonSharpProgramAsset udonSharpProgram in udonSharpPrograms) { if (udonSharpProgram.sourceCsScript == null) { continue; } string sourcePath = AssetDatabase.GetAssetPath(udonSharpProgram.sourceCsScript); string programSource = UdonSharpUtils.ReadFileTextSync(sourcePath); ResolverContext resolver = new ResolverContext(); SymbolTable classSymbols = new SymbolTable(resolver, null); classSymbols.OpenSymbolTable(); LabelTable classLabels = new LabelTable(); SyntaxTree tree = CSharpSyntaxTree.ParseText(programSource); ClassVisitor classVisitor = new ClassVisitor(resolver, classSymbols, classLabels); try { classVisitor.Visit(tree.GetRoot()); } catch (System.Exception e) { UdonSharpUtils.LogBuildError($"{e.GetType()}: {e.Message}", sourcePath.Replace("/", "\\"), 0, 0); return(null); } classSymbols.CloseSymbolTable(); classVisitor.classDefinition.classScript = udonSharpProgram.sourceCsScript; classDefinitions.Add(classVisitor.classDefinition); } return(classDefinitions); }
public void AddJumpIfFalse(JumpLabel jumpTarget, string comment = "") { if (jumpTarget.IsResolved) { AppendCommentedLine($"JUMP_IF_FALSE, {jumpTarget.AddresStr()}", comment); } else { AppendCommentedLine($"JUMP_IF_FALSE_LABEL, [{jumpTarget.uniqueName}]", comment); } programCounter += UdonSharpUtils.GetUdonInstructionSize("JUMP_IF_FALSE"); }
public MethodBase FindBestOverloadFunction(MethodBase[] methods, List <System.Type> methodArgs, bool checkIfInUdon = true) { if (methods.Length == 0) { throw new System.ArgumentException("Cannot find overload from 0 length method array"); } List <MethodBase> validMethods = new List <MethodBase>(); foreach (MethodBase method in methods) { ParameterInfo[] methodParams = method.GetParameters(); bool isMethodValid = true; for (int i = 0; i < methodParams.Length; ++i) { ParameterInfo currentParam = methodParams[i]; // Check method arg count if (i >= methodArgs.Count && !currentParam.HasDefaultValue) { isMethodValid = false; break; } else if (currentParam.HasDefaultValue) { continue; } System.Type argType = methodArgs[i]; if (!currentParam.ParameterType.IsImplicitlyAssignableFrom(argType) && !currentParam.HasParamsParameter() && !currentParam.ParameterType.IsByRef) { // Handle implicit upcasts to int from lower precision types if (method is OperatorMethodInfo operatorParam && (operatorParam.operatorType == BuiltinOperatorType.LeftShift || operatorParam.operatorType == BuiltinOperatorType.RightShift) && (argType != typeof(uint) && argType != typeof(ulong) && argType != typeof(long))) { if (UdonSharpUtils.GetNumericConversionMethod(currentParam.ParameterType, argType) == null) { isMethodValid = false; break; } } else { isMethodValid = false; break; } }
public void AddJump(JumpLabel jumpTarget, string comment = "") { #if USE_UDON_LABELS AppendCommentedLine($"JUMP, {jumpTarget.uniqueName}", comment); #else if (jumpTarget.IsResolved) { AppendCommentedLine($"JUMP, {jumpTarget.AddresStr()}", comment); } else { AppendCommentedLine($"JUMP_LABEL, [{jumpTarget.uniqueName}]", comment); } #endif programCounter += UdonSharpUtils.GetUdonInstructionSize("JUMP"); }
private static string HashSourceFile(MonoScript script) { string scriptPath = AssetDatabase.GetAssetPath(script); string scriptText = ""; try { scriptText = UdonSharpUtils.ReadFileTextSync(scriptPath); } catch (System.Exception e) { scriptText = Random.value.ToString(); Debug.Log(e); } return(UdonSharpUtils.HashString(scriptText)); }
public CompilationModule(UdonSharpProgramAsset sourceAsset) { programAsset = sourceAsset; resolver = new ResolverContext(); moduleSymbols = new SymbolTable(resolver, null); moduleLabels = new LabelTable(); fieldsWithInitializers = new HashSet <FieldDeclarationSyntax>(); if (programAsset.sourceCsScript == null) { throw new System.ArgumentException($"Asset '{AssetDatabase.GetAssetPath(programAsset)}' does not have a valid program source to compile from"); } sourceCode = UdonSharpUtils.ReadFileTextSync(AssetDatabase.GetAssetPath(programAsset.sourceCsScript)); settings = UdonSharpSettings.GetSettings(); }
public string GetUdonFieldAccessorName(FieldInfo externField, FieldAccessorType accessorType, bool validate = true) { System.Type fieldType = UdonSharpUtils.RemapBaseType(externField.DeclaringType); string functionNamespace = SanitizeTypeName(fieldType.FullName).Replace("VRCUdonUdonBehaviour", "VRCUdonCommonInterfacesIUdonEventReceiver"); string methodName = $"__{(accessorType == FieldAccessorType.Get ? "get" : "set")}_{externField.Name.Trim('_')}"; string paramStr = $"__{GetUdonTypeName(externField.FieldType)}"; string finalFunctionSig = $"{functionNamespace}.{methodName}{paramStr}"; if (validate && !nodeDefinitionLookup.Contains(finalFunctionSig)) { throw new System.Exception($"Field accessor {finalFunctionSig} is not exposed in Udon"); } return(finalFunctionSig); }
public bool OnBuildRequested(VRCSDKRequestedBuildType requestedBuildType) { if (requestedBuildType == VRCSDKRequestedBuildType.Avatar) { return(true); } if (UdonSharpSettings.GetSettings()?.disableUploadCompile ?? false) { return(true); } UdonSharpProgramAsset.CompileAllCsPrograms(true, false); UdonSharpEditorCache.SaveAllCache(); if (UdonSharpProgramAsset.AnyUdonSharpScriptHasError()) { Debug.LogError("[<color=#FF00FF>UdonSharp</color>] Failed to compile UdonSharp scripts for build, check error log for details."); UdonSharpUtils.ShowEditorNotification("Failed to compile UdonSharp scripts for build, check error log for details."); return(false); } return(true); }
public int Compile(List <ClassDefinition> classDefinitions) { if (programAsset.sourceCsScript == null) { throw new System.ArgumentException($"Asset '{AssetDatabase.GetAssetPath(programAsset)}' does not have a valid program source to compile from"); } Profiler.BeginSample("Compile Module"); programAsset.compileErrors.Clear(); sourceCode = File.ReadAllText(AssetDatabase.GetAssetPath(programAsset.sourceCsScript)); Profiler.BeginSample("Parse AST"); SyntaxTree tree = CSharpSyntaxTree.ParseText(sourceCode); Profiler.EndSample(); int errorCount = 0; string errorString = ""; foreach (Diagnostic diagnostic in tree.GetDiagnostics()) { if (diagnostic.Severity == DiagnosticSeverity.Error) { errorCount++; LinePosition linePosition = diagnostic.Location.GetLineSpan().StartLinePosition; errorString = UdonSharpUtils.LogBuildError($"error {diagnostic.Descriptor.Id}: {diagnostic.GetMessage()}", AssetDatabase.GetAssetPath(programAsset.sourceCsScript).Replace("/", "\\"), linePosition.Line, linePosition.Character); programAsset.compileErrors.Add(errorString); } } if (errorCount > 0) { ErrorCount = errorCount; Profiler.EndSample(); return(errorCount); } Profiler.BeginSample("Visit"); UdonSharpFieldVisitor fieldVisitor = new UdonSharpFieldVisitor(fieldsWithInitializers); fieldVisitor.Visit(tree.GetRoot()); MethodVisitor methodVisitor = new MethodVisitor(resolver, moduleSymbols, moduleLabels); methodVisitor.Visit(tree.GetRoot()); UdonSharpSettings settings = UdonSharpSettings.GetSettings(); ClassDebugInfo debugInfo = null; if (settings == null || settings.buildDebugInfo) { debugInfo = new ClassDebugInfo(sourceCode, settings == null || settings.includeInlineCode); } ASTVisitor visitor = new ASTVisitor(resolver, moduleSymbols, moduleLabels, methodVisitor.definedMethods, classDefinitions, debugInfo); try { visitor.Visit(tree.GetRoot()); visitor.VerifyIntegrity(); } catch (System.Exception e) { SyntaxNode currentNode = visitor.visitorContext.currentNode; string logMessage = ""; if (currentNode != null) { FileLinePositionSpan lineSpan = currentNode.GetLocation().GetLineSpan(); logMessage = UdonSharpUtils.LogBuildError($"{e.GetType()}: {e.Message}", AssetDatabase.GetAssetPath(programAsset.sourceCsScript).Replace("/", "\\"), lineSpan.StartLinePosition.Line, lineSpan.StartLinePosition.Character); } else { logMessage = e.ToString(); Debug.LogException(e); } programAsset.compileErrors.Add(logMessage); errorCount++; } Profiler.EndSample(); if (errorCount == 0) { Profiler.BeginSample("Build assembly"); string dataBlock = BuildHeapDataBlock(); string codeBlock = visitor.GetCompiledUasm(); programAsset.SetUdonAssembly(dataBlock + codeBlock); Profiler.EndSample(); Profiler.BeginSample("Assemble Program"); programAsset.AssembleCsProgram((uint)(moduleSymbols.GetAllUniqueChildSymbols().Count + visitor.GetExternStrCount())); Profiler.EndSample(); programAsset.behaviourIDHeapVarName = visitor.GetIDHeapVarName(); programAsset.fieldDefinitions = visitor.visitorContext.localFieldDefinitions; if (debugInfo != null) { debugInfo.FinalizeDebugInfo(); } programAsset.debugInfo = debugInfo; } Profiler.EndSample(); return(errorCount); }
public void AddCopy(string comment = "") { AppendCommentedLine("COPY", comment); programCounter += UdonSharpUtils.GetUdonInstructionSize("COPY"); }
public void AddJumpIndirect(SymbolDefinition addressSymbol, string comment = "") { AppendCommentedLine($"JUMP_INDIRECT, {addressSymbol.symbolUniqueName}", comment); programCounter += UdonSharpUtils.GetUdonInstructionSize("JUMP_INDIRECT"); }
public void AddJumpToExit() { AppendCommentedLine($"JUMP, 0xFFFFFFFF", ""); programCounter += UdonSharpUtils.GetUdonInstructionSize("JUMP"); }
private void AddPush(string heapAddress, string comment) { AppendCommentedLine($"PUSH, {heapAddress}", comment); programCounter += UdonSharpUtils.GetUdonInstructionSize("PUSH"); }
public int Compile(List <ClassDefinition> classDefinitions) { if (programAsset.sourceCsScript == null) { throw new System.ArgumentException($"Asset '{AssetDatabase.GetAssetPath(programAsset)}' does not have a valid program source to compile from"); } sourceCode = File.ReadAllText(AssetDatabase.GetAssetPath(programAsset.sourceCsScript)); SyntaxTree tree = CSharpSyntaxTree.ParseText(sourceCode); int errorCount = 0; foreach (Diagnostic diagnostic in tree.GetDiagnostics()) { if (diagnostic.Severity == DiagnosticSeverity.Error) { errorCount++; LinePosition linePosition = diagnostic.Location.GetLineSpan().StartLinePosition; UdonSharpUtils.LogBuildError($"error {diagnostic.Descriptor.Id}: {diagnostic.GetMessage()}", AssetDatabase.GetAssetPath(programAsset.sourceCsScript).Replace("/", "\\"), linePosition.Line, linePosition.Character); } if (errorCount > 0) { return(errorCount); } } UdonSharpFieldVisitor fieldVisitor = new UdonSharpFieldVisitor(fieldsWithInitializers); fieldVisitor.Visit(tree.GetRoot()); MethodVisitor methodVisitor = new MethodVisitor(resolver, moduleSymbols, moduleLabels); methodVisitor.Visit(tree.GetRoot()); ASTVisitor visitor = new ASTVisitor(resolver, moduleSymbols, moduleLabels, methodVisitor.definedMethods, classDefinitions); try { visitor.Visit(tree.GetRoot()); visitor.VerifyIntegrity(); } catch (System.Exception e) { SyntaxNode currentNode = visitor.visitorContext.currentNode; if (currentNode != null) { FileLinePositionSpan lineSpan = currentNode.GetLocation().GetLineSpan(); UdonSharpUtils.LogBuildError($"{e.GetType()}: {e.Message}", AssetDatabase.GetAssetPath(programAsset.sourceCsScript).Replace("/", "\\"), lineSpan.StartLinePosition.Line, lineSpan.StartLinePosition.Character); } else { Debug.LogException(e); } errorCount++; } string dataBlock = BuildHeapDataBlock(); string codeBlock = visitor.GetCompiledUasm(); programAsset.SetUdonAssembly(dataBlock + codeBlock); programAsset.AssembleCsProgram(); programAsset.fieldDefinitions = visitor.visitorContext.localFieldDefinitions; return(errorCount); }
public void AddJumpIndirect(SymbolDefinition addressSymbol, string comment = "") { //throw new System.NotImplementedException("Jump indirect instruction is not implemented in UdonSharp yet."); // I'm not sure why JUMP_INDIRECT both takes an address and pops an address from the stack AppendCommentedLine($"JUMP_INDIRECT, {addressSymbol.symbolUniqueName}", comment); programCounter += UdonSharpUtils.GetUdonInstructionSize("JUMP_INDIRECT"); }
private bool IsNormalUnityObject(System.Type declaredType, FieldDefinition fieldDefinition) { return(!UdonSharpUtils.IsUserDefinedBehaviour(declaredType) && (fieldDefinition == null || fieldDefinition.fieldSymbol.userCsType == null || !fieldDefinition.fieldSymbol.IsUserDefinedBehaviour())); }
/// <summary> /// Create a symbol given a name. At the moment assumes that only internal symbols get incremented, this means there's no masking at the moment for local variables. /// </summary> /// <param name="symbolName"></param> /// <param name="resolvedSymbolType"></param> /// <param name="declType"></param> /// <param name="appendType">Used to disable redundant type append from unnamed variable allocations</param> /// <returns></returns> private SymbolDefinition CreateNamedSymbolInternal(string symbolName, System.Type resolvedSymbolType, SymbolDeclTypeFlags declType, bool appendType = true) { if (resolvedSymbolType == null || symbolName == null) { throw new System.ArgumentNullException(); } if (!declType.HasFlag(SymbolDeclTypeFlags.Internal) && symbolName.StartsWith("__")) { throw new System.ArgumentException($"Symbol {symbolName} cannot have name starting with \"__\", this naming is reserved for internal variables."); } string uniqueSymbolName = symbolName; bool hasGlobalDeclaration = false; if (declType.HasFlag(SymbolDeclTypeFlags.Internal)) { uniqueSymbolName = $"intnl_{uniqueSymbolName}"; } if (declType.HasFlag(SymbolDeclTypeFlags.Constant)) { uniqueSymbolName = $"const_{uniqueSymbolName}"; hasGlobalDeclaration = true; } if (declType.HasFlag(SymbolDeclTypeFlags.This)) { uniqueSymbolName = $"this_{uniqueSymbolName}"; hasGlobalDeclaration = true; } if (declType.HasFlag(SymbolDeclTypeFlags.Reflection)) { uniqueSymbolName = $"__refl_{uniqueSymbolName}"; hasGlobalDeclaration = true; } if (!declType.HasFlag(SymbolDeclTypeFlags.Public) && !declType.HasFlag(SymbolDeclTypeFlags.Private) && !declType.HasFlag(SymbolDeclTypeFlags.Reflection)) { if (appendType) { string sanitizedName = resolver.SanitizeTypeName(resolvedSymbolType.Name); uniqueSymbolName += $"_{sanitizedName}"; } if (hasGlobalDeclaration) { uniqueSymbolName = $"__{IncrementGlobalNameCounter(uniqueSymbolName)}_{uniqueSymbolName}"; } else { uniqueSymbolName = $"__{IncrementUniqueNameCounter(uniqueSymbolName)}_{uniqueSymbolName}"; } } System.Type typeForName = UdonSharpUtils.UserTypeToUdonType(resolvedSymbolType); string udonTypeName = resolver.GetUdonTypeName(typeForName); if (udonTypeName == null) { throw new System.ArgumentException($"Could not locate Udon type for system type {resolvedSymbolType.FullName}"); } udonTypeName = udonTypeName.Replace("VRCUdonCommonInterfacesIUdonEventReceiver", "VRCUdonUdonBehaviour"); SymbolDefinition symbolDefinition = new SymbolDefinition(); symbolDefinition.declarationType = declType; symbolDefinition.symbolCsType = resolvedSymbolType; symbolDefinition.symbolOriginalName = symbolName; symbolDefinition.symbolResolvedTypeName = udonTypeName; symbolDefinition.symbolUniqueName = uniqueSymbolName; if (hasGlobalDeclaration) { GetGlobalSymbolTable().symbolDefinitions.Add(symbolDefinition); } else { symbolDefinitions.Add(symbolDefinition); } return(symbolDefinition); }
private int RunFieldInitalizers(CompilationModule[] compiledModules) { CompilationModule[] modulesToInitialize = compiledModules.Where(e => e.fieldsWithInitializers.Count > 0).ToArray(); // We don't need to run the costly compilation if the user hasn't defined any fields with initializers if (modulesToInitialize.Length == 0) { return(0); } int initializerErrorCount = 0; SyntaxTree[] initializerTrees = new SyntaxTree[modulesToInitialize.Length]; StringBuilder[] codeStringBuilders = new StringBuilder[modulesToInitialize.Length]; for (int moduleIdx = 0; moduleIdx < modulesToInitialize.Length; ++moduleIdx) { CompilationModule module = modulesToInitialize[moduleIdx]; CodeCompileUnit compileUnit = new CodeCompileUnit(); CodeNamespace ns = new CodeNamespace("FieldInitialzers"); compileUnit.Namespaces.Add(ns); foreach (var resolverUsingNamespace in module.resolver.usingNamespaces) { if (!string.IsNullOrEmpty(resolverUsingNamespace)) { ns.Imports.Add(new CodeNamespaceImport(resolverUsingNamespace)); } } CodeTypeDeclaration _class = new CodeTypeDeclaration($"Initializer{moduleIdx}"); ns.Types.Add(_class); CodeMemberMethod method = new CodeMemberMethod(); _class.Members.Add(method); method.Attributes = MemberAttributes.Public | MemberAttributes.Static; method.ReturnType = new CodeTypeReference(typeof(void)); method.Name = "DoInit"; method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(IUdonProgram), "program")); foreach (var fieldDeclarationSyntax in module.fieldsWithInitializers) { var type = fieldDeclarationSyntax.Declaration.Type; int count = 0; bool isConst = fieldDeclarationSyntax.Modifiers.Any(t => t.ToString() == "const"); foreach (var variable in fieldDeclarationSyntax.Declaration.Variables) { FieldDefinition fieldDef = module.compiledClassDefinition?.fieldDefinitions?.Find(e => (e.fieldSymbol.declarationType == SymbolDeclTypeFlags.Private || e.fieldSymbol.declarationType == SymbolDeclTypeFlags.Public) && e.fieldSymbol.symbolOriginalName == variable.Identifier.ToString()); string typeQualifiedName = type.ToString(); if (fieldDef != null) { if (fieldDef.fieldSymbol.symbolCsType.Namespace.Length == 0) { typeQualifiedName = fieldDef.fieldSymbol.symbolCsType.Name; } else { typeQualifiedName = fieldDef.fieldSymbol.symbolCsType.Namespace + "." + fieldDef.fieldSymbol.symbolCsType.Name; } } if (variable.Initializer != null) { string name = variable.Identifier.ToString(); if (isConst) { _class.Members.Add(new CodeSnippetTypeMember($"const {typeQualifiedName} {name} {variable.Initializer};")); } else { method.Statements.Add(new CodeSnippetStatement($"{typeQualifiedName} {name} {variable.Initializer};")); } method.Statements.Add(new CodeSnippetStatement( $"program.Heap.SetHeapVariable(program.SymbolTable.GetAddressFromSymbol(\"{variable.Identifier}\"), {name});")); count++; } } } StringBuilder sb = new StringBuilder(); codeStringBuilders[moduleIdx] = sb; CSharpCodeProvider provider = new CSharpCodeProvider(); using (StringWriter streamWriter = new StringWriter(sb)) { provider.GenerateCodeFromCompileUnit(compileUnit, streamWriter, new CodeGeneratorOptions()); } SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(sb.ToString()); initializerTrees[moduleIdx] = syntaxTree; } var assemblies = System.AppDomain.CurrentDomain.GetAssemblies(); var references = new List <MetadataReference>(); for (int i = 0; i < assemblies.Length; i++) { if (!assemblies[i].IsDynamic && assemblies[i].Location.Length > 0) { references.Add(MetadataReference.CreateFromFile(assemblies[i].Location)); } } CSharpCompilation compilation = CSharpCompilation.Create( $"init{initAssemblyCounter++}", syntaxTrees: initializerTrees, references: references, options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); using (var memoryStream = new MemoryStream()) { EmitResult result = compilation.Emit(memoryStream); if (!result.Success) { // todo: make these errors point to the correct source files bool error = false; foreach (Diagnostic diagnostic in result.Diagnostics) { if (diagnostic.Severity == DiagnosticSeverity.Error) { Debug.LogError(diagnostic); error = true; initializerErrorCount++; } } if (error) { Debug.LogError($"Generated Source code: {string.Join("\n", codeStringBuilders.Select(e => e.ToString()))}"); } } else { memoryStream.Seek(0, SeekOrigin.Begin); Assembly assembly = Assembly.Load(memoryStream.ToArray()); for (int moduleIdx = 0; moduleIdx < modulesToInitialize.Length; ++moduleIdx) { CompilationModule module = modulesToInitialize[moduleIdx]; IUdonProgram program = module.programAsset.GetRealProgram(); System.Type cls = assembly.GetType($"FieldInitialzers.Initializer{moduleIdx}"); MethodInfo methodInfo = cls.GetMethod("DoInit", BindingFlags.Public | BindingFlags.Static); methodInfo.Invoke(null, new[] { program }); foreach (var fieldDeclarationSyntax in module.fieldsWithInitializers) { foreach (var variable in fieldDeclarationSyntax.Declaration.Variables) { string varName = variable.Identifier.ToString(); object heapValue = program.Heap.GetHeapVariable(program.SymbolTable.GetAddressFromSymbol(varName)); if (heapValue != null && UdonSharpUtils.IsUserDefinedType(heapValue.GetType())) { UdonSharpUtils.LogBuildError($"Field: '{varName}' UdonSharp does not yet support field initializers on user-defined types or jagged arrays", AssetDatabase.GetAssetPath(module.programAsset.sourceCsScript), 0, 0); } } } } } } return(initializerErrorCount); }
public void Compile() { Profiler.BeginSample("UdonSharp Compile"); System.Diagnostics.Stopwatch compileTimer = new System.Diagnostics.Stopwatch(); compileTimer.Start(); int totalErrorCount = 0; try { List <ClassDefinition> classDefinitions = BuildClassDefinitions(); if (classDefinitions == null) { totalErrorCount++; } if (totalErrorCount == 0) { #if UDONSHARP_DEBUG // Single threaded compile List <CompileTaskResult> compileTasks = new List <CompileTaskResult>(); foreach (CompilationModule module in modules) { compileTasks.Add(module.Compile(classDefinitions)); } #else List <Task <CompileTaskResult> > compileTasks = new List <Task <CompileTaskResult> >(); foreach (CompilationModule module in modules) { compileTasks.Add(Task.Factory.StartNew(() => module.Compile(classDefinitions))); } #endif int totalTaskCount = compileTasks.Count; while (compileTasks.Count > 0) { #if UDONSHARP_DEBUG CompileTaskResult compileResult = compileTasks.Last(); compileTasks.RemoveAt(compileTasks.Count - 1); #else Task <CompileTaskResult> compileResultTask = Task.WhenAny(compileTasks).Result; compileTasks.Remove(compileResultTask); CompileTaskResult compileResult = compileResultTask.Result; #endif if (compileResult.compileErrors.Count == 0) { compileResult.programAsset.SetUdonAssembly(compileResult.compiledAssembly); compileResult.programAsset.AssembleCsProgram(compileResult.symbolCount); } else { foreach (CompileError error in compileResult.compileErrors) { string errorMessage = UdonSharpUtils.LogBuildError(error.errorStr, AssetDatabase.GetAssetPath(error.script).Replace("/", "\\"), error.lineIdx, error.charIdx); compileResult.programAsset.compileErrors.Add(errorMessage); } totalErrorCount += compileResult.compileErrors.Count; } int processedTaskCount = totalTaskCount - compileTasks.Count; EditorUtility.DisplayProgressBar("UdonSharp Compile", $"Compiling scripts ({processedTaskCount}/{totalTaskCount})...", Mathf.Clamp01((processedTaskCount / ((float)totalTaskCount + 1f)))); } if (totalErrorCount == 0) { EditorUtility.DisplayProgressBar("UdonSharp Compile", "Assigning constants...", 1f); int initializerErrorCount = AssignHeapConstants(); totalErrorCount += initializerErrorCount; if (initializerErrorCount == 0) { foreach (CompilationModule module in modules) { module.programAsset.ApplyProgram(); } } } } } finally { EditorUtility.ClearProgressBar(); } compileTimer.Stop(); EditorUtility.ClearProgressBar(); if (totalErrorCount == 0) { if (modules.Length > 5) { Debug.Log($"[UdonSharp] Compile of {modules.Length} scripts finished in {compileTimer.Elapsed.ToString("mm\\:ss\\.fff")}"); } else { Debug.Log($"[UdonSharp] Compile of script{(modules.Length > 1 ? "s" : "")} {string.Join(", ", modules.Select(e => Path.GetFileName(AssetDatabase.GetAssetPath(e.programAsset.sourceCsScript))))} finished in {compileTimer.Elapsed.ToString("mm\\:ss\\.fff")}"); } } Profiler.EndSample(); }
/// <summary> /// Verifies that Udon supports the given method and resolves the name used to reference it in Udon EXTERN calls /// </summary> /// <param name="externMethod"></param> /// <returns></returns> public string GetUdonMethodName(MethodBase externMethod, bool validate = true, List <System.Type> genericArguments = null) { System.Type methodSourceType = externMethod.ReflectedType; if (genericArguments != null) { if (genericArguments.Count != 1) { throw new System.ArgumentException("UdonSharp only supports 1 type generic methods at the moment"); } methodSourceType = genericArguments.First(); } methodSourceType = UdonSharpUtils.RemapBaseType(methodSourceType); bool isUdonSharpBehaviour = false; if (methodSourceType == typeof(UdonSharpBehaviour) || methodSourceType.IsSubclassOf(typeof(UdonSharpBehaviour))) { methodSourceType = typeof(VRC.Udon.UdonBehaviour); isUdonSharpBehaviour = true; } string functionNamespace = SanitizeTypeName(methodSourceType.FullName ?? methodSourceType.Namespace + methodSourceType.Name).Replace("VRCUdonUdonBehaviour", "VRCUdonCommonInterfacesIUdonEventReceiver"); string methodName = $"__{externMethod.Name.Trim('_').TrimStart('.')}"; ParameterInfo[] methodParams = externMethod.GetParameters(); if (isUdonSharpBehaviour && methodName == "__VRCInstantiate") { functionNamespace = "VRCInstantiate"; methodName = "__Instantiate"; } string paramStr = ""; if (methodParams.Length > 0) { paramStr = "_"; // Arg separator foreach (ParameterInfo parameterInfo in methodParams) { paramStr += $"_{GetUdonTypeName(parameterInfo.ParameterType, true)}"; } } else if (externMethod is ConstructorInfo) { paramStr = "__"; } string returnStr = ""; if (externMethod is MethodInfo) { returnStr = $"__{GetUdonTypeName(((MethodInfo)externMethod).ReturnType, true)}"; } else if (externMethod is ConstructorInfo) { returnStr = $"__{GetUdonTypeName(((ConstructorInfo)externMethod).DeclaringType)}"; } else { throw new System.Exception("Invalid extern method type for getting Udon name"); } string finalFunctionSig = $"{functionNamespace}.{methodName}{paramStr}{returnStr}"; if (validate && !nodeDefinitionLookup.Contains(finalFunctionSig)) { throw new System.Exception($"Method {finalFunctionSig} is not exposed in Udon"); } return(finalFunctionSig); }
public bool IsUserDefinedBehaviour() { return(UdonSharpUtils.IsUserDefinedBehaviour(internalType)); }
public bool IsUserDefinedType() { return(UdonSharpUtils.IsUserDefinedType(internalType)); }
public SymbolDefinition Invoke(SymbolDefinition[] invokeParams) { SymbolDefinition methodResult = null; System.Type resultSymbolType = null; string lookupSymbolName = null; switch (captureFunc) { case InternalFunc.TypeIDInstance: case InternalFunc.TypeIDGeneric: resultSymbolType = typeof(long); lookupSymbolName = "udonTypeID"; break; case InternalFunc.TypeNameInstance: case InternalFunc.TypeNameGeneric: resultSymbolType = typeof(string); lookupSymbolName = "udonTypeName"; break; default: throw new System.ArgumentException("Invalid internal method invocation"); } methodResult = visitorContext.topTable.CreateUnnamedSymbol(resultSymbolType, SymbolDeclTypeFlags.Internal); if (captureFunc == InternalFunc.TypeIDInstance || captureFunc == InternalFunc.TypeNameInstance) { SymbolDefinition invokeSymbol = captureScope.accessSymbol; using (ExpressionCaptureScope resultSetterScope = new ExpressionCaptureScope(visitorContext, null)) { resultSetterScope.SetToLocalSymbol(methodResult); using (ExpressionCaptureScope getInvokeScope = new ExpressionCaptureScope(visitorContext, null)) { getInvokeScope.SetToLocalSymbol(invokeSymbol); getInvokeScope.ResolveAccessToken(nameof(VRC.Udon.UdonBehaviour.GetProgramVariable)); string symbolName = visitorContext.topTable.GetReflectionSymbol(lookupSymbolName, resultSymbolType).symbolUniqueName; SymbolDefinition invokeResult = getInvokeScope.Invoke(new SymbolDefinition[] { visitorContext.topTable.CreateConstSymbol(typeof(string), symbolName) }); JumpLabel exitBranchJump = visitorContext.labelTable.GetNewJumpLabel("exitUdonTypeIdLoc"); JumpLabel falseBranchLoc = visitorContext.labelTable.GetNewJumpLabel("falseUdonTypeIdLoc"); SymbolDefinition nullCheckSymbol = null; using (ExpressionCaptureScope nullCheckCondition = new ExpressionCaptureScope(visitorContext, null)) { nullCheckCondition.SetToMethods(UdonSharpUtils.GetOperators(typeof(object), BuiltinOperatorType.Inequality)); nullCheckSymbol = nullCheckCondition.Invoke(new SymbolDefinition[] { invokeResult, visitorContext.topTable.CreateConstSymbol(typeof(object), null) }); } visitorContext.uasmBuilder.AddJumpIfFalse(falseBranchLoc, nullCheckSymbol); resultSetterScope.ExecuteSet(captureScope.CastSymbolToType(invokeResult, resultSymbolType, true)); visitorContext.uasmBuilder.AddJump(exitBranchJump); // If the value is null visitorContext.uasmBuilder.AddJumpLabel(falseBranchLoc); if (captureFunc == InternalFunc.TypeIDInstance) { resultSetterScope.ExecuteSet(visitorContext.topTable.CreateConstSymbol(typeof(long), 0L)); } else { resultSetterScope.ExecuteSet(visitorContext.topTable.CreateConstSymbol(typeof(string), "UnknownType")); } visitorContext.uasmBuilder.AddJumpLabel(exitBranchJump); } } } else { object resultSymbolValue = null; if (captureFunc == InternalFunc.TypeIDGeneric) { resultSymbolValue = Internal.UdonSharpInternalUtility.GetTypeID(genericType); } else if (captureFunc == InternalFunc.TypeNameGeneric) { resultSymbolValue = Internal.UdonSharpInternalUtility.GetTypeName(genericType); } methodResult = visitorContext.topTable.CreateConstSymbol(resultSymbolType, resultSymbolValue); } using (ExpressionCaptureScope propagateScope = new ExpressionCaptureScope(visitorContext, visitorContext.topCaptureScope)) { propagateScope.SetToLocalSymbol(methodResult); } return(methodResult); }