Added Unity project files
This commit is contained in:
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6012c815f9aee2944a15b0ecf21c44cc
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e814c25513069cc499028a77f499b5a0
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,45 @@
|
||||
|
||||
using UdonSharp;
|
||||
using UnityEditor;
|
||||
using VRC.SDKBase.Editor.BuildPipeline;
|
||||
|
||||
namespace UdonSharpEditor
|
||||
{
|
||||
internal class UdonSharpBuildChecks : IVRCSDKBuildRequestedCallback
|
||||
{
|
||||
public int callbackOrder => -1;
|
||||
|
||||
/// <summary>
|
||||
/// If you're considering commenting any section of this out, try enabling the force compile in the U# settings first.
|
||||
/// This is here to prevent you from corrupting your project files.
|
||||
/// If scripts are left uncompiled from Unity's side when uploading, there is a chance to corrupt your assemblies which can cause all of your UdonBehaviours to lose their variables if handled wrong.
|
||||
/// </summary>
|
||||
/// <param name="requestedBuildType"></param>
|
||||
/// <returns></returns>
|
||||
public bool OnBuildRequested(VRCSDKRequestedBuildType requestedBuildType)
|
||||
{
|
||||
UdonSharpSettings settings = UdonSharpSettings.GetSettings();
|
||||
bool shouldForceCompile = settings.shouldForceCompile;
|
||||
|
||||
// Unity doesn't like this and will throw errors if it ends up compiling scripts. But it seems to work.
|
||||
// This is marked experimental for now since I don't know if it will break horribly in some case.
|
||||
if (shouldForceCompile)
|
||||
{
|
||||
AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate | ImportAssetOptions.ForceSynchronousImport);
|
||||
}
|
||||
else
|
||||
{
|
||||
AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
|
||||
|
||||
if (EditorApplication.isCompiling)
|
||||
{
|
||||
UdonSharpUtils.LogWarning("Scripts are in the process of compiling, please retry build after scripts have compiled.");
|
||||
UdonSharpUtils.ShowEditorNotification("Scripts are in the process of compiling, please retry build after scripts have compiled.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 67ef2f829963ac94db0d01f19c3a1caf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,39 @@
|
||||
|
||||
using UdonSharp;
|
||||
using UdonSharp.Compiler;
|
||||
using VRC.SDKBase.Editor.BuildPipeline;
|
||||
|
||||
namespace UdonSharpEditor
|
||||
{
|
||||
internal class UdonSharpBuildCompile : IVRCSDKBuildRequestedCallback
|
||||
{
|
||||
public int callbackOrder => 100;
|
||||
|
||||
public bool OnBuildRequested(VRCSDKRequestedBuildType requestedBuildType)
|
||||
{
|
||||
if (requestedBuildType == VRCSDKRequestedBuildType.Avatar)
|
||||
return true;
|
||||
|
||||
if (UdonSharpSettings.GetSettings().disableUploadCompile)
|
||||
return true;
|
||||
|
||||
UdonSharpCompilerV1.CompileSync(new UdonSharpCompileOptions() { IsEditorBuild = false });
|
||||
UdonSharpEditorCache.SaveAllCache();
|
||||
|
||||
if (UdonSharpProgramAsset.AnyUdonSharpScriptHasError())
|
||||
{
|
||||
UdonSharpUtils.LogError("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;
|
||||
}
|
||||
|
||||
if (UdonSharpEditorManager.RunAllUpgrades())
|
||||
{
|
||||
UdonSharpUtils.LogWarning(UdonSharpEditorManager.UPGRADE_MESSAGE);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 32fdba3afe6c0a74d8714d0803e14c1a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 511bc0d84cc7ac341ba11007cab661cf
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,96 @@
|
||||
using System;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using UdonSharp.Compiler.Symbols;
|
||||
|
||||
namespace UdonSharp.Compiler
|
||||
{
|
||||
/// <summary>
|
||||
/// Base representation of a phase of compilation
|
||||
/// </summary>
|
||||
internal abstract class AbstractPhaseContext
|
||||
{
|
||||
public CompilationContext CompileContext { get; }
|
||||
public SyntaxNode CurrentNode { get; set; }
|
||||
|
||||
protected AbstractPhaseContext(CompilationContext compileContext)
|
||||
{
|
||||
CompileContext = compileContext;
|
||||
}
|
||||
|
||||
public TypeSymbol GetTypeSymbol(ITypeSymbol type)
|
||||
{
|
||||
TypeSymbol typeSymbol = CompileContext.GetTypeSymbol(type, this);
|
||||
typeSymbol = (TypeSymbol)RedirectTypeSymbol(typeSymbol);
|
||||
OnSymbolRetrieved(typeSymbol);
|
||||
|
||||
return typeSymbol;
|
||||
}
|
||||
|
||||
public TypeSymbol GetTypeSymbolWithoutRedirect(ITypeSymbol type)
|
||||
{
|
||||
TypeSymbol typeSymbol = CompileContext.GetTypeSymbol(type, this);
|
||||
OnSymbolRetrieved(typeSymbol);
|
||||
|
||||
return typeSymbol;
|
||||
}
|
||||
|
||||
public TypeSymbol GetUdonTypeSymbol(ITypeSymbol type)
|
||||
{
|
||||
TypeSymbol typeSymbol = CompileContext.GetUdonTypeSymbol(type, this);
|
||||
typeSymbol = (TypeSymbol)RedirectTypeSymbol(typeSymbol);
|
||||
OnSymbolRetrieved(typeSymbol);
|
||||
|
||||
return typeSymbol;
|
||||
}
|
||||
|
||||
public TypeSymbol GetTypeSymbol(Type systemType)
|
||||
{
|
||||
TypeSymbol typeSymbol = CompileContext.GetTypeSymbol(systemType, this);
|
||||
typeSymbol = (TypeSymbol)RedirectTypeSymbol(typeSymbol);
|
||||
OnSymbolRetrieved(typeSymbol);
|
||||
|
||||
return typeSymbol;
|
||||
}
|
||||
|
||||
public TypeSymbol GetTypeSymbol(SpecialType type)
|
||||
{
|
||||
TypeSymbol typeSymbol = CompileContext.GetTypeSymbol(type, this);
|
||||
typeSymbol = (TypeSymbol)RedirectTypeSymbol(typeSymbol);
|
||||
OnSymbolRetrieved(typeSymbol);
|
||||
|
||||
return typeSymbol;
|
||||
}
|
||||
|
||||
public Symbol GetSymbol(ISymbol sourceSymbol)
|
||||
{
|
||||
Symbol symbol = CompileContext.GetSymbol(sourceSymbol, this);
|
||||
symbol = RedirectTypeSymbol(symbol);
|
||||
symbol = RedirectParameterSymbol(symbol);
|
||||
symbol = RedirectMethodSymbol(symbol);
|
||||
OnSymbolRetrieved(symbol);
|
||||
|
||||
return symbol;
|
||||
}
|
||||
|
||||
public Symbol GetSymbolNoRedirect(ISymbol sourceSymbol)
|
||||
{
|
||||
Symbol symbol = CompileContext.GetSymbol(sourceSymbol, this);
|
||||
OnSymbolRetrieved(symbol);
|
||||
|
||||
return symbol;
|
||||
}
|
||||
|
||||
public void MarkSymbolReferenced(Symbol symbol)
|
||||
{
|
||||
OnSymbolRetrieved(symbol);
|
||||
}
|
||||
|
||||
protected virtual void OnSymbolRetrieved(Symbol symbol)
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual Symbol RedirectTypeSymbol(Symbol symbol) => symbol;
|
||||
protected virtual Symbol RedirectMethodSymbol(Symbol symbol) => symbol;
|
||||
protected virtual Symbol RedirectParameterSymbol(Symbol symbol) => symbol;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bcc0ab02ef3e9d84996c029106dfdda6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 43efb9a5e72cddc4797f9f58eabbe2ab
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,393 @@
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
using UdonSharp.Compiler.Emit;
|
||||
using UdonSharp.Compiler.Symbols;
|
||||
|
||||
namespace UdonSharp.Compiler.Assembly
|
||||
{
|
||||
internal enum InstructionKind
|
||||
{
|
||||
Nop,
|
||||
Push,
|
||||
Pop,
|
||||
Copy,
|
||||
Jump,
|
||||
JumpIfFalse,
|
||||
Extern,
|
||||
ExternSet,
|
||||
ExternGet,
|
||||
JumpIndirect,
|
||||
Return,
|
||||
Comment,
|
||||
ExportTag,
|
||||
SyncTag,
|
||||
}
|
||||
|
||||
internal abstract class AssemblyInstruction
|
||||
{
|
||||
public virtual InstructionKind GetKind() => throw new NotImplementedException();
|
||||
public virtual uint Size => throw new NotImplementedException();
|
||||
|
||||
public uint InstructionAddress { get; set; } = UInt32.MaxValue;
|
||||
|
||||
public string Comment { get; set; }
|
||||
|
||||
public abstract void WriteAssembly(StringBuilder builder);
|
||||
|
||||
protected void WriteIndentedLine(string str, StringBuilder builder, int indent = 2)
|
||||
{
|
||||
if (indent == 2)
|
||||
builder.AppendFormat(" {0}\n", str);
|
||||
else
|
||||
builder.AppendFormat("{0}{1}\n", new string(' ', indent * 4), str);
|
||||
}
|
||||
}
|
||||
|
||||
namespace Instructions
|
||||
{
|
||||
internal class NopInstruction : AssemblyInstruction
|
||||
{
|
||||
public override InstructionKind GetKind() => InstructionKind.Nop;
|
||||
public override uint Size => 4;
|
||||
public override void WriteAssembly(StringBuilder builder)
|
||||
{
|
||||
WriteIndentedLine("NOP", builder);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "NOP";
|
||||
}
|
||||
}
|
||||
|
||||
internal class Comment : AssemblyInstruction
|
||||
{
|
||||
public override InstructionKind GetKind() => InstructionKind.Comment;
|
||||
public override uint Size => 0;
|
||||
public override void WriteAssembly(StringBuilder builder)
|
||||
{
|
||||
WriteIndentedLine($"# {Comment}", builder, 0);
|
||||
}
|
||||
|
||||
public Comment(string comment)
|
||||
{
|
||||
Comment = comment;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "# " + Comment;
|
||||
}
|
||||
}
|
||||
|
||||
internal class ExportTag : AssemblyInstruction
|
||||
{
|
||||
public override InstructionKind GetKind() => InstructionKind.ExportTag;
|
||||
public override uint Size => 0;
|
||||
|
||||
public UdonSharpBehaviourMethodSymbol ExportedMethod { get; }
|
||||
|
||||
public ExportTag(UdonSharpBehaviourMethodSymbol methodSymbol)
|
||||
{
|
||||
ExportedMethod = methodSymbol;
|
||||
}
|
||||
|
||||
public override void WriteAssembly(StringBuilder builder)
|
||||
{
|
||||
WriteIndentedLine($".export {ExportedMethod.ExportedMethodAddress.AddressString}", builder, 1);
|
||||
WriteIndentedLine($"{ExportedMethod.ExportedMethodAddress.AddressString}:", builder, 1);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "export " + ExportedMethod.ExportedMethodAddress.AddressString;
|
||||
}
|
||||
}
|
||||
|
||||
internal class FieldCallbackExportTag : AssemblyInstruction
|
||||
{
|
||||
public override InstructionKind GetKind() => InstructionKind.ExportTag;
|
||||
public override uint Size => 0;
|
||||
|
||||
public UdonSharpBehaviourFieldSymbol ExportedField { get; }
|
||||
|
||||
public FieldCallbackExportTag(UdonSharpBehaviourFieldSymbol fieldSymbol)
|
||||
{
|
||||
ExportedField = fieldSymbol;
|
||||
}
|
||||
|
||||
public override void WriteAssembly(StringBuilder builder)
|
||||
{
|
||||
string fieldChangeName = $"_onVarChange_{ExportedField.Name}";
|
||||
|
||||
WriteIndentedLine($".export {fieldChangeName}", builder, 1);
|
||||
WriteIndentedLine($"{fieldChangeName}:", builder, 1);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "export field change" + ExportedField.Name;
|
||||
}
|
||||
}
|
||||
|
||||
internal class SyncTag : AssemblyInstruction
|
||||
{
|
||||
public override InstructionKind GetKind() => InstructionKind.SyncTag;
|
||||
public override uint Size => 0;
|
||||
|
||||
public Value SyncedValue { get; }
|
||||
public UdonSyncMode SyncMode { get; }
|
||||
|
||||
public SyncTag(Value syncedValue, UdonSyncMode syncMode)
|
||||
{
|
||||
SyncedValue = syncedValue;
|
||||
SyncMode = syncMode;
|
||||
}
|
||||
|
||||
public override void WriteAssembly(StringBuilder builder)
|
||||
{
|
||||
// WriteIndentedLine($".export {}");
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"sync {SyncedValue}, {SyncMode}";
|
||||
}
|
||||
}
|
||||
|
||||
internal class PushInstruction : AssemblyInstruction
|
||||
{
|
||||
public override InstructionKind GetKind() => InstructionKind.Push;
|
||||
public override uint Size => 8;
|
||||
|
||||
public Value PushValue { get; }
|
||||
|
||||
public PushInstruction(Value value)
|
||||
{
|
||||
PushValue = value;
|
||||
}
|
||||
|
||||
public override void WriteAssembly(StringBuilder builder)
|
||||
{
|
||||
WriteIndentedLine($"PUSH, {PushValue.UniqueID}", builder);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "PUSH: " + PushValue;
|
||||
}
|
||||
}
|
||||
|
||||
internal class PopInstruction : AssemblyInstruction
|
||||
{
|
||||
public override InstructionKind GetKind() => InstructionKind.Pop;
|
||||
public override uint Size => 4;
|
||||
|
||||
public override void WriteAssembly(StringBuilder builder)
|
||||
{
|
||||
WriteIndentedLine($"POP", builder);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "POP";
|
||||
}
|
||||
}
|
||||
|
||||
internal class CopyInstruction : AssemblyInstruction
|
||||
{
|
||||
public override InstructionKind GetKind() => InstructionKind.Copy;
|
||||
public override uint Size => 20;
|
||||
|
||||
public Value SourceValue { get; }
|
||||
public Value TargetValue { get; }
|
||||
|
||||
public CopyInstruction(Value sourceValue, Value targetValue)
|
||||
{
|
||||
SourceValue = sourceValue;
|
||||
TargetValue = targetValue;
|
||||
}
|
||||
|
||||
public override void WriteAssembly(StringBuilder builder)
|
||||
{
|
||||
WriteIndentedLine($"PUSH, {SourceValue.UniqueID}", builder);
|
||||
WriteIndentedLine($"PUSH, {TargetValue.UniqueID}", builder);
|
||||
WriteIndentedLine($"COPY", builder);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"COPY: {SourceValue} -> {TargetValue}";
|
||||
}
|
||||
}
|
||||
|
||||
internal class JumpInstruction : AssemblyInstruction
|
||||
{
|
||||
public override InstructionKind GetKind() => InstructionKind.Jump;
|
||||
public override uint Size => 8;
|
||||
|
||||
public JumpLabel JumpTarget { get; private set; }
|
||||
|
||||
public JumpInstruction(JumpLabel jumpTarget)
|
||||
{
|
||||
JumpTarget = jumpTarget;
|
||||
}
|
||||
|
||||
public override void WriteAssembly(StringBuilder builder)
|
||||
{
|
||||
if (JumpTarget.Address == uint.MaxValue)
|
||||
throw new InvalidOperationException($"Cannot jump to uninitialized jump label!" + (JumpTarget.DebugMethod == null ? "" : $" Target method: {JumpTarget.DebugMethod}"));
|
||||
|
||||
WriteIndentedLine($"JUMP, 0x{JumpTarget.Address:X8}", builder);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"JUMP: {JumpTarget}";
|
||||
}
|
||||
}
|
||||
|
||||
internal class JumpIfFalseInstruction : AssemblyInstruction
|
||||
{
|
||||
public override InstructionKind GetKind() => InstructionKind.JumpIfFalse;
|
||||
public override uint Size => 16;
|
||||
|
||||
public JumpLabel JumpTarget { get; }
|
||||
public Value ConditionValue { get; }
|
||||
|
||||
public JumpIfFalseInstruction(JumpLabel jumpTarget, Value conditionValue)
|
||||
{
|
||||
JumpTarget = jumpTarget;
|
||||
ConditionValue = conditionValue;
|
||||
}
|
||||
|
||||
public override void WriteAssembly(StringBuilder builder)
|
||||
{
|
||||
if (JumpTarget.Address == uint.MaxValue)
|
||||
throw new InvalidOperationException($"Cannot jump to uninitialized jump label!" + (JumpTarget.DebugMethod == null ? "" : $" Target method: {JumpTarget.DebugMethod}"));
|
||||
|
||||
WriteIndentedLine($"PUSH, {ConditionValue.UniqueID}", builder);
|
||||
WriteIndentedLine($"JUMP_IF_FALSE, 0x{JumpTarget.Address:x8}", builder);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"JUMP_IF_FALSE: target: {JumpTarget}, condition {ConditionValue}";
|
||||
}
|
||||
}
|
||||
|
||||
internal class JumpIndirectInstruction : AssemblyInstruction
|
||||
{
|
||||
public override InstructionKind GetKind() => InstructionKind.JumpIndirect;
|
||||
public override uint Size => 8;
|
||||
|
||||
public Value JumpTargetValue { get; }
|
||||
|
||||
public JumpIndirectInstruction(Value jumpTargetValue)
|
||||
{
|
||||
JumpTargetValue = jumpTargetValue;
|
||||
}
|
||||
|
||||
public override void WriteAssembly(StringBuilder builder)
|
||||
{
|
||||
WriteIndentedLine($"JUMP_INDIRECT, {JumpTargetValue.UniqueID}", builder);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"JUMP_INDIRECT: {JumpTargetValue}";
|
||||
}
|
||||
}
|
||||
|
||||
internal class ExternInstruction : AssemblyInstruction
|
||||
{
|
||||
public override InstructionKind GetKind() => InstructionKind.Extern;
|
||||
public override uint Size => 8;
|
||||
|
||||
public IExternSymbol Extern { get; }
|
||||
|
||||
public ExternInstruction(IExternSymbol @extern)
|
||||
{
|
||||
Extern = @extern;
|
||||
}
|
||||
|
||||
public override void WriteAssembly(StringBuilder builder)
|
||||
{
|
||||
WriteIndentedLine($"EXTERN, \"{Extern.ExternSignature}\"", builder);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"EXTERN: {Extern}, {Extern.ExternSignature}";
|
||||
}
|
||||
}
|
||||
|
||||
internal class ExternSetInstruction : AssemblyInstruction
|
||||
{
|
||||
public override InstructionKind GetKind() => InstructionKind.ExternSet;
|
||||
public override uint Size => 8;
|
||||
|
||||
public IExternAccessor Extern { get; }
|
||||
|
||||
public ExternSetInstruction(IExternAccessor externSet)
|
||||
{
|
||||
Extern = externSet;
|
||||
}
|
||||
|
||||
public override void WriteAssembly(StringBuilder builder)
|
||||
{
|
||||
WriteIndentedLine($"EXTERN, \"{Extern.ExternSetSignature}\"", builder);
|
||||
}
|
||||
}
|
||||
|
||||
internal class ExternGetInstruction : AssemblyInstruction
|
||||
{
|
||||
public override InstructionKind GetKind() => InstructionKind.ExternGet;
|
||||
public override uint Size => 8;
|
||||
|
||||
public IExternAccessor Extern { get; }
|
||||
|
||||
public ExternGetInstruction(IExternAccessor externGet)
|
||||
{
|
||||
Extern = externGet;
|
||||
}
|
||||
|
||||
public override void WriteAssembly(StringBuilder builder)
|
||||
{
|
||||
WriteIndentedLine($"EXTERN, \"{Extern.ExternGetSignature}\"", builder);
|
||||
}
|
||||
}
|
||||
|
||||
internal class RetInstruction : AssemblyInstruction
|
||||
{
|
||||
public override InstructionKind GetKind() => InstructionKind.Return;
|
||||
public override uint Size => 20; // Push, Copy, JumpIndirect
|
||||
// public override uint Size => 12; // Temp force exit jump
|
||||
|
||||
public Value RetValRef { get; }
|
||||
|
||||
public RetInstruction(Value retVal)
|
||||
{
|
||||
RetValRef = retVal;
|
||||
}
|
||||
|
||||
public override void WriteAssembly(StringBuilder builder)
|
||||
{
|
||||
// WriteIndentedLine("POP", builder);
|
||||
// WriteIndentedLine("JUMP, 0xFFFFFFF0", builder);
|
||||
|
||||
WriteIndentedLine($"PUSH, {RetValRef.UniqueID}", builder);
|
||||
WriteIndentedLine("COPY", builder);
|
||||
WriteIndentedLine($"JUMP_INDIRECT, {RetValRef.UniqueID}", builder);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "RET";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 08d479294d790bf48b1acd11f27985c7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,256 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using UdonSharp.Compiler.Assembly.Instructions;
|
||||
using UdonSharp.Compiler.Emit;
|
||||
using UdonSharp.Compiler.Symbols;
|
||||
|
||||
namespace UdonSharp.Compiler.Assembly
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains the assembly for a given module of compilation.
|
||||
/// A module in this context is all code and associated heap values that will be compiled into a single program asset.
|
||||
/// This includes primary events and methods on a given UdonBehaviour, along with all imported methods for referenced types.
|
||||
/// </summary>
|
||||
internal class AssemblyModule
|
||||
{
|
||||
public CompilationContext CompileContext { get; }
|
||||
|
||||
private List<AssemblyInstruction> _instructions = new List<AssemblyInstruction>();
|
||||
private List<JumpLabel> _jumpLabels = new List<JumpLabel>();
|
||||
private uint _currentAddress;
|
||||
|
||||
public uint CurrentAddress => _currentAddress;
|
||||
|
||||
public int ExecutionOrder { get; set; }
|
||||
|
||||
public ValueTable RootTable { get; }
|
||||
|
||||
public Dictionary<Symbol, ExportAddress> Exports { get; } = new Dictionary<Symbol, ExportAddress>();
|
||||
|
||||
public AssemblyModule(CompilationContext context)
|
||||
{
|
||||
CompileContext = context;
|
||||
RootTable = new ValueTable(this, null);
|
||||
}
|
||||
|
||||
public AssemblyInstruction this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (index < 0 || index >= _instructions.Count)
|
||||
throw new IndexOutOfRangeException("Instruction index is not valid");
|
||||
|
||||
return _instructions[index];
|
||||
}
|
||||
}
|
||||
|
||||
public int InstructionCount => _instructions.Count;
|
||||
|
||||
#region Instruction Adds
|
||||
private void AddInstruction(AssemblyInstruction instruction)
|
||||
{
|
||||
_instructions.Add(instruction);
|
||||
instruction.InstructionAddress = _currentAddress;
|
||||
_currentAddress += instruction.Size;
|
||||
}
|
||||
|
||||
public void AddNop()
|
||||
{
|
||||
AddInstruction(new NopInstruction());
|
||||
}
|
||||
|
||||
public void AddCommentTag(string comment)
|
||||
{
|
||||
AddInstruction(new Comment(comment));
|
||||
}
|
||||
|
||||
public void AddExportTag(UdonSharpBehaviourMethodSymbol exportMethod)
|
||||
{
|
||||
AddInstruction(new ExportTag(exportMethod));
|
||||
}
|
||||
|
||||
public void AddFieldChangeExportTag(UdonSharpBehaviourFieldSymbol fieldSymbol)
|
||||
{
|
||||
AddInstruction(new FieldCallbackExportTag(fieldSymbol));
|
||||
}
|
||||
|
||||
public void AddSyncTag(Value syncValue, UdonSyncMode syncMode)
|
||||
{
|
||||
AddInstruction(new SyncTag(syncValue, syncMode));
|
||||
}
|
||||
|
||||
public void AddPush(Value pushValue)
|
||||
{
|
||||
if (pushValue == null)
|
||||
throw new ArgumentNullException(nameof(pushValue));
|
||||
|
||||
AddInstruction(new PushInstruction(pushValue));
|
||||
}
|
||||
|
||||
public void AddPop()
|
||||
{
|
||||
AddInstruction(new PopInstruction());
|
||||
}
|
||||
|
||||
public void AddCopy(Value sourceValue, Value targetValue)
|
||||
{
|
||||
if (targetValue.IsConstant)
|
||||
throw new ArgumentException("Cannot copy to const value");
|
||||
|
||||
AddInstruction(new CopyInstruction(sourceValue, targetValue));
|
||||
}
|
||||
|
||||
public void AddJump(JumpLabel jumpTarget)
|
||||
{
|
||||
AddInstruction(new JumpInstruction(jumpTarget));
|
||||
}
|
||||
|
||||
public void AddJumpIfFalse(JumpLabel jumpTarget, Value conditionValue)
|
||||
{
|
||||
AddInstruction(new JumpIfFalseInstruction(jumpTarget, conditionValue));
|
||||
}
|
||||
|
||||
public void AddJumpIndrect(Value targetJumpValue)
|
||||
{
|
||||
AddInstruction(new JumpIndirectInstruction(targetJumpValue));
|
||||
}
|
||||
|
||||
public void AddExtern(IExternSymbol method)
|
||||
{
|
||||
AddInstruction(new ExternInstruction(method));
|
||||
}
|
||||
|
||||
public void AddExternSet(IExternAccessor externAccessor)
|
||||
{
|
||||
AddInstruction(new ExternSetInstruction(externAccessor));
|
||||
}
|
||||
|
||||
public void AddExternGet(IExternAccessor externAccessor)
|
||||
{
|
||||
AddInstruction(new ExternGetInstruction(externAccessor));
|
||||
}
|
||||
|
||||
public void AddReturn(Value returnVal)
|
||||
{
|
||||
AddInstruction(new RetInstruction(returnVal));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Jump Labels
|
||||
|
||||
public JumpLabel CreateLabel()
|
||||
{
|
||||
JumpLabel newLabel = new JumpLabel();
|
||||
|
||||
_jumpLabels.Add(newLabel);
|
||||
|
||||
return newLabel;
|
||||
}
|
||||
|
||||
public void LabelJump(JumpLabel label)
|
||||
{
|
||||
if (label.Address != uint.MaxValue)
|
||||
throw new ArgumentException("Label has already been set");
|
||||
|
||||
label.Address = _currentAddress;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Address Allocation
|
||||
|
||||
public void AddAddress(ExportAddress address)
|
||||
{
|
||||
Exports.Add(address.AddressSymbol, address);
|
||||
}
|
||||
#endregion
|
||||
|
||||
private void BuildDataBlock(StringBuilder builder)
|
||||
{
|
||||
List<Value> allValues = RootTable.GetAllUniqueChildValues();
|
||||
|
||||
builder.Append(".data_start\n");
|
||||
|
||||
foreach (Value value in allValues)
|
||||
{
|
||||
if (value.AssociatedSymbol is FieldSymbol fieldSymbol)
|
||||
{
|
||||
if (fieldSymbol.IsSerialized)
|
||||
builder.AppendFormat(" .export {0}\n", value.UniqueID);
|
||||
|
||||
UdonSyncMode? syncMode = fieldSymbol.SyncMode;
|
||||
|
||||
switch (syncMode)
|
||||
{
|
||||
case UdonSyncMode.None:
|
||||
builder.AppendFormat(" .sync {0}, none\n", value.UniqueID);
|
||||
break;
|
||||
case UdonSyncMode.Linear:
|
||||
builder.AppendFormat(" .sync {0}, linear\n", value.UniqueID);
|
||||
break;
|
||||
case UdonSyncMode.Smooth:
|
||||
builder.AppendFormat(" .sync {0}, smooth\n", value.UniqueID);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Value value in allValues)
|
||||
{
|
||||
builder.AppendFormat(" {0}\n", value.GetDeclarationStr());
|
||||
}
|
||||
|
||||
builder.Append(".data_end\n");
|
||||
}
|
||||
|
||||
private void BuildInstructionUasm(StringBuilder builder)
|
||||
{
|
||||
builder.Append(".code_start\n");
|
||||
|
||||
if (ExecutionOrder != 0)
|
||||
builder.Append($".update_order {ExecutionOrder}\n");
|
||||
|
||||
foreach (var instruction in _instructions)
|
||||
instruction.WriteAssembly(builder);
|
||||
|
||||
builder.Append(".code_end\n");
|
||||
}
|
||||
|
||||
public string BuildUasmStr()
|
||||
{
|
||||
StringBuilder uasmBuilder = new StringBuilder();
|
||||
|
||||
BuildDataBlock(uasmBuilder);
|
||||
BuildInstructionUasm(uasmBuilder);
|
||||
|
||||
return uasmBuilder.ToString();
|
||||
}
|
||||
|
||||
public uint GetHeapSize()
|
||||
{
|
||||
int heapValCount = RootTable.GetAllUniqueChildValues().Count;
|
||||
|
||||
HashSet<string> uniqueExternCount = new HashSet<string>();
|
||||
|
||||
foreach (AssemblyInstruction instruction in _instructions)
|
||||
{
|
||||
switch (instruction)
|
||||
{
|
||||
case ExternInstruction externMethodSymbol:
|
||||
uniqueExternCount.Add(externMethodSymbol.Extern.ExternSignature);
|
||||
break;
|
||||
case ExternSetInstruction externSetInstruction:
|
||||
uniqueExternCount.Add(externSetInstruction.Extern.ExternSetSignature);
|
||||
break;
|
||||
case ExternGetInstruction externGetInstruction:
|
||||
uniqueExternCount.Add(externGetInstruction.Extern.ExternGetSignature);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (uint)(heapValCount + uniqueExternCount.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cbaa361a12f19c941bdb7989992f4454
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,91 @@
|
||||
|
||||
using JetBrains.Annotations;
|
||||
using UdonSharp.Compiler.Symbols;
|
||||
|
||||
namespace UdonSharp.Compiler.Assembly
|
||||
{
|
||||
internal class ExportAddress
|
||||
{
|
||||
public enum AddressKind
|
||||
{
|
||||
String,
|
||||
Uint,
|
||||
}
|
||||
|
||||
public AddressKind Kind { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The symbol this export belongs to
|
||||
/// </summary>
|
||||
public Symbol AddressSymbol { get; }
|
||||
|
||||
public string AddressString { get; private set; }
|
||||
|
||||
public uint AddressUint { get; private set; }
|
||||
|
||||
public bool IsResolved { get; private set; }
|
||||
|
||||
// public ExportAddress(string address, [NotNull] Symbol symbol)
|
||||
// {
|
||||
// Kind = AddressKind.String;
|
||||
// AddressString = address;
|
||||
// AddressSymbol = symbol;
|
||||
// IsResolved = true;
|
||||
// }
|
||||
//
|
||||
// public ExportAddress(uint address, [NotNull] Symbol symbol)
|
||||
// {
|
||||
// Kind = AddressKind.Uint;
|
||||
// AddressUint = address;
|
||||
// AddressSymbol = symbol;
|
||||
// IsResolved = true;
|
||||
// }
|
||||
|
||||
public ExportAddress(AddressKind kind, [NotNull] Symbol symbol)
|
||||
{
|
||||
Kind = kind;
|
||||
AddressSymbol = symbol;
|
||||
}
|
||||
|
||||
public void ResolveAddress(uint address)
|
||||
{
|
||||
IsResolved = true;
|
||||
AddressUint = address;
|
||||
}
|
||||
|
||||
public void ResolveAddress(string address)
|
||||
{
|
||||
IsResolved = true;
|
||||
AddressString = address;
|
||||
}
|
||||
|
||||
protected bool Equals(ExportAddress other)
|
||||
{
|
||||
return Equals(AddressSymbol, other.AddressSymbol) && AddressString == other.AddressString && AddressUint == other.AddressUint;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((ExportAddress) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
var hashCode = (AddressSymbol != null ? AddressSymbol.GetHashCode() : 0);
|
||||
hashCode = (hashCode * 397) ^ (AddressString != null ? AddressString.GetHashCode() : 0);
|
||||
hashCode = (hashCode * 397) ^ (int) AddressUint;
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"address: {(Kind == AddressKind.String ? AddressString : AddressUint.ToString())}, symbol: {AddressSymbol}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c3e167753e866c64cbdd3c5c2dd4f079
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,12 @@
|
||||
|
||||
using UdonSharp.Compiler.Symbols;
|
||||
|
||||
namespace UdonSharp.Compiler.Assembly
|
||||
{
|
||||
internal class JumpLabel
|
||||
{
|
||||
public MethodSymbol DebugMethod { get; set; }
|
||||
|
||||
public uint Address { get; set; } = uint.MaxValue;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 105086e0adf1f75488bd27992509f552
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9f8e17574f242e045b3723a56bd7ecf2
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,190 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using UdonSharp.Compiler.Symbols;
|
||||
|
||||
namespace UdonSharp.Compiler.Binder
|
||||
{
|
||||
/// <summary>
|
||||
/// The context for the binding phase, this is local to a single type that is being bound
|
||||
/// </summary>
|
||||
internal class BindContext : AbstractPhaseContext
|
||||
{
|
||||
private ITypeSymbol BindSymbol { get; }
|
||||
private IEnumerable<Symbol> TypeSymbolsToBind { get; }
|
||||
|
||||
private Symbol _currentBindSymbol;
|
||||
public MethodSymbol CurrentBindMethod => _currentBindSymbol as MethodSymbol;
|
||||
|
||||
private HashSet<Symbol> _currentReferencedSymbols;
|
||||
|
||||
public BindContext(CompilationContext context, ITypeSymbol bindSymbol, IEnumerable<Symbol> referencedTypeSymbolsToBind)
|
||||
:base(context)
|
||||
{
|
||||
BindSymbol = bindSymbol;
|
||||
TypeSymbolsToBind = referencedTypeSymbolsToBind;
|
||||
}
|
||||
|
||||
public void Bind()
|
||||
{
|
||||
TypeSymbol containingType = GetTypeSymbol(BindSymbol);
|
||||
|
||||
if (!containingType.IsBound)
|
||||
containingType.Bind(this);
|
||||
|
||||
foreach (Symbol symbol in TypeSymbolsToBind)
|
||||
{
|
||||
if (symbol.IsBound)
|
||||
continue;
|
||||
|
||||
using (OpenMemberBindScope(symbol))
|
||||
symbol.Bind(this);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnSymbolRetrieved(Symbol symbol)
|
||||
{
|
||||
if (_currentBindSymbol != null && !symbol.IsExtern)
|
||||
_currentReferencedSymbols.Add(symbol);
|
||||
}
|
||||
|
||||
private TypeSymbol MakeArraySymbol(TypeSymbol element, int depth)
|
||||
{
|
||||
if (element is TypeParameterSymbol)
|
||||
return element;
|
||||
|
||||
TypeSymbol currentSymbol = element;
|
||||
|
||||
while (depth-- > 0)
|
||||
currentSymbol = currentSymbol.MakeArrayType(this);
|
||||
|
||||
return currentSymbol;
|
||||
}
|
||||
|
||||
protected override Symbol RedirectTypeSymbol(Symbol symbol)
|
||||
{
|
||||
if (_currentBindSymbol == null || !(symbol is TypeParameterSymbol))
|
||||
return symbol;
|
||||
|
||||
TypeSymbol parameterSymbol = (TypeSymbol)symbol;
|
||||
|
||||
int typeIdx = -1;
|
||||
int arrayDepth = 0;
|
||||
|
||||
while (parameterSymbol.IsArray)
|
||||
{
|
||||
arrayDepth++;
|
||||
parameterSymbol = parameterSymbol.ElementType;
|
||||
}
|
||||
|
||||
if (arrayDepth > 0)
|
||||
return MakeArraySymbol((TypeSymbol)RedirectTypeSymbol(parameterSymbol), arrayDepth);
|
||||
|
||||
if (_currentBindSymbol is MethodSymbol currentMethodSymbol)
|
||||
{
|
||||
if (currentMethodSymbol.TypeArguments.Length > 0 && currentMethodSymbol.OriginalSymbol != null)
|
||||
{
|
||||
MethodSymbol originalMethod = (MethodSymbol)currentMethodSymbol.OriginalSymbol;
|
||||
|
||||
for (int i = 0; i < originalMethod.TypeArguments.Length; ++i)
|
||||
{
|
||||
if (!parameterSymbol.Equals(originalMethod.TypeArguments[i])) continue;
|
||||
typeIdx = i;
|
||||
break;
|
||||
}
|
||||
|
||||
if (typeIdx != -1)
|
||||
return currentMethodSymbol.TypeArguments[typeIdx];
|
||||
}
|
||||
}
|
||||
|
||||
TypeSymbol containingType = _currentBindSymbol.ContainingType;
|
||||
|
||||
if (containingType.TypeArguments.Length > 0 && containingType.OriginalSymbol != null)
|
||||
{
|
||||
TypeSymbol originalType = (TypeSymbol)containingType.OriginalSymbol;
|
||||
|
||||
for (int i = 0; i < originalType.TypeArguments.Length; ++i)
|
||||
{
|
||||
if (!parameterSymbol.Equals(originalType.TypeArguments[i])) continue;
|
||||
typeIdx = i;
|
||||
break;
|
||||
}
|
||||
|
||||
if (typeIdx != -1)
|
||||
return containingType.TypeArguments[typeIdx];
|
||||
}
|
||||
|
||||
return symbol;
|
||||
}
|
||||
|
||||
protected override Symbol RedirectParameterSymbol(Symbol symbol)
|
||||
{
|
||||
if (_currentBindSymbol == null || !(symbol is ParameterSymbol))
|
||||
return symbol;
|
||||
|
||||
if (_currentBindSymbol is MethodSymbol methodSymbol && methodSymbol.RoslynSymbol != methodSymbol.RoslynSymbol.OriginalDefinition)
|
||||
{
|
||||
foreach (ParameterSymbol methodParam in methodSymbol.Parameters)
|
||||
{
|
||||
if (methodParam.OriginalSymbol.Equals(symbol))
|
||||
return methodParam;
|
||||
}
|
||||
}
|
||||
|
||||
return symbol;
|
||||
}
|
||||
|
||||
protected override Symbol RedirectMethodSymbol(Symbol symbol)
|
||||
{
|
||||
if (symbol is MethodSymbol methodSymbol
|
||||
&& methodSymbol.IsGenericMethod
|
||||
&& methodSymbol.IsUntypedGenericMethod)
|
||||
{
|
||||
TypeSymbol[] typeSymbols = methodSymbol.TypeArguments.Select(e => (TypeSymbol)RedirectTypeSymbol(e)).ToArray();
|
||||
|
||||
if (!typeSymbols.Any(e => e is TypeParameterSymbol))
|
||||
return methodSymbol.ConstructGenericMethod(this, typeSymbols);
|
||||
}
|
||||
|
||||
return symbol;
|
||||
}
|
||||
|
||||
public IDisposable OpenMemberBindScope(Symbol boundSymbol)
|
||||
{
|
||||
return new MemberBindScope(this, boundSymbol);
|
||||
}
|
||||
|
||||
private void FinishSymbolBind()
|
||||
{
|
||||
_currentBindSymbol.SetDependencies(_currentReferencedSymbols);
|
||||
_currentBindSymbol = null;
|
||||
_currentReferencedSymbols = null;
|
||||
}
|
||||
|
||||
public TypeSymbol GetCurrentReturnType()
|
||||
{
|
||||
return (_currentBindSymbol as MethodSymbol)?.ReturnType;
|
||||
}
|
||||
|
||||
private class MemberBindScope : IDisposable
|
||||
{
|
||||
private BindContext context;
|
||||
|
||||
public MemberBindScope(BindContext context, Symbol bindSymbol)
|
||||
{
|
||||
this.context = context;
|
||||
context._currentBindSymbol = bindSymbol;
|
||||
context._currentReferencedSymbols = new HashSet<Symbol>();
|
||||
context.CurrentNode = context._currentBindSymbol.RoslynSymbol.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
context.FinishSymbolBind();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 28ac79db21e90a34bb90a4c981fcb1e4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f116f846e181258419efe1e77984f5e8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d72e1dfd17efea749b583c237a21c2d2
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,222 @@
|
||||
|
||||
using System;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using UdonSharp.Compiler.Emit;
|
||||
using UdonSharp.Compiler.Symbols;
|
||||
using NotSupportedException = UdonSharp.Core.NotSupportedException;
|
||||
|
||||
namespace UdonSharp.Compiler.Binder
|
||||
{
|
||||
internal abstract class BoundAccessExpression : BoundExpression
|
||||
{
|
||||
protected BoundAccessExpression(SyntaxNode node, BoundExpression sourceExpression)
|
||||
: base(node, sourceExpression)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void MarkForcedBaseCall() {}
|
||||
|
||||
/// <summary>
|
||||
/// Emits a set operation on this access
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="valueExpression"></param>
|
||||
/// <returns>Returns the evaluated value of the valueExpression. This is primarily to handle chained assignment expressions correctly.</returns>
|
||||
public abstract Value EmitSet(EmitContext context, BoundExpression valueExpression);
|
||||
|
||||
public static BoundAccessExpression BindAccess(AbstractPhaseContext context, SyntaxNode node, Symbol accessSymbol, BoundExpression symbolExpressionSource)
|
||||
{
|
||||
if (node.Kind() == SyntaxKind.ThisExpression)
|
||||
return new BoundThisAccessExpression(node, ((ParameterSymbol)accessSymbol).Type, symbolExpressionSource);
|
||||
|
||||
switch (accessSymbol)
|
||||
{
|
||||
case LocalSymbol localSymbol:
|
||||
return BoundLocalAccessExpression.BindLocalAccess(node, localSymbol);
|
||||
case ParameterSymbol parameterSymbol:
|
||||
return new BoundParameterAccessExpression(node, parameterSymbol);
|
||||
case FieldSymbol fieldSymbol:
|
||||
return BoundFieldAccessExpression.BindFieldAccess(context, node, fieldSymbol, symbolExpressionSource);
|
||||
case PropertySymbol propertySymbol:
|
||||
return BoundPropertyAccessExpression.BindPropertyAccess(context, node, propertySymbol, symbolExpressionSource);
|
||||
}
|
||||
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public static BoundAccessExpression BindThisAccess(TypeSymbol typeSymbol)
|
||||
{
|
||||
return new BoundThisAccessExpression(null, typeSymbol, null);
|
||||
}
|
||||
|
||||
public static BoundAccessExpression BindAccess(Value value)
|
||||
{
|
||||
return new BoundValueAccessExpression(value);
|
||||
}
|
||||
|
||||
public static BoundAccessExpression BindAccess(Value.CowValue value)
|
||||
{
|
||||
return new BoundCowValueAccessExpression(value);
|
||||
}
|
||||
|
||||
public static BoundAccessExpression BindElementAccess(AbstractPhaseContext context, SyntaxNode node,
|
||||
BoundExpression symbolExpressionSource, BoundExpression[] indexers)
|
||||
{
|
||||
return new BoundArrayAccessExpression(node, context, symbolExpressionSource, indexers);
|
||||
}
|
||||
|
||||
public static BoundAccessExpression BindElementAccess(AbstractPhaseContext context, SyntaxNode node, PropertySymbol accessSymbol,
|
||||
BoundExpression symbolExpressionSource, BoundExpression[] indexers)
|
||||
{
|
||||
return BoundPropertyAccessExpression.BindPropertyAccess(context, node, accessSymbol,
|
||||
symbolExpressionSource, indexers);
|
||||
}
|
||||
|
||||
private sealed class BoundLocalAccessExpression : BoundAccessExpression
|
||||
{
|
||||
private LocalSymbol AccessSymbol { get; }
|
||||
|
||||
public override TypeSymbol ValueType => AccessSymbol.Type;
|
||||
|
||||
public BoundLocalAccessExpression(SyntaxNode node, LocalSymbol accessSymbol)
|
||||
: base(node, null)
|
||||
{
|
||||
AccessSymbol = accessSymbol;
|
||||
}
|
||||
|
||||
public static BoundAccessExpression BindLocalAccess(SyntaxNode node, LocalSymbol localSymbol)
|
||||
{
|
||||
if (localSymbol.IsConst)
|
||||
return new BoundConstantExpression(localSymbol.ConstantValue, localSymbol.Type);
|
||||
|
||||
return new BoundLocalAccessExpression(node, localSymbol);
|
||||
}
|
||||
|
||||
public override Value EmitValue(EmitContext context)
|
||||
{
|
||||
if (AccessSymbol.IsConst)
|
||||
return context.GetConstantValue(ValueType, AccessSymbol.ConstantValue);
|
||||
|
||||
return context.GetUserValue(AccessSymbol);
|
||||
}
|
||||
|
||||
public override Value EmitSet(EmitContext context, BoundExpression valueExpression)
|
||||
{
|
||||
return context.EmitValueAssignment(context.GetUserValue(AccessSymbol), valueExpression);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class BoundParameterAccessExpression : BoundAccessExpression
|
||||
{
|
||||
private ParameterSymbol AccessSymbol { get; }
|
||||
|
||||
public override TypeSymbol ValueType => AccessSymbol.Type;
|
||||
|
||||
public BoundParameterAccessExpression(SyntaxNode node, ParameterSymbol accessSymbol)
|
||||
: base(node, null)
|
||||
{
|
||||
AccessSymbol = accessSymbol;
|
||||
}
|
||||
|
||||
private Value GetParamValue(EmitContext context)
|
||||
{
|
||||
MethodSymbol containingMethod = AccessSymbol.ContainingSymbol;
|
||||
EmitContext.MethodLinkage linkage = context.GetMethodLinkage(containingMethod, false);
|
||||
|
||||
int paramIdx = -1;
|
||||
for (int i = 0; i < containingMethod.Parameters.Length; ++i)
|
||||
{
|
||||
if (containingMethod.Parameters[i] == AccessSymbol)
|
||||
{
|
||||
paramIdx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return linkage.ParameterValues[paramIdx];
|
||||
}
|
||||
|
||||
public override Value EmitValue(EmitContext context)
|
||||
{
|
||||
return GetParamValue(context);
|
||||
}
|
||||
|
||||
public override Value EmitSet(EmitContext context, BoundExpression valueExpression)
|
||||
{
|
||||
return context.EmitValueAssignment(GetParamValue(context), valueExpression);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class BoundThisAccessExpression : BoundAccessExpression
|
||||
{
|
||||
public BoundThisAccessExpression(SyntaxNode node, TypeSymbol thisType, BoundExpression sourceExpression) : base(node, sourceExpression)
|
||||
{
|
||||
ValueType = thisType;
|
||||
IsThis = true;
|
||||
}
|
||||
|
||||
public override TypeSymbol ValueType { get; }
|
||||
|
||||
public override Value EmitValue(EmitContext context)
|
||||
{
|
||||
if (!((INamedTypeSymbol) ValueType.RoslynSymbol).IsUdonSharpBehaviour())
|
||||
throw new NotImplementedException("Non udonsharp behaviour `this` is not yet implemented");
|
||||
|
||||
return context.GetUdonThisValue(ValueType);
|
||||
}
|
||||
|
||||
public override Value EmitSet(EmitContext context, BoundExpression valueExpression)
|
||||
{
|
||||
throw new NotSupportedException("Cannot set `this` reference", SyntaxNode.GetLocation());
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class BoundValueAccessExpression : BoundAccessExpression
|
||||
{
|
||||
private Value AccessValue { get; }
|
||||
|
||||
public override TypeSymbol ValueType => AccessValue.UserType;
|
||||
|
||||
public BoundValueAccessExpression(Value value)
|
||||
: base(null, null)
|
||||
{
|
||||
AccessValue = value;
|
||||
}
|
||||
|
||||
public override Value EmitValue(EmitContext context)
|
||||
{
|
||||
return AccessValue;
|
||||
}
|
||||
|
||||
public override Value EmitSet(EmitContext context, BoundExpression valueExpression)
|
||||
{
|
||||
return context.EmitValueAssignment(AccessValue, valueExpression);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class BoundCowValueAccessExpression : BoundAccessExpression
|
||||
{
|
||||
private Value.CowValue AccessValue { get; }
|
||||
|
||||
public override TypeSymbol ValueType { get; }
|
||||
|
||||
public BoundCowValueAccessExpression(Value.CowValue value)
|
||||
: base(null, null)
|
||||
{
|
||||
AccessValue = value;
|
||||
ValueType = value.Value.UserType;
|
||||
}
|
||||
|
||||
public override Value EmitValue(EmitContext context)
|
||||
{
|
||||
return AccessValue.Value;
|
||||
}
|
||||
|
||||
public override Value EmitSet(EmitContext context, BoundExpression valueExpression)
|
||||
{
|
||||
return context.EmitValueAssignment(AccessValue.Value, valueExpression);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fe318b910c0dee1499132005ed0ebdc9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,42 @@
|
||||
|
||||
using System;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using UdonSharp.Compiler.Symbols;
|
||||
using UdonSharp.Compiler.Udon;
|
||||
|
||||
namespace UdonSharp.Compiler.Binder
|
||||
{
|
||||
internal sealed class BoundArrayAccessExpression : BoundPropertyAccessExpression
|
||||
{
|
||||
public BoundArrayAccessExpression(SyntaxNode node, AbstractPhaseContext context, BoundExpression sourceExpression, BoundExpression[] indexerExpressions)
|
||||
: base(context, node, BuildProperty(context, sourceExpression), sourceExpression, indexerExpressions)
|
||||
{
|
||||
}
|
||||
|
||||
public override TypeSymbol ValueType => SourceExpression.ValueType.ElementType;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 988f5122454f3cf46b09b98948019ff4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,64 @@
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
using UdonSharp.Compiler.Emit;
|
||||
using UdonSharp.Compiler.Symbols;
|
||||
|
||||
namespace UdonSharp.Compiler.Binder
|
||||
{
|
||||
internal class BoundArrayCreationExpression : BoundExternInvocation
|
||||
{
|
||||
private class ArrayConstructorExtern : ExternMethodSymbol
|
||||
{
|
||||
public override string ExternSignature { get; }
|
||||
public override bool IsStatic => true;
|
||||
|
||||
public override string Name => ExternSignature;
|
||||
|
||||
public ArrayConstructorExtern(AbstractPhaseContext context, TypeSymbol arrayType)
|
||||
: base(null, context)
|
||||
{
|
||||
ExternSignature = $"{arrayType.UdonType.ExternSignature}.__ctor__SystemInt32__{arrayType.UdonType.ExternSignature}";
|
||||
ReturnType = arrayType;
|
||||
}
|
||||
}
|
||||
|
||||
private TypeSymbol ArrayType { get; }
|
||||
|
||||
private BoundExpression[] Initializers { get; }
|
||||
|
||||
public BoundArrayCreationExpression(SyntaxNode node, AbstractPhaseContext context, TypeSymbol arrayType, BoundExpression[] rankSizes, BoundExpression[] initializers)
|
||||
: base(node, context, new ArrayConstructorExtern(context, arrayType), null, rankSizes)
|
||||
{
|
||||
ArrayType = arrayType;
|
||||
Initializers = initializers;
|
||||
}
|
||||
|
||||
public override TypeSymbol ValueType => ArrayType;
|
||||
|
||||
public override Value EmitValue(EmitContext context)
|
||||
{
|
||||
Value returnValue = base.EmitValue(context);
|
||||
|
||||
if (Initializers != null)
|
||||
{
|
||||
BoundAccessExpression arrayAccess = BoundAccessExpression.BindAccess(returnValue);
|
||||
TypeSymbol intType = context.GetTypeSymbol(SpecialType.System_Int32);
|
||||
|
||||
using (context.InterruptAssignmentScope())
|
||||
{
|
||||
// This is quite wasteful for allocations in the compile, todo: look at caching these safely
|
||||
for (int i = 0; i < Initializers.Length; ++i)
|
||||
{
|
||||
BoundAccessExpression elementAccess = BoundAccessExpression.BindElementAccess(context,
|
||||
SyntaxNode, arrayAccess,
|
||||
new BoundExpression[]
|
||||
{new BoundConstantExpression(new ConstantValue<int>(i), intType, SyntaxNode)});
|
||||
context.EmitSet(elementAccess, Initializers[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d64aae3d727820c49b7d935b62c0df61
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,43 @@
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
using UdonSharp.Compiler.Emit;
|
||||
using UdonSharp.Compiler.Symbols;
|
||||
using UdonSharp.Compiler.Udon;
|
||||
|
||||
namespace UdonSharp.Compiler.Binder
|
||||
{
|
||||
internal sealed class BoundAssignmentExpression : BoundExpression
|
||||
{
|
||||
private BoundAccessExpression TargetExpression { get; }
|
||||
|
||||
public override TypeSymbol ValueType => SourceExpression.ValueType;
|
||||
|
||||
public BoundAssignmentExpression(SyntaxNode node, AbstractPhaseContext context, Symbol symbol,
|
||||
BoundAccessExpression assignmentTarget, BoundExpression instanceExpression, BoundExpression assignmentSource)
|
||||
: base(node, assignmentSource)
|
||||
{
|
||||
if (symbol is ExternFieldSymbol externFieldSymbol)
|
||||
{
|
||||
// This is assignment, so check for set only
|
||||
// Passing originalSymbol.RoslynSymbol to synthesized fields to inherit attributes (static, const, etc.)
|
||||
TypeSymbol assignmentTargetTypeSymbol = assignmentTarget.ValueType;
|
||||
ExternFieldSymbol externAlternateFieldSymbol = SetupExternAccessor(node, context, externFieldSymbol, instanceExpression, CompilerUdonInterface.FieldAccessorType.Set,
|
||||
(candidate) => new ExternSynthesizedFieldSymbol(externFieldSymbol.RoslynSymbol, context, externFieldSymbol.Name, candidate, assignmentTargetTypeSymbol));
|
||||
|
||||
// Was an alternate found?
|
||||
if (externAlternateFieldSymbol != externFieldSymbol)
|
||||
{
|
||||
// Ensure the emitted assembly points at the correct left-hand type.
|
||||
assignmentTarget = BoundFieldAccessExpression.BindFieldAccess(context, node, externAlternateFieldSymbol, instanceExpression);
|
||||
}
|
||||
}
|
||||
|
||||
TargetExpression = assignmentTarget;
|
||||
}
|
||||
|
||||
public override Value EmitValue(EmitContext context)
|
||||
{
|
||||
return context.EmitSet(TargetExpression, SourceExpression);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 36e46b7844beaf641b74006dac880f3a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,50 @@
|
||||
|
||||
using System;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using UdonSharp.Compiler.Assembly;
|
||||
using UdonSharp.Compiler.Emit;
|
||||
using UdonSharp.Compiler.Symbols;
|
||||
|
||||
namespace UdonSharp.Compiler.Binder
|
||||
{
|
||||
internal sealed class BoundBitwiseNotExpression : BoundInvocationExpression
|
||||
{
|
||||
public override TypeSymbol ValueType => SourceExpression.ValueType;
|
||||
|
||||
public BoundBitwiseNotExpression(SyntaxNode node, BoundExpression sourceExpression)
|
||||
: base(node, null, sourceExpression, new BoundExpression[] {})
|
||||
{
|
||||
}
|
||||
|
||||
public override Value EmitValue(EmitContext context)
|
||||
{
|
||||
Type operandType = ValueType.UdonType.SystemType;
|
||||
|
||||
object allBits;
|
||||
|
||||
if (UdonSharpUtils.IsSignedType(operandType))
|
||||
{
|
||||
allBits = Convert.ChangeType(-1, operandType);
|
||||
} else
|
||||
{
|
||||
allBits = operandType.GetField("MaxValue").GetValue(null);
|
||||
}
|
||||
|
||||
BoundAccessExpression allBitsExpr = BoundAccessExpression.BindAccess(context.GetConstantValue(ValueType, allBits));
|
||||
|
||||
Value returnValue = context.GetReturnValue(ValueType);
|
||||
|
||||
using (context.InterruptAssignmentScope())
|
||||
{
|
||||
BoundAccessExpression operandVal = BoundAccessExpression.BindAccess(context.EmitValue(SourceExpression));
|
||||
|
||||
context.EmitValueAssignment(returnValue,
|
||||
CreateBoundInvocation(context, null,
|
||||
new ExternSynthesizedOperatorSymbol(BuiltinOperatorType.LogicalXor, ValueType, context), null,
|
||||
new BoundExpression[] { operandVal, allBitsExpr }));
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: beb46a34e718df243927180e735d4c2c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,34 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using UdonSharp.Compiler.Emit;
|
||||
|
||||
namespace UdonSharp.Compiler.Binder
|
||||
{
|
||||
internal class BoundBlock : BoundStatement
|
||||
{
|
||||
private ImmutableArray<BoundStatement> Statements { get; }
|
||||
|
||||
public BoundBlock(SyntaxNode node)
|
||||
:base(node)
|
||||
{
|
||||
Statements = ImmutableArray<BoundStatement>.Empty;
|
||||
}
|
||||
|
||||
public BoundBlock(SyntaxNode node, IEnumerable<BoundStatement> statements)
|
||||
: base(node)
|
||||
{
|
||||
Statements = ImmutableArray.CreateRange(statements);
|
||||
}
|
||||
|
||||
public override void Emit(EmitContext context)
|
||||
{
|
||||
using (context.OpenBlockScope())
|
||||
{
|
||||
foreach (BoundStatement statement in Statements)
|
||||
context.Emit(statement);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dd541469feb075d4eacb3446c5039db8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,18 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using UdonSharp.Compiler.Emit;
|
||||
|
||||
namespace UdonSharp.Compiler.Binder
|
||||
{
|
||||
internal sealed class BoundBreakStatement : BoundStatement
|
||||
{
|
||||
public BoundBreakStatement(SyntaxNode node)
|
||||
:base(node)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Emit(EmitContext context)
|
||||
{
|
||||
context.Module.AddJump(context.TopBreakLabel);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 29000ebe49b8b1542b77829cce1b5323
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,32 @@
|
||||
|
||||
using System;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using UdonSharp.Compiler.Emit;
|
||||
using UdonSharp.Compiler.Symbols;
|
||||
|
||||
namespace UdonSharp.Compiler.Binder
|
||||
{
|
||||
internal sealed class BoundCastExpression : BoundExpression
|
||||
{
|
||||
private TypeSymbol TargetType { get; }
|
||||
|
||||
private bool IsExplicit { get; }
|
||||
|
||||
public override TypeSymbol ValueType => TargetType;
|
||||
|
||||
public BoundCastExpression(SyntaxNode node, BoundExpression sourceExpression, TypeSymbol targetType, bool isExplicit)
|
||||
: base(node, sourceExpression)
|
||||
{
|
||||
if (targetType is TypeParameterSymbol)
|
||||
throw new InvalidOperationException("Cannot cast to generic parameter types");
|
||||
|
||||
TargetType = targetType;
|
||||
IsExplicit = isExplicit;
|
||||
}
|
||||
|
||||
public override Value EmitValue(EmitContext context)
|
||||
{
|
||||
return context.CastValue(context.EmitValue(SourceExpression), TargetType, IsExplicit);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2b4a317dc7fb76a40b198a313d5a936b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,54 @@
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
using UdonSharp.Compiler.Assembly;
|
||||
using UdonSharp.Compiler.Emit;
|
||||
using UdonSharp.Compiler.Symbols;
|
||||
|
||||
namespace UdonSharp.Compiler.Binder
|
||||
{
|
||||
internal sealed class BoundCoalesceExpression : BoundExpression
|
||||
{
|
||||
private BoundExpression Lhs { get; }
|
||||
private BoundExpression Rhs { get; }
|
||||
|
||||
public BoundCoalesceExpression(SyntaxNode node, BoundExpression lhs, BoundExpression rhs)
|
||||
: base(node, null)
|
||||
{
|
||||
Lhs = lhs;
|
||||
Rhs = rhs;
|
||||
}
|
||||
|
||||
public override TypeSymbol ValueType => Lhs.ValueType;
|
||||
|
||||
public override Value EmitValue(EmitContext context)
|
||||
{
|
||||
// We don't want any references outside the flow control to be dirtied conditionally
|
||||
context.TopTable.DirtyAllValues();
|
||||
|
||||
Value returnValue = context.GetReturnValue(ValueType);
|
||||
|
||||
context.EmitValueAssignment(returnValue, Lhs);
|
||||
|
||||
TypeSymbol systemObjectType = context.GetTypeSymbol(SpecialType.System_Object);
|
||||
|
||||
MethodSymbol objectEquality = new ExternSynthesizedOperatorSymbol(BuiltinOperatorType.Equality, systemObjectType, context);
|
||||
|
||||
Value conditionCheck = context.EmitValue(BoundInvocationExpression.CreateBoundInvocation(context, null,
|
||||
objectEquality, null,
|
||||
new BoundExpression[]
|
||||
{
|
||||
BoundAccessExpression.BindAccess(returnValue),
|
||||
BoundAccessExpression.BindAccess(context.GetConstantValue(systemObjectType, null))
|
||||
}));
|
||||
|
||||
JumpLabel notNullLabel = context.Module.CreateLabel();
|
||||
context.Module.AddJumpIfFalse(notNullLabel, conditionCheck);
|
||||
|
||||
context.EmitValueAssignment(returnValue, Rhs);
|
||||
|
||||
context.Module.LabelJump(notNullLabel);
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 71ebbbf1dd6f1e844a417f1b6b504d7b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,54 @@
|
||||
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using UdonSharp.Compiler.Assembly;
|
||||
using UdonSharp.Compiler.Emit;
|
||||
using UdonSharp.Compiler.Symbols;
|
||||
|
||||
namespace UdonSharp.Compiler.Binder
|
||||
{
|
||||
internal sealed class BoundConditionalExpression : BoundExpression
|
||||
{
|
||||
private BoundExpression ConditionExpression { get; }
|
||||
private BoundExpression TrueExpression { get; }
|
||||
private BoundExpression FalseExpression { get; }
|
||||
|
||||
public override TypeSymbol ValueType { get; }
|
||||
|
||||
public BoundConditionalExpression(ConditionalExpressionSyntax node, TypeSymbol resultType, BoundExpression conditionExpression, BoundExpression trueExpression, BoundExpression falseExpression)
|
||||
: base(node)
|
||||
{
|
||||
ConditionExpression = conditionExpression;
|
||||
TrueExpression = trueExpression;
|
||||
FalseExpression = falseExpression;
|
||||
ValueType = resultType;
|
||||
}
|
||||
|
||||
public override Value EmitValue(EmitContext context)
|
||||
{
|
||||
var assignmentInterrupt = context.InterruptAssignmentScope();
|
||||
Value conditionValue = context.EmitValue(ConditionExpression);
|
||||
assignmentInterrupt.Dispose();
|
||||
|
||||
JumpLabel conditionJump = context.Module.CreateLabel();
|
||||
JumpLabel exitTrueJump = context.Module.CreateLabel();
|
||||
|
||||
Value returnValue = context.GetReturnValue(ValueType);
|
||||
|
||||
// We don't want any references outside the flow control to be dirtied conditionally
|
||||
context.TopTable.DirtyAllValues();
|
||||
|
||||
context.Module.AddJumpIfFalse(conditionJump, conditionValue);
|
||||
|
||||
context.EmitValueAssignment(returnValue, TrueExpression);
|
||||
|
||||
context.Module.AddJump(exitTrueJump);
|
||||
context.Module.LabelJump(conditionJump);
|
||||
|
||||
context.EmitValueAssignment(returnValue, FalseExpression);
|
||||
|
||||
context.Module.LabelJump(exitTrueJump);
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d00b34c212929b46ab3530fd6f6bd15
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,61 @@
|
||||
|
||||
using System;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using UdonSharp.Compiler.Emit;
|
||||
using UdonSharp.Compiler.Symbols;
|
||||
|
||||
namespace UdonSharp.Compiler.Binder
|
||||
{
|
||||
internal class BoundConstArrayCreationExpression : BoundExpression
|
||||
{
|
||||
private TypeSymbol ArrayType { get; }
|
||||
private TypeSymbol ElementType { get; }
|
||||
private BoundExpression[] Initializers { get; }
|
||||
|
||||
public BoundConstArrayCreationExpression(SyntaxNode node, TypeSymbol arrayType, BoundExpression[] initializers)
|
||||
: base(node)
|
||||
{
|
||||
ArrayType = arrayType;
|
||||
ElementType = arrayType.ElementType;
|
||||
Initializers = initializers;
|
||||
}
|
||||
|
||||
public override TypeSymbol ValueType => ArrayType;
|
||||
|
||||
public override Value EmitValue(EmitContext context)
|
||||
{
|
||||
Value returnValue;
|
||||
|
||||
if (context.IsRecursiveMethodEmit)
|
||||
{
|
||||
using (context.InterruptAssignmentScope())
|
||||
{
|
||||
returnValue = context.EmitValue(new BoundArrayCreationExpression(SyntaxNode, context, ArrayType,
|
||||
new BoundExpression[]
|
||||
{
|
||||
BoundAccessExpression.BindAccess(
|
||||
context.GetConstantValue(context.GetTypeSymbol(SpecialType.System_Int32),
|
||||
Initializers.Length))
|
||||
}, null));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
returnValue = context.CreateGlobalInternalValue(ArrayType);
|
||||
returnValue.DefaultValue = Activator.CreateInstance(ArrayType.UdonType.SystemType, Initializers.Length);
|
||||
}
|
||||
|
||||
BoundAccessExpression arrayAccess = BoundAccessExpression.BindAccess(returnValue);
|
||||
TypeSymbol intType = context.GetTypeSymbol(SpecialType.System_Int32);
|
||||
|
||||
// This is quite wasteful for allocations in the compile, todo: look at caching these safely
|
||||
for (int i = 0; i < Initializers.Length; ++i)
|
||||
{
|
||||
BoundAccessExpression elementAccess = BoundAccessExpression.BindElementAccess(context, SyntaxNode, arrayAccess, new BoundExpression[]{new BoundConstantExpression(new ConstantValue<int>(i), intType, SyntaxNode)});
|
||||
context.EmitSet(elementAccess, Initializers[i]);
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 14ff91ebda983c347a578b557526495e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,18 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using UdonSharp.Compiler.Emit;
|
||||
|
||||
namespace UdonSharp.Compiler.Binder
|
||||
{
|
||||
internal sealed class BoundContinueStatement : BoundStatement
|
||||
{
|
||||
public BoundContinueStatement(SyntaxNode node)
|
||||
:base(node)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Emit(EmitContext context)
|
||||
{
|
||||
context.Module.AddJump(context.TopContinueLabel);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 97d2bf6e5ad2cfd4ca0c07bc65e0722e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,39 @@
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
using UdonSharp.Compiler.Assembly;
|
||||
using UdonSharp.Compiler.Emit;
|
||||
|
||||
namespace UdonSharp.Compiler.Binder
|
||||
{
|
||||
internal sealed class BoundDoStatement : BoundStatement
|
||||
{
|
||||
private BoundExpression Condition { get; }
|
||||
private BoundStatement Body { get; }
|
||||
|
||||
public BoundDoStatement(SyntaxNode node, BoundExpression condition, BoundStatement body)
|
||||
:base(node)
|
||||
{
|
||||
Condition = condition;
|
||||
Body = body;
|
||||
}
|
||||
|
||||
public override void Emit(EmitContext context)
|
||||
{
|
||||
JumpLabel continueLabel = context.PushContinueLabel();
|
||||
JumpLabel breakLabel = context.PushBreakLabel();
|
||||
|
||||
context.Module.LabelJump(continueLabel);
|
||||
|
||||
context.Emit(Body);
|
||||
|
||||
context.Module.AddJumpIfFalse(breakLabel, context.EmitValue(Condition));
|
||||
|
||||
context.Module.AddJump(continueLabel);
|
||||
|
||||
context.Module.LabelJump(breakLabel);
|
||||
|
||||
context.PopBreakLabel();
|
||||
context.PopContinueLabel();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ac98576983713f547ac803a0b22bc2eb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,214 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using UdonSharp.Compiler.Emit;
|
||||
using UdonSharp.Compiler.Symbols;
|
||||
using UdonSharp.Compiler.Udon;
|
||||
using UdonSharp.Core;
|
||||
using UdonSharp.Localization;
|
||||
|
||||
namespace UdonSharp.Compiler.Binder
|
||||
{
|
||||
internal abstract class BoundExpression : BoundNode
|
||||
{
|
||||
public bool IsConstant => ConstantValue != null;
|
||||
public virtual IConstantValue ConstantValue => null;
|
||||
|
||||
/// <summary>
|
||||
/// The user type of Value that this expression will emit when EmitValue is called
|
||||
/// </summary>
|
||||
public abstract TypeSymbol ValueType { get; }
|
||||
|
||||
public bool IsThis { get; protected set; }
|
||||
|
||||
protected BoundExpression SourceExpression { get; }
|
||||
|
||||
protected BoundExpression(SyntaxNode node, BoundExpression sourceExpression = null)
|
||||
: base(node)
|
||||
{
|
||||
SourceExpression = sourceExpression;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// All expressions must instead implement EmitValue since they will always evaluate to something
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
public override void Emit(EmitContext context)
|
||||
{
|
||||
context.EmitValue(this);
|
||||
}
|
||||
|
||||
public abstract Value EmitValue(EmitContext context);
|
||||
|
||||
protected virtual void ReleaseCowValuesImpl(EmitContext context) {}
|
||||
|
||||
public void ReleaseCowReferences(EmitContext context)
|
||||
{
|
||||
ReleaseCowValuesImpl(context);
|
||||
context.ReleaseCowValues(this);
|
||||
SourceExpression?.ReleaseCowReferences(context);
|
||||
}
|
||||
|
||||
protected ExternFieldSymbol SetupExternAccessor(SyntaxNode node, AbstractPhaseContext context, ExternFieldSymbol externFieldAccessor,
|
||||
BoundExpression sourceExpression, CompilerUdonInterface.FieldAccessorType accessorType,
|
||||
Func<TypeSymbol, ExternFieldSymbol> synthesizedFieldSymbolFactory)
|
||||
{
|
||||
string sig = accessorType == CompilerUdonInterface.FieldAccessorType.Get ? externFieldAccessor.ExternGetSignature : externFieldAccessor.ExternSetSignature;
|
||||
|
||||
if (!CompilerUdonInterface.IsExposedToUdon(sig))
|
||||
{
|
||||
ExternFieldSymbol externAlternateAccessor = FindAlternateAccessor(context, externFieldAccessor, sourceExpression, accessorType, synthesizedFieldSymbolFactory);
|
||||
if (externAlternateAccessor == null)
|
||||
{
|
||||
throw new NotExposedException(LocStr.CE_UdonFieldNotExposed, node, $"{externFieldAccessor.RoslynSymbol?.ToDisplayString() ?? externFieldAccessor.ToString()}, sig: {sig}");
|
||||
}
|
||||
else
|
||||
{
|
||||
return externAlternateAccessor;
|
||||
}
|
||||
}
|
||||
|
||||
return externFieldAccessor;
|
||||
}
|
||||
|
||||
protected ExternMethodSymbol SetupExternMethodSymbol(SyntaxNode node, AbstractPhaseContext context, ExternMethodSymbol methodSymbol,
|
||||
BoundExpression instanceExpression, BoundExpression[] parameterExpressions)
|
||||
{
|
||||
if (!CompilerUdonInterface.IsExposedToUdon(methodSymbol.ExternSignature))
|
||||
{
|
||||
ExternMethodSymbol externAlternateMethodSymbol = FindAlternateInvocation(context, methodSymbol, instanceExpression, parameterExpressions);
|
||||
if (externAlternateMethodSymbol == null)
|
||||
{
|
||||
throw new NotExposedException(LocStr.CE_UdonMethodNotExposed, node, $"{methodSymbol.RoslynSymbol?.ToDisplayString() ?? methodSymbol.ToString()}, sig: {methodSymbol.ExternSignature}");
|
||||
}
|
||||
else
|
||||
{
|
||||
return externAlternateMethodSymbol;
|
||||
}
|
||||
}
|
||||
|
||||
return methodSymbol;
|
||||
}
|
||||
|
||||
private static ExternFieldSymbol FindAlternateAccessor(AbstractPhaseContext context, ExternFieldSymbol originalFieldSymbol,
|
||||
BoundExpression sourceExpression, CompilerUdonInterface.FieldAccessorType accessorType,
|
||||
Func<TypeSymbol, ExternFieldSymbol> synthesizedFieldSymbolFactory)
|
||||
{
|
||||
if (originalFieldSymbol.IsStatic) return null;
|
||||
|
||||
List<TypeSymbol> candidates = new List<TypeSymbol>();
|
||||
FindCandidateAlternateTypes(context, candidates, sourceExpression?.ValueType ?? originalFieldSymbol.ContainingType);
|
||||
|
||||
foreach (TypeSymbol candidate in candidates)
|
||||
{
|
||||
ExternFieldSymbol externFieldSymbol = synthesizedFieldSymbolFactory(candidate);
|
||||
string sig = accessorType == CompilerUdonInterface.FieldAccessorType.Get ? externFieldSymbol.ExternGetSignature : externFieldSymbol.ExternSetSignature;
|
||||
if (CompilerUdonInterface.IsExposedToUdon(sig))
|
||||
{
|
||||
return externFieldSymbol;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static ExternMethodSymbol FindAlternateInvocation(AbstractPhaseContext context,
|
||||
MethodSymbol originalSymbol, BoundExpression instanceExpression, BoundExpression[] parameterExpressions)
|
||||
{
|
||||
if (originalSymbol.IsStatic || originalSymbol.IsConstructor) return null;
|
||||
|
||||
List<TypeSymbol> candidates = new List<TypeSymbol>();
|
||||
FindCandidateAlternateTypes(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 void FindCandidateAlternateTypes(AbstractPhaseContext context, List<TypeSymbol> candidates, TypeSymbol ty)
|
||||
{
|
||||
foreach (var intf in ty.RoslynSymbol.AllInterfaces)
|
||||
{
|
||||
candidates.Add(context.GetTypeSymbol(intf));
|
||||
}
|
||||
|
||||
while (ty != null)
|
||||
{
|
||||
candidates.Add(ty);
|
||||
ty = ty.BaseType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class BoundConstantExpression : BoundAccessExpression
|
||||
{
|
||||
public override IConstantValue ConstantValue { get; }
|
||||
|
||||
public TypeSymbol ConstantType { get; }
|
||||
|
||||
public override TypeSymbol ValueType => ConstantType;
|
||||
|
||||
public BoundConstantExpression(IConstantValue constantValue, TypeSymbol constantType, SyntaxNode node)
|
||||
:base(node, null)
|
||||
{
|
||||
ConstantValue = constantValue;
|
||||
ConstantType = constantType;
|
||||
}
|
||||
|
||||
public BoundConstantExpression(object constantValue, TypeSymbol typeSymbol)
|
||||
:base(null, null)
|
||||
{
|
||||
ConstantType = typeSymbol;
|
||||
|
||||
Type targetType = typeSymbol.UdonType.SystemType;
|
||||
|
||||
if (typeSymbol.IsEnum && typeSymbol.IsExtern)
|
||||
constantValue = Enum.ToObject(targetType, constantValue);
|
||||
|
||||
ConstantValue =
|
||||
(IConstantValue) Activator.CreateInstance(typeof(ConstantValue<>).MakeGenericType(typeSymbol.UdonType.SystemType),
|
||||
constantValue);
|
||||
}
|
||||
|
||||
public BoundConstantExpression(object constantValue, TypeSymbol typeSymbol, SyntaxNode node)
|
||||
:base(null, null)
|
||||
{
|
||||
ConstantType = typeSymbol;
|
||||
|
||||
Type targetType = typeSymbol.UdonType.SystemType;
|
||||
|
||||
if (typeSymbol.IsEnum && typeSymbol.IsExtern)
|
||||
constantValue = Enum.ToObject(targetType, constantValue);
|
||||
|
||||
ConstantValue =
|
||||
(IConstantValue) Activator.CreateInstance(typeof(ConstantValue<>).MakeGenericType(typeSymbol.UdonType.SystemType),
|
||||
constantValue);
|
||||
}
|
||||
|
||||
public override Value EmitValue(EmitContext context)
|
||||
{
|
||||
return context.GetConstantValue(ConstantType, ConstantValue.Value);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"BoundConstantExpression<{ConstantValue.GetType().GetGenericArguments()[0]}>: " + ConstantValue.Value?.ToString() ?? "null";
|
||||
}
|
||||
|
||||
public override Value EmitSet(EmitContext context, BoundExpression valueExpression)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot set value on a constant value");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1dbf0ab7fd74e2649b46c6685f097515
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,22 @@
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
using UdonSharp.Compiler.Emit;
|
||||
|
||||
namespace UdonSharp.Compiler.Binder
|
||||
{
|
||||
internal class BoundExpressionStatement : BoundStatement
|
||||
{
|
||||
public BoundExpression Expression { get; }
|
||||
|
||||
public BoundExpressionStatement(SyntaxNode node, BoundExpression expression)
|
||||
: base(node)
|
||||
{
|
||||
Expression = expression;
|
||||
}
|
||||
|
||||
public override void Emit(EmitContext context)
|
||||
{
|
||||
context.Emit(Expression);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2bde224ee87b1b34a96be7c62bea99d8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,102 @@
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using UdonSharp.Compiler.Emit;
|
||||
using UdonSharp.Compiler.Symbols;
|
||||
|
||||
namespace UdonSharp.Compiler.Binder
|
||||
{
|
||||
internal class BoundExternInvocation : BoundInvocationExpression
|
||||
{
|
||||
private ExternMethodSymbol externMethodSymbol;
|
||||
|
||||
public BoundExternInvocation(SyntaxNode node, AbstractPhaseContext context, MethodSymbol method, BoundExpression instanceExpression, BoundExpression[] parameterExpressions)
|
||||
: base(node, method, instanceExpression, parameterExpressions)
|
||||
{
|
||||
externMethodSymbol = SetupExternMethodSymbol(node, context, (ExternMethodSymbol)method, instanceExpression, parameterExpressions);
|
||||
}
|
||||
|
||||
public override Value EmitValue(EmitContext context)
|
||||
{
|
||||
var module = context.Module;
|
||||
|
||||
Value.CowValue instanceValue = null;
|
||||
|
||||
using (context.InterruptAssignmentScope())
|
||||
{
|
||||
if (!Method.IsStatic && !Method.IsConstructor)
|
||||
{
|
||||
instanceValue = GetInstanceValue(context);
|
||||
|
||||
// Prevent mutating a value constant if you call a method on the constant for some reason
|
||||
// todo: fix for use with the cowvalues
|
||||
// if (instanceValue.IsConstant && instanceValue.UserType.IsValueType &&
|
||||
// Method.Name != "ToString" && Method.Name != "Equals") // Common non-mutating methods
|
||||
// {
|
||||
// Value proxyInstanceValue = context.CreateInternalValue(instanceValue.UserType);
|
||||
// module.AddCopy(instanceValue, proxyInstanceValue);
|
||||
// instanceValue = proxyInstanceValue;
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
Value[] parameterValues = GetParameterValues(context);
|
||||
|
||||
if (instanceValue != null)
|
||||
module.AddPush(instanceValue.Value);
|
||||
|
||||
Value[] recursiveValues = null;
|
||||
|
||||
string methodName = Method.Name;
|
||||
|
||||
if (methodName == "SendCustomEvent" ||
|
||||
methodName == "SendCustomNetworkEvent" ||
|
||||
methodName == "RunProgram")
|
||||
{
|
||||
if (context.IsRecursiveMethodEmit)
|
||||
{
|
||||
recursiveValues = context.CollectRecursiveValues();
|
||||
CheckStackSize(context.GetConstantValue(context.GetTypeSymbol(SpecialType.System_Int32), recursiveValues.Length), context);
|
||||
PushRecursiveValues(recursiveValues, context);
|
||||
context.UpdateRecursiveStackMaxSize(recursiveValues.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
instanceValue?.Dispose();
|
||||
}
|
||||
|
||||
context.TopTable.DirtyAllValues();
|
||||
}
|
||||
|
||||
foreach (Value value in parameterValues)
|
||||
module.AddPush(value);
|
||||
|
||||
Value returnValue = null;
|
||||
|
||||
if (IsPropertySetter)
|
||||
returnValue = parameterValues.Last();
|
||||
|
||||
if (Method.ReturnType != null)
|
||||
{
|
||||
returnValue = context.GetReturnValue(Method.ReturnType);
|
||||
if (returnValue.IsConstant)
|
||||
throw new InvalidOperationException("Cannot return to const value");
|
||||
|
||||
module.AddPush(returnValue);
|
||||
}
|
||||
|
||||
module.AddExtern(externMethodSymbol);
|
||||
|
||||
if (recursiveValues != null)
|
||||
PopRecursiveValues(recursiveValues, context);
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"BoundExternInvocation: {Method}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 004d86898762cfa49a0d4341af07d3be
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,176 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using UdonSharp.Compiler.Emit;
|
||||
using UdonSharp.Compiler.Symbols;
|
||||
using UdonSharp.Compiler.Udon;
|
||||
using UdonSharp.Core;
|
||||
using UdonSharp.Localization;
|
||||
|
||||
namespace UdonSharp.Compiler.Binder
|
||||
{
|
||||
internal abstract class BoundFieldAccessExpression : BoundAccessExpression
|
||||
{
|
||||
protected BoundFieldAccessExpression(SyntaxNode node, BoundExpression sourceExpression)
|
||||
: base(node, sourceExpression)
|
||||
{
|
||||
}
|
||||
|
||||
public static BoundAccessExpression BindFieldAccess(AbstractPhaseContext context, SyntaxNode node, FieldSymbol fieldSymbol, BoundExpression sourceExpression)
|
||||
{
|
||||
if (fieldSymbol.IsConst)
|
||||
return new BoundConstantExpression(fieldSymbol.RoslynSymbol.ConstantValue, fieldSymbol.Type);
|
||||
|
||||
if (fieldSymbol is ExternFieldSymbol externField)
|
||||
{
|
||||
return new BoundExternFieldAccessExpression(node, context, externField, sourceExpression);
|
||||
}
|
||||
|
||||
if (fieldSymbol is UdonSharpBehaviourFieldSymbol udonSharpBehaviourFieldSymbol)
|
||||
return new BoundUdonSharpBehaviourFieldAccessExpression(node, udonSharpBehaviourFieldSymbol,
|
||||
sourceExpression);
|
||||
|
||||
throw new NotImplementedException($"Field access for symbol {fieldSymbol.GetType()} is not implemented");
|
||||
}
|
||||
|
||||
private sealed class BoundExternFieldAccessExpression : BoundFieldAccessExpression
|
||||
{
|
||||
private ExternFieldSymbol Field { get; }
|
||||
|
||||
public override TypeSymbol ValueType => Field.Type;
|
||||
|
||||
public BoundExternFieldAccessExpression(SyntaxNode node, AbstractPhaseContext context, ExternFieldSymbol field,
|
||||
BoundExpression sourceExpression)
|
||||
: base(node, sourceExpression)
|
||||
{
|
||||
// This is access, so check for get only
|
||||
// Passing originalSymbol.RoslynSymbol to synthesized fields to inherit attributes (static, const, etc.)
|
||||
Field = SetupExternAccessor(node, context, field, sourceExpression, CompilerUdonInterface.FieldAccessorType.Get,
|
||||
(candidate) => new ExternSynthesizedFieldSymbol(field.RoslynSymbol, context, field.Name, candidate, field.Type));
|
||||
}
|
||||
|
||||
private Value.CowValue GetInstanceValue(EmitContext context)
|
||||
{
|
||||
Value.CowValue[] instanceValue = context.GetExpressionCowValues(this, "propertyInstance");
|
||||
|
||||
if (instanceValue != null)
|
||||
return instanceValue[0];
|
||||
|
||||
Value.CowValue instanceCowValue;
|
||||
|
||||
using (context.InterruptAssignmentScope())
|
||||
instanceCowValue = context.EmitValueWithDeferredRelease(SourceExpression).GetCowValue(context);
|
||||
|
||||
context.RegisterCowValues(new []{instanceCowValue}, this, "propertyInstance");
|
||||
|
||||
return instanceCowValue;
|
||||
}
|
||||
|
||||
public override Value EmitSet(EmitContext context, BoundExpression valueExpression)
|
||||
{
|
||||
Value.CowValue sourceValue = null;
|
||||
|
||||
if (!Field.IsStatic)
|
||||
sourceValue = GetInstanceValue(context);
|
||||
|
||||
Value expressionResult = context.EmitValue(valueExpression);
|
||||
|
||||
if (sourceValue != null)
|
||||
context.Module.AddPush(sourceValue.Value);
|
||||
|
||||
context.Module.AddPush(expressionResult);
|
||||
|
||||
context.Module.AddExternSet(Field);
|
||||
|
||||
if (sourceValue != null)
|
||||
{
|
||||
if (SourceExpression.ValueType.IsValueType &&
|
||||
SourceExpression is BoundAccessExpression sourceAccessExpression)
|
||||
context.EmitSet(sourceAccessExpression, BindAccess(sourceValue.Value));
|
||||
}
|
||||
|
||||
return expressionResult;
|
||||
}
|
||||
|
||||
public override Value EmitValue(EmitContext context)
|
||||
{
|
||||
Value returnValue = context.GetReturnValue(Field.Type);
|
||||
|
||||
Value.CowValue sourceValue = null;
|
||||
|
||||
if (!Field.IsStatic)
|
||||
sourceValue = GetInstanceValue(context);
|
||||
|
||||
if (sourceValue != null)
|
||||
context.Module.AddPush(sourceValue.Value);
|
||||
|
||||
context.Module.AddPush(returnValue);
|
||||
|
||||
context.Module.AddExternGet(Field);
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class BoundUdonSharpBehaviourFieldAccessExpression : BoundFieldAccessExpression
|
||||
{
|
||||
private FieldSymbol Field { get; }
|
||||
|
||||
public BoundUdonSharpBehaviourFieldAccessExpression(SyntaxNode node, FieldSymbol fieldSymbol, BoundExpression sourceExpression)
|
||||
: base(node, sourceExpression)
|
||||
{
|
||||
Field = fieldSymbol;
|
||||
}
|
||||
|
||||
public override TypeSymbol ValueType => Field.Type;
|
||||
|
||||
public override Value EmitValue(EmitContext context)
|
||||
{
|
||||
if (SourceExpression == null || SourceExpression.IsThis)
|
||||
return context.GetUserValue(Field);
|
||||
|
||||
TypeSymbol stringType = context.GetTypeSymbol(SpecialType.System_String);
|
||||
MethodSymbol setProgramVariableMethod = context.GetTypeSymbol(typeof(UdonSharpBehaviour))
|
||||
.GetMembers<MethodSymbol>("GetProgramVariable", context)
|
||||
.First(e => e.Parameters.Length == 1 &&
|
||||
e.Parameters[0].Type == stringType);
|
||||
|
||||
return context.CastValue(context.EmitValue(BoundInvocationExpression.CreateBoundInvocation(context, SyntaxNode,
|
||||
setProgramVariableMethod, SourceExpression,
|
||||
new BoundExpression[]
|
||||
{
|
||||
BindAccess(context.GetConstantValue(stringType, Field.Name))
|
||||
})), Field.Type, true);
|
||||
}
|
||||
|
||||
public override Value EmitSet(EmitContext context, BoundExpression valueExpression)
|
||||
{
|
||||
if (SourceExpression == null || SourceExpression.IsThis)
|
||||
return context.EmitValueAssignment(context.GetUserValue(Field), valueExpression);
|
||||
|
||||
if (Field.HasAttribute<FieldChangeCallbackAttribute>())
|
||||
throw new CompilerException("Cannot set field on U# behaviour by reference when that field has a FieldChangeCallback attribute.");
|
||||
|
||||
TypeSymbol stringType = context.GetTypeSymbol(SpecialType.System_String);
|
||||
MethodSymbol setProgramVariableMethod = context.GetTypeSymbol(typeof(UdonSharpBehaviour))
|
||||
.GetMembers<MethodSymbol>("SetProgramVariable", context)
|
||||
.First(e => e.Parameters.Length == 2 &&
|
||||
e.Parameters[0].Type == stringType);
|
||||
|
||||
Value value = context.EmitValue(valueExpression);
|
||||
|
||||
context.Emit(BoundInvocationExpression.CreateBoundInvocation(context, SyntaxNode,
|
||||
setProgramVariableMethod, SourceExpression,
|
||||
new BoundExpression[]
|
||||
{
|
||||
BindAccess(context.GetConstantValue(stringType, Field.Name)),
|
||||
BindAccess(value)
|
||||
}));
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3bd7d982e8955b547b222c05bf89facc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,90 @@
|
||||
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using UdonSharp.Compiler.Assembly;
|
||||
using UdonSharp.Compiler.Emit;
|
||||
using UdonSharp.Compiler.Symbols;
|
||||
|
||||
namespace UdonSharp.Compiler.Binder
|
||||
{
|
||||
internal sealed class BoundForEachCharStatement : BoundStatement
|
||||
{
|
||||
private BoundExpression IteratorSource { get; }
|
||||
private Symbol ValueSymbol { get; }
|
||||
private BoundStatement BodyStatement { get; }
|
||||
|
||||
public BoundForEachCharStatement(SyntaxNode node, BoundExpression iteratorSource, Symbol valueSymbol, BoundStatement bodyStatement)
|
||||
:base(node)
|
||||
{
|
||||
IteratorSource = iteratorSource;
|
||||
ValueSymbol = valueSymbol;
|
||||
BodyStatement = bodyStatement;
|
||||
}
|
||||
|
||||
public override void Emit(EmitContext context)
|
||||
{
|
||||
var blockScope = context.OpenBlockScope();
|
||||
|
||||
TypeSymbol intType = context.GetTypeSymbol(SpecialType.System_Int32);
|
||||
|
||||
MethodSymbol toCharArrayMethod = context.GetTypeSymbol(SpecialType.System_String).GetMembers<MethodSymbol>("ToCharArray", context).First(e => e.Parameters.Length == 0);
|
||||
PropertySymbol lengthProperty = context.GetTypeSymbol(SpecialType.System_Array).GetMember<PropertySymbol>("Length", context);
|
||||
|
||||
Value iteratorValue = context.EmitValue(BoundInvocationExpression.CreateBoundInvocation(context, SyntaxNode, toCharArrayMethod, IteratorSource, new BoundExpression[] {}));
|
||||
iteratorValue.MarkUsedRecursively();
|
||||
var iteratorAccess = BoundAccessExpression.BindAccess(iteratorValue);
|
||||
|
||||
Value arraySize = context.CreateInternalValue(intType);
|
||||
arraySize.MarkUsedRecursively();
|
||||
|
||||
BoundAccessExpression getLength = BoundAccessExpression.BindAccess(context, SyntaxNode, lengthProperty, iteratorAccess);
|
||||
context.EmitValueAssignment(arraySize, getLength);
|
||||
|
||||
// Declare and reset incrementor value
|
||||
Value incrementorValue = context.CreateInternalValue(intType);
|
||||
incrementorValue.MarkUsedRecursively();
|
||||
context.EmitValueAssignment(incrementorValue, BoundAccessExpression.BindAccess(context.GetConstantValue(intType, 0)));
|
||||
|
||||
JumpLabel loopLabel = context.Module.CreateLabel();
|
||||
context.Module.LabelJump(loopLabel);
|
||||
|
||||
var incrementorAccess = BoundAccessExpression.BindAccess(incrementorValue);
|
||||
|
||||
BoundExpression increment = new BoundInvocationExpression.BoundPrefixOperatorExpression(context, SyntaxNode,
|
||||
incrementorAccess, new ExternSynthesizedOperatorSymbol(BuiltinOperatorType.Addition, intType, context));
|
||||
|
||||
var lengthCheck = BoundInvocationExpression.CreateBoundInvocation(context, SyntaxNode,
|
||||
new ExternSynthesizedOperatorSymbol(BuiltinOperatorType.LessThan, intType, context), null,
|
||||
new BoundExpression[]
|
||||
{
|
||||
incrementorAccess,
|
||||
BoundAccessExpression.BindAccess(arraySize)
|
||||
});
|
||||
|
||||
JumpLabel exitLoopLabel = context.PushBreakLabel();
|
||||
JumpLabel continueLabel = context.PushContinueLabel();
|
||||
|
||||
Value lengthCheckResult = context.EmitValue(lengthCheck);
|
||||
context.Module.AddJumpIfFalse(exitLoopLabel, lengthCheckResult);
|
||||
|
||||
context.EmitValueAssignment(context.GetUserValue(ValueSymbol),
|
||||
BoundAccessExpression.BindElementAccess(context, SyntaxNode, iteratorAccess,
|
||||
new BoundExpression[] {incrementorAccess}));
|
||||
|
||||
context.Emit(BodyStatement);
|
||||
|
||||
context.Module.LabelJump(continueLabel);
|
||||
|
||||
context.Emit(increment);
|
||||
|
||||
context.Module.AddJump(loopLabel);
|
||||
|
||||
context.Module.LabelJump(exitLoopLabel);
|
||||
|
||||
context.PopBreakLabel();
|
||||
context.PopContinueLabel();
|
||||
|
||||
blockScope.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 471eaf7f712b1b94b8729a6db30302a8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,91 @@
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
using UdonSharp.Compiler.Assembly;
|
||||
using UdonSharp.Compiler.Emit;
|
||||
using UdonSharp.Compiler.Symbols;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UdonSharp.Compiler.Binder
|
||||
{
|
||||
internal sealed class BoundForEachChildTransformStatement : BoundStatement
|
||||
{
|
||||
private BoundExpression IteratorSource { get; }
|
||||
private Symbol ValueSymbol { get; }
|
||||
private BoundStatement BodyStatement { get; }
|
||||
|
||||
public BoundForEachChildTransformStatement(SyntaxNode node, BoundExpression iteratorSource, Symbol valueSymbol, BoundStatement bodyStatement)
|
||||
:base(node)
|
||||
{
|
||||
IteratorSource = iteratorSource;
|
||||
ValueSymbol = valueSymbol;
|
||||
BodyStatement = bodyStatement;
|
||||
}
|
||||
|
||||
public override void Emit(EmitContext context)
|
||||
{
|
||||
var blockScope = context.OpenBlockScope();
|
||||
|
||||
TypeSymbol intType = context.GetTypeSymbol(SpecialType.System_Int32);
|
||||
TypeSymbol transformType = context.GetTypeSymbol(typeof(Transform));
|
||||
|
||||
MethodSymbol getChildMethod = transformType.GetMember<MethodSymbol>("GetChild", context);
|
||||
PropertySymbol lengthProperty = transformType.GetMember<PropertySymbol>("childCount", context);
|
||||
|
||||
Value iteratorValue = context.EmitValue(IteratorSource);
|
||||
iteratorValue.MarkUsedRecursively();
|
||||
var iteratorAccess = BoundAccessExpression.BindAccess(iteratorValue);
|
||||
|
||||
Value arraySize = context.CreateInternalValue(intType);
|
||||
arraySize.MarkUsedRecursively();
|
||||
|
||||
BoundAccessExpression getLength = BoundAccessExpression.BindAccess(context, SyntaxNode, lengthProperty, iteratorAccess);
|
||||
context.EmitValueAssignment(arraySize, getLength);
|
||||
|
||||
// Declare and reset incrementor value
|
||||
Value incrementorValue = context.CreateInternalValue(intType);
|
||||
incrementorValue.MarkUsedRecursively();
|
||||
context.EmitValueAssignment(incrementorValue, BoundAccessExpression.BindAccess(context.GetConstantValue(intType, 0)));
|
||||
|
||||
JumpLabel loopLabel = context.Module.CreateLabel();
|
||||
context.Module.LabelJump(loopLabel);
|
||||
|
||||
var incrementorAccess = BoundAccessExpression.BindAccess(incrementorValue);
|
||||
|
||||
BoundExpression increment = new BoundInvocationExpression.BoundPrefixOperatorExpression(context, SyntaxNode,
|
||||
incrementorAccess, new ExternSynthesizedOperatorSymbol(BuiltinOperatorType.Addition, intType, context));
|
||||
|
||||
var lengthCheck = BoundInvocationExpression.CreateBoundInvocation(context, SyntaxNode,
|
||||
new ExternSynthesizedOperatorSymbol(BuiltinOperatorType.LessThan, intType, context), null,
|
||||
new BoundExpression[]
|
||||
{
|
||||
incrementorAccess,
|
||||
BoundAccessExpression.BindAccess(arraySize)
|
||||
});
|
||||
|
||||
JumpLabel exitLoopLabel = context.PushBreakLabel();
|
||||
JumpLabel continueLabel = context.PushContinueLabel();
|
||||
|
||||
Value lengthCheckResult = context.EmitValue(lengthCheck);
|
||||
context.Module.AddJumpIfFalse(exitLoopLabel, lengthCheckResult);
|
||||
|
||||
context.EmitValueAssignment(context.GetUserValue(ValueSymbol),
|
||||
BoundInvocationExpression.CreateBoundInvocation(context, SyntaxNode, getChildMethod, iteratorAccess,
|
||||
new BoundExpression[] {incrementorAccess}));
|
||||
|
||||
context.Emit(BodyStatement);
|
||||
|
||||
context.Module.LabelJump(continueLabel);
|
||||
|
||||
context.Emit(increment);
|
||||
|
||||
context.Module.AddJump(loopLabel);
|
||||
|
||||
context.Module.LabelJump(exitLoopLabel);
|
||||
|
||||
context.PopBreakLabel();
|
||||
context.PopContinueLabel();
|
||||
|
||||
blockScope.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 87631ba1def01394daff5d58136487a9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,87 @@
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
using UdonSharp.Compiler.Assembly;
|
||||
using UdonSharp.Compiler.Emit;
|
||||
using UdonSharp.Compiler.Symbols;
|
||||
|
||||
namespace UdonSharp.Compiler.Binder
|
||||
{
|
||||
internal sealed class BoundForEachStatement : BoundStatement
|
||||
{
|
||||
private BoundExpression IteratorSource { get; }
|
||||
private Symbol ValueSymbol { get; }
|
||||
private BoundStatement BodyStatement { get; }
|
||||
|
||||
public BoundForEachStatement(SyntaxNode node, BoundExpression iteratorSource, Symbol valueSymbol, BoundStatement bodyStatement)
|
||||
:base(node)
|
||||
{
|
||||
IteratorSource = iteratorSource;
|
||||
ValueSymbol = valueSymbol;
|
||||
BodyStatement = bodyStatement;
|
||||
}
|
||||
|
||||
public override void Emit(EmitContext context)
|
||||
{
|
||||
var blockScope = context.OpenBlockScope();
|
||||
|
||||
TypeSymbol intType = context.GetTypeSymbol(SpecialType.System_Int32);
|
||||
Value iteratorValue = context.EmitValue(IteratorSource);
|
||||
iteratorValue.MarkUsedRecursively();
|
||||
var iteratorAccess = BoundAccessExpression.BindAccess(iteratorValue);
|
||||
|
||||
Value arraySize = context.CreateInternalValue(intType);
|
||||
arraySize.MarkUsedRecursively();
|
||||
|
||||
PropertySymbol lengthProperty = context.GetTypeSymbol(SpecialType.System_Array).GetMember<PropertySymbol>("Length", context);
|
||||
|
||||
BoundAccessExpression getLength = BoundAccessExpression.BindAccess(context, SyntaxNode, lengthProperty, iteratorAccess);
|
||||
context.EmitValueAssignment(arraySize, getLength);
|
||||
|
||||
// Declare and reset incrementor value
|
||||
Value incrementorValue = context.CreateInternalValue(intType);
|
||||
incrementorValue.MarkUsedRecursively();
|
||||
context.EmitValueAssignment(incrementorValue, BoundAccessExpression.BindAccess(context.GetConstantValue(intType, 0)));
|
||||
|
||||
JumpLabel loopLabel = context.Module.CreateLabel();
|
||||
context.Module.LabelJump(loopLabel);
|
||||
|
||||
var incrementorAccess = BoundAccessExpression.BindAccess(incrementorValue);
|
||||
|
||||
BoundExpression increment = new BoundInvocationExpression.BoundPrefixOperatorExpression(context, SyntaxNode,
|
||||
incrementorAccess, new ExternSynthesizedOperatorSymbol(BuiltinOperatorType.Addition, intType, context));
|
||||
|
||||
BoundExpression lengthCheck = BoundInvocationExpression.CreateBoundInvocation(context, SyntaxNode,
|
||||
new ExternSynthesizedOperatorSymbol(BuiltinOperatorType.LessThan, intType, context), null,
|
||||
new BoundExpression[]
|
||||
{
|
||||
incrementorAccess,
|
||||
BoundAccessExpression.BindAccess(arraySize)
|
||||
});
|
||||
|
||||
JumpLabel exitLoopLabel = context.PushBreakLabel();
|
||||
JumpLabel continueLabel = context.PushContinueLabel();
|
||||
|
||||
Value lengthCheckResult = context.EmitValue(lengthCheck);
|
||||
context.Module.AddJumpIfFalse(exitLoopLabel, lengthCheckResult);
|
||||
|
||||
context.EmitValueAssignment(context.GetUserValue(ValueSymbol),
|
||||
BoundAccessExpression.BindElementAccess(context, SyntaxNode, iteratorAccess,
|
||||
new BoundExpression[] {incrementorAccess}));
|
||||
|
||||
context.Emit(BodyStatement);
|
||||
|
||||
context.Module.LabelJump(continueLabel);
|
||||
|
||||
context.Emit(increment);
|
||||
|
||||
context.Module.AddJump(loopLabel);
|
||||
|
||||
context.Module.LabelJump(exitLoopLabel);
|
||||
|
||||
context.PopBreakLabel();
|
||||
context.PopContinueLabel();
|
||||
|
||||
blockScope.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7b07c77cc30beda43bcdb9851b8de6fc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,61 @@
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
using UdonSharp.Compiler.Assembly;
|
||||
using UdonSharp.Compiler.Emit;
|
||||
|
||||
namespace UdonSharp.Compiler.Binder
|
||||
{
|
||||
internal sealed class BoundForStatement : BoundStatement
|
||||
{
|
||||
private BoundVariableDeclarationStatement Declaration { get; }
|
||||
private BoundExpression[] Initializers { get; }
|
||||
private BoundExpression Condition { get; }
|
||||
private BoundExpression[] Incrementors { get; }
|
||||
private BoundStatement Body { get; }
|
||||
|
||||
public BoundForStatement(SyntaxNode node, BoundVariableDeclarationStatement declaration, BoundExpression[] initializers, BoundExpression condition, BoundExpression[] incrementors, BoundStatement body)
|
||||
:base(node)
|
||||
{
|
||||
Declaration = declaration;
|
||||
Initializers = initializers;
|
||||
Condition = condition;
|
||||
Incrementors = incrementors;
|
||||
Body = body;
|
||||
}
|
||||
|
||||
public override void Emit(EmitContext context)
|
||||
{
|
||||
using (context.OpenBlockScope())
|
||||
{
|
||||
if (Declaration != null)
|
||||
context.Emit(Declaration);
|
||||
|
||||
foreach (var initializer in Initializers)
|
||||
context.Emit(initializer);
|
||||
|
||||
JumpLabel continueLabel = context.PushContinueLabel();
|
||||
JumpLabel breakLabel = context.PushBreakLabel();
|
||||
JumpLabel loopLabel = context.Module.CreateLabel();
|
||||
|
||||
context.Module.LabelJump(loopLabel);
|
||||
|
||||
if (Condition != null)
|
||||
context.Module.AddJumpIfFalse(breakLabel, context.EmitValue(Condition));
|
||||
|
||||
context.Emit(Body);
|
||||
|
||||
context.Module.LabelJump(continueLabel);
|
||||
|
||||
foreach (var incrementor in Incrementors)
|
||||
context.Emit(incrementor);
|
||||
|
||||
context.Module.AddJump(loopLabel);
|
||||
|
||||
context.Module.LabelJump(breakLabel);
|
||||
|
||||
context.PopBreakLabel();
|
||||
context.PopContinueLabel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: edb01f70cd2b7bd4ebce59dfe86e28aa
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,45 @@
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
using UdonSharp.Compiler.Assembly;
|
||||
using UdonSharp.Compiler.Emit;
|
||||
|
||||
namespace UdonSharp.Compiler.Binder
|
||||
{
|
||||
internal sealed class BoundIfStatement : BoundStatement
|
||||
{
|
||||
private BoundExpression ConditionExpression { get; }
|
||||
private BoundStatement BodyStatement { get; }
|
||||
private BoundStatement ElseStatement { get; }
|
||||
|
||||
public BoundIfStatement(SyntaxNode node, BoundExpression conditionExpression, BoundStatement bodyStatement, BoundStatement elseStatement)
|
||||
:base(node)
|
||||
{
|
||||
ConditionExpression = conditionExpression;
|
||||
BodyStatement = bodyStatement;
|
||||
ElseStatement = elseStatement;
|
||||
}
|
||||
|
||||
public override void Emit(EmitContext context)
|
||||
{
|
||||
Value conditionValue = context.EmitValue(ConditionExpression);
|
||||
|
||||
JumpLabel exitLabel = context.Module.CreateLabel();
|
||||
JumpLabel failLabel = context.Module.CreateLabel();
|
||||
|
||||
context.Module.AddJumpIfFalse(failLabel, conditionValue);
|
||||
|
||||
if (BodyStatement != null)
|
||||
context.Emit(BodyStatement);
|
||||
|
||||
if (ElseStatement != null)
|
||||
context.Module.AddJump(exitLabel);
|
||||
|
||||
context.Module.LabelJump(failLabel);
|
||||
|
||||
if (ElseStatement != null)
|
||||
context.Emit(ElseStatement);
|
||||
|
||||
context.Module.LabelJump(exitLabel);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7bc238b740f2a564f954f381c12538cf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,67 @@
|
||||
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using UdonSharp.Compiler.Emit;
|
||||
using UdonSharp.Compiler.Symbols;
|
||||
|
||||
namespace UdonSharp.Compiler.Binder
|
||||
{
|
||||
internal sealed class BoundInterpolatedStringExpression : BoundExpression
|
||||
{
|
||||
private BoundConstantExpression BuiltStr { get; }
|
||||
private BoundExpression[] InterpolationExpressions { get; }
|
||||
|
||||
private ExternMethodSymbol StringFormatMethod { get; }
|
||||
|
||||
public override TypeSymbol ValueType { get; }
|
||||
|
||||
private TypeSymbol ObjectArr { get; }
|
||||
|
||||
public BoundInterpolatedStringExpression(InterpolatedStringExpressionSyntax node, string builtStr, BoundExpression[] interpolatedExpressions, BindContext context)
|
||||
: base(node)
|
||||
{
|
||||
ValueType = context.GetTypeSymbol(SpecialType.System_String);
|
||||
|
||||
BuiltStr = new BoundConstantExpression(new ConstantValue<string>(builtStr), ValueType, node);
|
||||
InterpolationExpressions = interpolatedExpressions;
|
||||
|
||||
ObjectArr = context.GetTypeSymbol(SpecialType.System_Object).MakeArrayType(context);
|
||||
|
||||
if (interpolatedExpressions.Length > 3)
|
||||
{
|
||||
StringFormatMethod = ValueType.GetMembers<ExternMethodSymbol>("Format", context).First(e =>
|
||||
e.Parameters[0].Type == ValueType && e.Parameters[1].Type == ObjectArr);
|
||||
}
|
||||
else
|
||||
{
|
||||
StringFormatMethod = ValueType.GetMembers<ExternMethodSymbol>("Format", context).First(e =>
|
||||
e.Parameters[0].Type == ValueType && e.Parameters.Length == interpolatedExpressions.Length + 1);
|
||||
}
|
||||
}
|
||||
|
||||
public override Value EmitValue(EmitContext context)
|
||||
{
|
||||
BoundInvocationExpression formatInvoke;
|
||||
|
||||
if (InterpolationExpressions.Length > 3)
|
||||
{
|
||||
BoundConstArrayCreationExpression interpolationArray =
|
||||
new BoundConstArrayCreationExpression(SyntaxNode, ObjectArr, InterpolationExpressions);
|
||||
|
||||
BoundAccessExpression arrayAccess =
|
||||
BoundAccessExpression.BindAccess(context.EmitValue(interpolationArray));
|
||||
|
||||
formatInvoke = BoundInvocationExpression.CreateBoundInvocation(context, SyntaxNode, StringFormatMethod,
|
||||
null, new BoundExpression[] { BuiltStr, arrayAccess });
|
||||
}
|
||||
else
|
||||
{
|
||||
formatInvoke = BoundInvocationExpression.CreateBoundInvocation(context, SyntaxNode, StringFormatMethod,
|
||||
null, new BoundExpression[] {BuiltStr}.Concat(InterpolationExpressions).ToArray());
|
||||
}
|
||||
|
||||
return context.EmitValue(formatInvoke);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: daea6d6d4a4f1f54e9b463cc39143799
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,855 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using UdonSharp.Compiler.Assembly;
|
||||
using UdonSharp.Compiler.Emit;
|
||||
using UdonSharp.Compiler.Symbols;
|
||||
using UdonSharp.Compiler.Udon;
|
||||
using UdonSharp.Core;
|
||||
using UdonSharp.Internal;
|
||||
using UdonSharp.Lib.Internal;
|
||||
using UdonSharp.Localization;
|
||||
using UnityEngine;
|
||||
using VRC.Udon;
|
||||
using NotSupportedException = UdonSharp.Core.NotSupportedException;
|
||||
|
||||
namespace UdonSharp.Compiler.Binder
|
||||
{
|
||||
internal abstract class BoundInvocationExpression : BoundExpression
|
||||
{
|
||||
[PublicAPI]
|
||||
public MethodSymbol Method { get; }
|
||||
|
||||
[PublicAPI]
|
||||
public BoundExpression[] ParameterExpressions { get; }
|
||||
|
||||
public override TypeSymbol ValueType => Method.ReturnType;
|
||||
protected bool IsPropertySetter { get; private set; }
|
||||
|
||||
public void MarkPropertySetter()
|
||||
{
|
||||
IsPropertySetter = true;
|
||||
}
|
||||
|
||||
protected BoundInvocationExpression(SyntaxNode node, MethodSymbol method, BoundExpression instanceExpression, BoundExpression[] parameterExpressions)
|
||||
:base(node, instanceExpression)
|
||||
{
|
||||
Method = method;
|
||||
ParameterExpressions = parameterExpressions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks a bound invocation as a base invocation which prevents searching for more derived methods for the call and prevents a virtual call altogether
|
||||
/// </summary>
|
||||
public virtual void MarkForcedBaseCall() {}
|
||||
|
||||
private static readonly HashSet<string> _getComponentNames = new HashSet<string>()
|
||||
{
|
||||
"GetComponent",
|
||||
"GetComponents",
|
||||
"GetComponentInChildren",
|
||||
"GetComponentsInChildren",
|
||||
"GetComponentInParent",
|
||||
"GetComponentsInParent",
|
||||
};
|
||||
|
||||
private static bool TryCreateUdonSharpMetadataInvocation(AbstractPhaseContext context, SyntaxNode node,
|
||||
MethodSymbol symbol, BoundExpression instanceExpression,
|
||||
out BoundInvocationExpression createdInvocation)
|
||||
{
|
||||
if (symbol.Name == "GetUdonTypeID" || symbol.Name == "GetUdonTypeName")
|
||||
{
|
||||
if (symbol.IsStatic &&
|
||||
symbol.TypeArguments.Length == 1 &&
|
||||
symbol.ContainingType == context.GetTypeSymbol(typeof(UdonSharpBehaviour)))
|
||||
{
|
||||
IConstantValue constantValue;
|
||||
TypeSymbol constantType;
|
||||
|
||||
var typeArgs = symbol.TypeArguments.Select(e => context.GetTypeSymbol(e.RoslynSymbol)).ToArray();
|
||||
|
||||
if (symbol.Name == "GetUdonTypeID")
|
||||
{
|
||||
constantValue = new ConstantValue<long>(UdonSharpInternalUtility.GetTypeID(TypeSymbol.GetFullTypeName(typeArgs[0].RoslynSymbol)));
|
||||
constantType = context.GetTypeSymbol(SpecialType.System_Int64);
|
||||
}
|
||||
else
|
||||
{
|
||||
constantValue = new ConstantValue<string>(TypeSymbol.GetFullTypeName(typeArgs[0].RoslynSymbol));
|
||||
constantType = context.GetTypeSymbol(SpecialType.System_String);
|
||||
}
|
||||
|
||||
createdInvocation = new BoundConstantInvocationExpression(node, constantValue, constantType);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!symbol.IsStatic &&
|
||||
instanceExpression != null &&
|
||||
symbol.ContainingType == context.GetTypeSymbol(typeof(UdonSharpBehaviour)))
|
||||
{
|
||||
TypeSymbol methodContainer = context.GetTypeSymbol(typeof(UdonSharpBehaviourMethods));
|
||||
var shimMethod = methodContainer.GetMember<MethodSymbol>(symbol.Name, context);
|
||||
context.MarkSymbolReferenced(shimMethod);
|
||||
|
||||
createdInvocation = CreateBoundInvocation(context, node, shimMethod, null, new [] {instanceExpression});
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
createdInvocation = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static readonly HashSet<Type> _brokenGetComponentTypes = new HashSet<Type>()
|
||||
{
|
||||
typeof(VRC.SDKBase.VRC_AvatarPedestal), typeof(VRC.SDK3.Components.VRCAvatarPedestal),
|
||||
typeof(VRC.SDKBase.VRC_Pickup), typeof(VRC.SDK3.Components.VRCPickup),
|
||||
typeof(VRC.SDKBase.VRC_PortalMarker), typeof(VRC.SDK3.Components.VRCPortalMarker),
|
||||
//typeof(VRC.SDKBase.VRC_MirrorReflection), typeof(VRC.SDK3.Components.VRCMirrorReflection),
|
||||
typeof(VRC.SDKBase.VRCStation),typeof(VRC.SDK3.Components.VRCStation),
|
||||
typeof(VRC.SDK3.Video.Components.VRCUnityVideoPlayer),
|
||||
typeof(VRC.SDK3.Video.Components.AVPro.VRCAVProVideoPlayer),
|
||||
typeof(VRC.SDK3.Video.Components.Base.BaseVRCVideoPlayer),
|
||||
typeof(VRC.SDK3.Components.VRCObjectPool),
|
||||
typeof(VRC.SDK3.Components.VRCObjectSync),
|
||||
typeof(UdonBehaviour),
|
||||
};
|
||||
|
||||
private static bool TryCreateGetComponentInvocation(AbstractPhaseContext context, SyntaxNode node,
|
||||
MethodSymbol symbol, BoundExpression instanceExpression, BoundExpression[] parameterExpressions,
|
||||
out BoundInvocationExpression createdInvocation)
|
||||
{
|
||||
if (symbol.RoslynSymbol != null &&
|
||||
symbol.RoslynSymbol.IsGenericMethod &&
|
||||
symbol.TypeArguments.Length == 1 &&
|
||||
_getComponentNames.Contains(symbol.Name) &&
|
||||
(symbol.ContainingType.UdonType.SystemType == typeof(Component) || symbol.ContainingType.UdonType.SystemType == typeof(GameObject)))
|
||||
{
|
||||
TypeSymbol gameObjectType = context.GetTypeSymbol(typeof(GameObject));
|
||||
TypeSymbol typeArgument = symbol.TypeArguments[0];
|
||||
|
||||
// udon-workaround: Work around the udon bug where it checks the strongbox type instead of variable type and blows up when the strong box is `object`
|
||||
if (instanceExpression.ValueType == gameObjectType)
|
||||
{
|
||||
PropertySymbol accessProperty = gameObjectType.GetMember<PropertySymbol>("transform", context);
|
||||
instanceExpression = BoundAccessExpression.BindAccess(context, node, accessProperty, instanceExpression);
|
||||
}
|
||||
else
|
||||
{
|
||||
PropertySymbol accessProperty = context.GetTypeSymbol(typeof(Component)).GetMember<PropertySymbol>("transform", context);
|
||||
instanceExpression = BoundAccessExpression.BindAccess(context, node, accessProperty, instanceExpression);
|
||||
}
|
||||
|
||||
TypeSymbol udonSharpBehaviourType = context.GetTypeSymbol(typeof(UdonSharpBehaviour));
|
||||
|
||||
// Exact UdonSharpBehaviour type match
|
||||
if (typeArgument == udonSharpBehaviourType)
|
||||
{
|
||||
MethodSymbol getComponentMethodShim = context.GetTypeSymbol(typeof(GetComponentShim))
|
||||
.GetMembers<MethodSymbol>(symbol.Name + "USB", context)
|
||||
.First(e => e.Parameters.Length == parameterExpressions.Length + 1);
|
||||
|
||||
createdInvocation = new BoundStaticUserMethodInvocation(node, getComponentMethodShim,
|
||||
new [] {instanceExpression}.Concat(parameterExpressions).ToArray());
|
||||
|
||||
context.MarkSymbolReferenced(getComponentMethodShim);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Subclass of UdonSharpBehaviour
|
||||
if (typeArgument.IsUdonSharpBehaviour)
|
||||
{
|
||||
// Handle inherited types
|
||||
if (context.CompileContext.HasInheritedUdonSharpBehaviours(typeArgument))
|
||||
{
|
||||
MethodSymbol getComponentInheritedMethodShim = context.GetTypeSymbol(typeof(GetComponentShim))
|
||||
.GetMembers<MethodSymbol>(symbol.Name + "I", context)
|
||||
.First(e => e.Parameters.Length == parameterExpressions.Length + 1);
|
||||
|
||||
getComponentInheritedMethodShim = getComponentInheritedMethodShim.ConstructGenericMethod(context, new [] { typeArgument });
|
||||
|
||||
createdInvocation = new BoundStaticUserMethodInvocation(node, getComponentInheritedMethodShim,
|
||||
new [] {instanceExpression}.Concat(parameterExpressions).ToArray());
|
||||
|
||||
context.MarkSymbolReferenced(getComponentInheritedMethodShim);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MethodSymbol getComponentMethodShim = context.GetTypeSymbol(typeof(GetComponentShim))
|
||||
.GetMembers<MethodSymbol>(symbol.Name, context)
|
||||
.First(e => e.Parameters.Length == parameterExpressions.Length + 1);
|
||||
|
||||
getComponentMethodShim = getComponentMethodShim.ConstructGenericMethod(context, new [] { typeArgument });
|
||||
|
||||
createdInvocation = new BoundStaticUserMethodInvocation(node, getComponentMethodShim,
|
||||
new [] {instanceExpression}.Concat(parameterExpressions).ToArray());
|
||||
|
||||
context.MarkSymbolReferenced(getComponentMethodShim);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_brokenGetComponentTypes.Contains(typeArgument.UdonType.SystemType))
|
||||
{
|
||||
MethodSymbol getComponentInheritedMethodShim = context.GetTypeSymbol(typeof(GetComponentShim))
|
||||
.GetMembers<MethodSymbol>(symbol.Name + "VRC", context)
|
||||
.First(e => e.Parameters.Length == parameterExpressions.Length + 1);
|
||||
|
||||
getComponentInheritedMethodShim = getComponentInheritedMethodShim.ConstructGenericMethod(context, new [] { typeArgument });
|
||||
|
||||
createdInvocation = new BoundStaticUserMethodInvocation(node, getComponentInheritedMethodShim,
|
||||
new [] {instanceExpression}.Concat(parameterExpressions).ToArray());
|
||||
|
||||
context.MarkSymbolReferenced(getComponentInheritedMethodShim);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
createdInvocation = new BoundGetUnityEngineComponentInvocation(context, node, symbol,
|
||||
instanceExpression,
|
||||
parameterExpressions);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
createdInvocation = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TryCreateInstantiationInvocation(AbstractPhaseContext context, SyntaxNode node,
|
||||
MethodSymbol symbol, BoundExpression instanceExpression, BoundExpression[] parameterExpressions,
|
||||
out BoundInvocationExpression createdInvocation)
|
||||
{
|
||||
switch (symbol.Name)
|
||||
{
|
||||
case "Instantiate_Extern" when symbol.ContainingType == context.GetTypeSymbol(typeof(InstantiationShim)):
|
||||
createdInvocation = new BoundExternInvocation(node, context,
|
||||
new ExternSynthesizedMethodSymbol(context,
|
||||
"VRCInstantiate.__Instantiate__UnityEngineGameObject__UnityEngineGameObject",
|
||||
parameterExpressions.Select(e => e.ValueType).ToArray(),
|
||||
context.GetTypeSymbol(typeof(GameObject)), true),
|
||||
instanceExpression, parameterExpressions);
|
||||
|
||||
return true;
|
||||
case "VRCInstantiate" when symbol.ContainingType == context.GetTypeSymbol(typeof(UdonSharpBehaviour)): // Backwards compatibility for UdonSharpBehaviour.VRCInstantiate
|
||||
case "Instantiate" when symbol.ContainingType == context.GetTypeSymbol(typeof(UnityEngine.Object)):
|
||||
{
|
||||
if (symbol.Name != "VRCInstantiate" &&
|
||||
(symbol.TypeArguments.Length != 1 ||
|
||||
symbol.TypeArguments[0] != context.GetTypeSymbol(typeof(GameObject))))
|
||||
throw new NotSupportedException("Udon does not support instantiating non-GameObject types");
|
||||
|
||||
TypeSymbol instantiateShim = context.GetTypeSymbol(typeof(InstantiationShim));
|
||||
MethodSymbol instantiateMethod = instantiateShim.GetMembers<MethodSymbol>("Instantiate", context)
|
||||
.First(e => e.Parameters
|
||||
.Select(p => p.Type)
|
||||
.SequenceEqual(parameterExpressions
|
||||
.Select(p => p.ValueType)));
|
||||
|
||||
context.MarkSymbolReferenced(instantiateMethod);
|
||||
|
||||
createdInvocation = new BoundStaticUserMethodInvocation(node, instantiateMethod, parameterExpressions);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
createdInvocation = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Udon exposes a generic SetProgramVariable which the overload finding will attempt to use and fail to find,
|
||||
/// so just use the non-generic version in this case
|
||||
/// </summary>
|
||||
private static bool TryCreateSetProgramVariableInvocation(AbstractPhaseContext context, SyntaxNode node,
|
||||
MethodSymbol symbol, BoundExpression instanceExpression, BoundExpression[] parameterExpressions,
|
||||
out BoundInvocationExpression createdInvocation)
|
||||
{
|
||||
if (symbol.Name == "SetProgramVariable" &&
|
||||
symbol.ContainingType == context.GetTypeSymbol(typeof(UdonBehaviour)))
|
||||
{
|
||||
MethodSymbol setProgramVarObjMethod = context.GetTypeSymbol(typeof(UdonBehaviour))
|
||||
.GetMembers<MethodSymbol>("SetProgramVariable", context)
|
||||
.First(e => !e.RoslynSymbol.IsGenericMethod);
|
||||
|
||||
createdInvocation = new BoundExternInvocation(node, context, setProgramVarObjMethod, instanceExpression,
|
||||
parameterExpressions);
|
||||
return true;
|
||||
}
|
||||
|
||||
createdInvocation = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TryCreateArrayMethodInvocation(AbstractPhaseContext context, SyntaxNode node,
|
||||
MethodSymbol symbol, BoundExpression instanceExpression, BoundExpression[] parameterExpressions,
|
||||
out BoundInvocationExpression createdInvocation)
|
||||
{
|
||||
if ((symbol.Name == "IndexOf" || symbol.Name == "BinarySearch" || symbol.Name == "LastIndexOf" || symbol.Name == "Reverse") &&
|
||||
symbol.ContainingType == context.GetTypeSymbol(typeof(Array)))
|
||||
{
|
||||
MethodSymbol arrayMethod = context.GetTypeSymbol(typeof(Array))
|
||||
.GetMembers<MethodSymbol>(symbol.Name, context)
|
||||
.First(e => !e.RoslynSymbol.IsGenericMethod && e.Parameters.Length == symbol.Parameters.Length);
|
||||
|
||||
createdInvocation = new BoundExternInvocation(node, context, arrayMethod, instanceExpression,
|
||||
parameterExpressions);
|
||||
return true;
|
||||
}
|
||||
|
||||
createdInvocation = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TryCreateTMPMethodInvocation(AbstractPhaseContext context, SyntaxNode node,
|
||||
MethodSymbol symbol, BoundExpression instanceExpression, BoundExpression[] parameterExpressions,
|
||||
out BoundInvocationExpression createdInvocation)
|
||||
{
|
||||
if (symbol.ContainingType != null &&
|
||||
symbol.ContainingType.ToString() == "TMPro.TMP_Text")
|
||||
{
|
||||
createdInvocation = new BoundExternInvocation(node, context,
|
||||
new ExternSynthesizedMethodSymbol(context, symbol.Name, instanceExpression.ValueType, symbol.Parameters.Select(e => e.Type).ToArray(), symbol.ReturnType, symbol.IsStatic),
|
||||
instanceExpression,
|
||||
parameterExpressions);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
createdInvocation = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TryCreateBaseEnumMethodInvocation(AbstractPhaseContext context, SyntaxNode node,
|
||||
MethodSymbol symbol, BoundExpression instanceExpression, BoundExpression[] parameterExpressions,
|
||||
out BoundInvocationExpression createdInvocation)
|
||||
{
|
||||
if ((symbol.Name == "ToString" || symbol.Name == "GetHashCode" || symbol.Name == "Equals") &&
|
||||
symbol.ContainingType != null &&
|
||||
symbol.ContainingType == context.GetTypeSymbol(SpecialType.System_Enum))
|
||||
{
|
||||
createdInvocation = new BoundExternInvocation(node, context,
|
||||
context.GetTypeSymbol(SpecialType.System_Object).GetMember<MethodSymbol>(symbol.Name, context),
|
||||
instanceExpression,
|
||||
parameterExpressions);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
createdInvocation = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TryCreateCompareToInvocation(AbstractPhaseContext context, SyntaxNode node,
|
||||
MethodSymbol symbol, BoundExpression instanceExpression, BoundExpression[] parameterExpressions,
|
||||
out BoundInvocationExpression createdInvocation)
|
||||
{
|
||||
if (symbol.Name == "CompareTo" &&
|
||||
symbol.ContainingType != null &&
|
||||
symbol.ContainingType == context.GetTypeSymbol(typeof(IComparable)))
|
||||
{
|
||||
createdInvocation = new BoundExternInvocation(node, context,
|
||||
new ExternSynthesizedMethodSymbol(context, "CompareTo", instanceExpression.ValueType,
|
||||
new [] { instanceExpression.ValueType },
|
||||
context.GetTypeSymbol(SpecialType.System_Int32), false),
|
||||
instanceExpression, parameterExpressions);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
createdInvocation = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TryCreateShimInvocation(AbstractPhaseContext context, SyntaxNode node,
|
||||
MethodSymbol symbol, BoundExpression instanceExpression, BoundExpression[] parameterExpressions,
|
||||
out BoundInvocationExpression createdInvocation)
|
||||
{
|
||||
if (TryCreateUdonSharpMetadataInvocation(context, node, symbol, instanceExpression, out createdInvocation))
|
||||
return true;
|
||||
|
||||
if (TryCreateGetComponentInvocation(context, node, symbol, instanceExpression, parameterExpressions, out createdInvocation))
|
||||
return true;
|
||||
|
||||
if (TryCreateInstantiationInvocation(context, node, symbol, instanceExpression, parameterExpressions, out createdInvocation))
|
||||
return true;
|
||||
|
||||
if (TryCreateSetProgramVariableInvocation(context, node, symbol, instanceExpression, parameterExpressions, out createdInvocation))
|
||||
return true;
|
||||
|
||||
if (TryCreateArrayMethodInvocation(context, node, symbol, instanceExpression, parameterExpressions, out createdInvocation))
|
||||
return true;
|
||||
|
||||
if (TryCreateTMPMethodInvocation(context, node, symbol, instanceExpression, parameterExpressions, out createdInvocation))
|
||||
return true;
|
||||
|
||||
if (TryCreateBaseEnumMethodInvocation(context, node, symbol, instanceExpression, parameterExpressions, out createdInvocation))
|
||||
return true;
|
||||
|
||||
if (TryCreateCompareToInvocation(context, node, symbol, instanceExpression, parameterExpressions, out createdInvocation))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
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) &&
|
||||
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 void ReleaseCowValuesImpl(EmitContext context)
|
||||
{
|
||||
if (ParameterExpressions == null)
|
||||
return;
|
||||
|
||||
foreach (BoundExpression parameterExpression in ParameterExpressions)
|
||||
{
|
||||
parameterExpression.ReleaseCowReferences(context);
|
||||
}
|
||||
}
|
||||
|
||||
protected Value[] GetParameterValues(EmitContext context)
|
||||
{
|
||||
Value.CowValue[] parameterCows = context.GetExpressionCowValues(this, "parameters");
|
||||
|
||||
if (parameterCows != null)
|
||||
return parameterCows.Select(e => e.Value).ToArray();
|
||||
|
||||
parameterCows = new Value.CowValue[ParameterExpressions.Length];
|
||||
|
||||
using (context.InterruptAssignmentScope())
|
||||
{
|
||||
for (int i = 0; i < parameterCows.Length; ++i)
|
||||
parameterCows[i] = context.EmitValueWithDeferredRelease(ParameterExpressions[i])
|
||||
.GetCowValue(context);
|
||||
}
|
||||
|
||||
context.RegisterCowValues(parameterCows, this, "parameters");
|
||||
|
||||
Value[] parameterValues = new Value[ParameterExpressions.Length];
|
||||
|
||||
for (int i = 0; i < parameterValues.Length; ++i)
|
||||
parameterValues[i] = parameterCows[i].Value;
|
||||
|
||||
return parameterValues;
|
||||
}
|
||||
|
||||
protected Value.CowValue GetInstanceValue(EmitContext context)
|
||||
{
|
||||
Value.CowValue[] instanceValue = context.GetExpressionCowValues(this, "instance");
|
||||
|
||||
if (instanceValue == null)
|
||||
{
|
||||
using (context.InterruptAssignmentScope())
|
||||
instanceValue = new[] {context.EmitValue(SourceExpression).GetCowValue(context)};
|
||||
|
||||
context.RegisterCowValues(instanceValue, this, "instance");
|
||||
}
|
||||
|
||||
return instanceValue[0];
|
||||
}
|
||||
|
||||
protected void CheckStackSize(Value valueCount, EmitContext context)
|
||||
{
|
||||
using (context.InterruptAssignmentScope())
|
||||
{
|
||||
Value stack = context.RecursiveStackValue;
|
||||
BoundAccessExpression stackAccess = BoundAccessExpression.BindAccess(stack);
|
||||
Value stackAddr = context.RecursiveStackAddressValue;
|
||||
BoundAccessExpression stackAddrAccess = BoundAccessExpression.BindAccess(stackAddr);
|
||||
|
||||
TypeSymbol arrayType = context.GetTypeSymbol(SpecialType.System_Array);
|
||||
|
||||
context.Module.AddCommentTag("Stack size check");
|
||||
|
||||
// Check stack size and double it if it's not enough
|
||||
// We know that doubling once will always be enough since the default size of the stack is the max number of stack values pushed in any method
|
||||
PropertySymbol arraySizeProperty = arrayType.GetMember<PropertySymbol>("Length", context);
|
||||
|
||||
TypeSymbol intType = context.GetTypeSymbol(SpecialType.System_Int32);
|
||||
|
||||
Value arraySize =
|
||||
context.EmitValue(BoundAccessExpression.BindAccess(context, SyntaxNode, arraySizeProperty,
|
||||
stackAccess));
|
||||
BoundAccessExpression arraySizeAccess = BoundAccessExpression.BindAccess(arraySize);
|
||||
|
||||
Value targetSize = context.EmitValue(CreateBoundInvocation(context, SyntaxNode,
|
||||
new ExternSynthesizedOperatorSymbol(BuiltinOperatorType.Addition, intType, context), null,
|
||||
new BoundExpression[] { stackAddrAccess, BoundAccessExpression.BindAccess(valueCount) }));
|
||||
|
||||
Value isSizeGreaterThan = context.EmitValue(CreateBoundInvocation(context, SyntaxNode,
|
||||
new ExternSynthesizedOperatorSymbol(BuiltinOperatorType.GreaterThanOrEqual, intType, context), null,
|
||||
new BoundExpression[]
|
||||
{
|
||||
BoundAccessExpression.BindAccess(targetSize),
|
||||
arraySizeAccess,
|
||||
}));
|
||||
|
||||
JumpLabel skipResizeLabel = context.Module.CreateLabel();
|
||||
|
||||
context.Module.AddJumpIfFalse(skipResizeLabel, isSizeGreaterThan);
|
||||
|
||||
// Resize logic
|
||||
Value constantTwo = context.GetConstantValue(intType, 2);
|
||||
Value newSize = context.EmitValue(CreateBoundInvocation(context, SyntaxNode,
|
||||
new ExternSynthesizedOperatorSymbol(BuiltinOperatorType.Multiplication, intType, context), null,
|
||||
new BoundExpression[]
|
||||
{
|
||||
arraySizeAccess,
|
||||
BoundAccessExpression.BindAccess(constantTwo),
|
||||
}));
|
||||
|
||||
Value newArray = context.EmitValue(new BoundArrayCreationExpression(SyntaxNode, context,
|
||||
context.GetTypeSymbol(SpecialType.System_Object).MakeArrayType(context),
|
||||
new BoundExpression[] { BoundAccessExpression.BindAccess(newSize) }, null));
|
||||
|
||||
MethodSymbol arrayCopyMethod = arrayType.GetMembers<MethodSymbol>("Copy", context)
|
||||
.First(e => e.Parameters.Length == 3 && e.Parameters[2].Type == intType);
|
||||
|
||||
context.Emit(CreateBoundInvocation(context, null, arrayCopyMethod, null,
|
||||
new BoundExpression[]
|
||||
{
|
||||
stackAccess,
|
||||
BoundAccessExpression.BindAccess(newArray),
|
||||
BoundAccessExpression.BindAccess(arraySize)
|
||||
}));
|
||||
|
||||
context.Module.AddCopy(newArray, stack);
|
||||
|
||||
context.Module.LabelJump(skipResizeLabel);
|
||||
|
||||
context.Module.AddCommentTag("Stack size check end");
|
||||
}
|
||||
}
|
||||
|
||||
protected void PushRecursiveValues(Value[] values, EmitContext context)
|
||||
{
|
||||
if (values.Length == 0)
|
||||
return;
|
||||
|
||||
Value stack = context.RecursiveStackValue;
|
||||
BoundAccessExpression stackAccess = BoundAccessExpression.BindAccess(stack);
|
||||
Value stackAddr = context.RecursiveStackAddressValue;
|
||||
BoundAccessExpression stackAddrAccess = BoundAccessExpression.BindAccess(stackAddr);
|
||||
|
||||
context.Module.AddCommentTag("Recursive stack push");
|
||||
|
||||
// Now we start copying values over to the stack
|
||||
BoundInvocationExpression incrementExpression = new BoundPrefixOperatorExpression(context, SyntaxNode,
|
||||
stackAddrAccess, new ExternSynthesizedOperatorSymbol(BuiltinOperatorType.Addition, context.GetTypeSymbol(SpecialType.System_Int32), context));
|
||||
|
||||
foreach (var valueToPush in values)
|
||||
{
|
||||
BoundArrayAccessExpression arraySet = new BoundArrayAccessExpression(null, context, stackAccess,
|
||||
new BoundExpression[] { stackAddrAccess });
|
||||
|
||||
context.EmitSet(arraySet, BoundAccessExpression.BindAccess(valueToPush));
|
||||
|
||||
context.Emit(incrementExpression);
|
||||
}
|
||||
|
||||
context.Module.AddCommentTag("Recursive stack push end");
|
||||
}
|
||||
|
||||
protected void PopRecursiveValues(Value[] values, EmitContext context)
|
||||
{
|
||||
if (values.Length == 0)
|
||||
return;
|
||||
|
||||
Value stack = context.RecursiveStackValue;
|
||||
Value stackAddr = context.RecursiveStackAddressValue;
|
||||
BoundAccessExpression stackAddrAccess = BoundAccessExpression.BindAccess(stackAddr);
|
||||
TypeSymbol intType = context.GetTypeSymbol(SpecialType.System_Int32);
|
||||
TypeSymbol objectType = context.GetTypeSymbol(SpecialType.System_Object);
|
||||
TypeSymbol objectArrayType = objectType.MakeArrayType(context);
|
||||
|
||||
context.Module.AddCommentTag("Recursive stack pop");
|
||||
|
||||
BoundInvocationExpression decrementExpression = new BoundPrefixOperatorExpression(context, SyntaxNode,
|
||||
stackAddrAccess, new ExternSynthesizedOperatorSymbol(BuiltinOperatorType.Subtraction, intType, context));
|
||||
|
||||
foreach (var valueToPop in values.Reverse())
|
||||
{
|
||||
context.Emit(decrementExpression);
|
||||
|
||||
ExternSynthesizedMethodSymbol arrayGetMethod = new ExternSynthesizedMethodSymbol(context, "Get",
|
||||
objectArrayType, new [] { intType }, objectType, false);
|
||||
|
||||
context.Module.AddPush(stack);
|
||||
context.Module.AddPush(stackAddr);
|
||||
context.Module.AddPush(valueToPop);
|
||||
context.Module.AddExtern(arrayGetMethod);
|
||||
}
|
||||
|
||||
context.Module.AddCommentTag("Recursive stack pop end");
|
||||
}
|
||||
|
||||
private sealed class BoundBuiltinOperatorInvocationExpression : BoundExternInvocation
|
||||
{
|
||||
public BoundBuiltinOperatorInvocationExpression(SyntaxNode node, AbstractPhaseContext context, MethodSymbol method, BoundExpression[] operandExpressions)
|
||||
:base(node, context, method, null, operandExpressions)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class BoundCompoundAssignmentExpression : BoundInvocationExpression
|
||||
{
|
||||
private BoundAccessExpression TargetExpression { get; }
|
||||
private BoundExpression AssignmentSource { get; }
|
||||
private MethodSymbol OperatorMethod { get; }
|
||||
|
||||
public BoundCompoundAssignmentExpression(AbstractPhaseContext context, SyntaxNode node, BoundAccessExpression assignmentTarget, MethodSymbol operatorMethod, BoundExpression assignmentSource)
|
||||
: base(node, null, null, null)
|
||||
{
|
||||
TargetExpression = assignmentTarget;
|
||||
AssignmentSource = assignmentSource;
|
||||
OperatorMethod = operatorMethod;
|
||||
}
|
||||
|
||||
public override TypeSymbol ValueType => TargetExpression.ValueType;
|
||||
|
||||
public override Value EmitValue(EmitContext context)
|
||||
{
|
||||
Value targetValue =
|
||||
context.EmitValueWithDeferredRelease(TargetExpression);
|
||||
|
||||
var invocation = CreateBoundInvocation(context, null, OperatorMethod, null,
|
||||
new[] {BoundAccessExpression.BindAccess(targetValue), AssignmentSource});
|
||||
|
||||
Value setResult;
|
||||
|
||||
if (TargetExpression.ValueType != OperatorMethod.ReturnType)
|
||||
setResult = context.EmitSet(TargetExpression, new BoundCastExpression(null, invocation, ValueType, true));
|
||||
else
|
||||
setResult = context.EmitSet(TargetExpression, invocation);
|
||||
|
||||
return setResult;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class BoundGetUnityEngineComponentInvocation : BoundExternInvocation
|
||||
{
|
||||
public override TypeSymbol ValueType { get; }
|
||||
|
||||
public BoundGetUnityEngineComponentInvocation(AbstractPhaseContext context, SyntaxNode node, MethodSymbol methodSymbol, BoundExpression sourceExpression, BoundExpression[] parametersExpressions)
|
||||
: base(node, context, BuildMethod(context, methodSymbol), sourceExpression, GetParameterExpressions(context, methodSymbol, parametersExpressions))
|
||||
{
|
||||
ValueType = methodSymbol.TypeArguments[0];
|
||||
|
||||
if (methodSymbol.ReturnType.IsArray)
|
||||
ValueType = ValueType.MakeArrayType(context);
|
||||
}
|
||||
|
||||
private static BoundExpression[] GetParameterExpressions(AbstractPhaseContext context, MethodSymbol symbol, BoundExpression[] parameters)
|
||||
{
|
||||
BoundExpression typeExpression = new BoundConstantExpression(
|
||||
symbol.TypeArguments[0].UdonType.SystemType,
|
||||
context.GetTypeSymbol(typeof(Type)));
|
||||
|
||||
if (parameters == null || parameters.Length == 0)
|
||||
return new [] { typeExpression };
|
||||
|
||||
return parameters.Concat(new []{typeExpression}).ToArray();
|
||||
}
|
||||
|
||||
private static MethodSymbol BuildMethod(AbstractPhaseContext context, MethodSymbol methodSymbol)
|
||||
{
|
||||
string methodName = methodSymbol.Name;
|
||||
string returnName;
|
||||
|
||||
if (methodSymbol.ReturnType.IsArray)
|
||||
returnName = "__TArray";
|
||||
else
|
||||
returnName = "__T";
|
||||
|
||||
string paramStr = "";
|
||||
|
||||
if (methodSymbol.Parameters.Length > 0)
|
||||
paramStr = "__SystemBoolean";
|
||||
|
||||
string methodIdentifier = $"UnityEngineComponent.__{methodName}{paramStr}{returnName}";
|
||||
|
||||
var roslynSymbol = methodSymbol.RoslynSymbol;
|
||||
|
||||
return new ExternSynthesizedMethodSymbol(context, methodIdentifier,
|
||||
roslynSymbol.Parameters.Select(e => context.GetTypeSymbol(e.Type)).ToArray(),
|
||||
context.GetTypeSymbol(roslynSymbol.ReturnType), false);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class BoundPostfixOperatorExpression : BoundInvocationExpression
|
||||
{
|
||||
private BoundAccessExpression TargetExpression { get; }
|
||||
private BoundInvocationExpression InternalExpression { get; }
|
||||
|
||||
public BoundPostfixOperatorExpression(AbstractPhaseContext context, SyntaxNode node, BoundAccessExpression assignmentTarget, MethodSymbol operatorMethod)
|
||||
: base(node, null, null, null)
|
||||
{
|
||||
TargetExpression = assignmentTarget;
|
||||
Type targetType = TargetExpression.ValueType.UdonType.SystemType;
|
||||
IConstantValue incrementValue = (IConstantValue) Activator.CreateInstance(
|
||||
typeof(ConstantValue<>).MakeGenericType(targetType), Convert.ChangeType(1, targetType));
|
||||
|
||||
InternalExpression = CreateBoundInvocation(context, null, operatorMethod, null,
|
||||
new BoundExpression[] {assignmentTarget, new BoundConstantExpression(incrementValue, TargetExpression.ValueType, node)});
|
||||
}
|
||||
|
||||
public override TypeSymbol ValueType => TargetExpression.ValueType;
|
||||
|
||||
public override Value EmitValue(EmitContext context)
|
||||
{
|
||||
Value returnValue = context.GetReturnValue(TargetExpression.ValueType);
|
||||
|
||||
context.EmitValueAssignment(returnValue, TargetExpression);
|
||||
|
||||
Type targetType = TargetExpression.ValueType.UdonType.SystemType;
|
||||
IConstantValue incrementValue = (IConstantValue) Activator.CreateInstance(
|
||||
typeof(ConstantValue<>).MakeGenericType(targetType), Convert.ChangeType(1, targetType));
|
||||
BoundExpression expression = CreateBoundInvocation(context, null, InternalExpression.Method, null,
|
||||
new BoundExpression[] {BoundAccessExpression.BindAccess(returnValue), new BoundConstantExpression(incrementValue, TargetExpression.ValueType, SyntaxNode)});
|
||||
|
||||
if (InternalExpression.Method.ReturnType != TargetExpression.ValueType)
|
||||
expression = new BoundCastExpression(null, expression, ValueType, true);
|
||||
|
||||
context.EmitSet(TargetExpression, expression);
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If we aren't requesting a value, we can just direct assign.
|
||||
/// This helps keep increments on stuff like loops with i++ fast
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
public override void Emit(EmitContext context)
|
||||
{
|
||||
if (InternalExpression.Method.ReturnType != TargetExpression.ValueType)
|
||||
context.EmitSet(TargetExpression, new BoundCastExpression(null, InternalExpression, ValueType, true));
|
||||
else
|
||||
context.EmitSet(TargetExpression, InternalExpression);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class BoundPrefixOperatorExpression : BoundInvocationExpression
|
||||
{
|
||||
private BoundAccessExpression TargetExpression { get; }
|
||||
private BoundInvocationExpression InternalExpression { get; }
|
||||
|
||||
public BoundPrefixOperatorExpression(AbstractPhaseContext context, SyntaxNode node, BoundAccessExpression assignmentTarget, MethodSymbol operatorMethod)
|
||||
: base(node, null, null, null)
|
||||
{
|
||||
TargetExpression = assignmentTarget;
|
||||
Type targetType = TargetExpression.ValueType.UdonType.SystemType;
|
||||
IConstantValue incrementValue = (IConstantValue) Activator.CreateInstance(
|
||||
typeof(ConstantValue<>).MakeGenericType(targetType), Convert.ChangeType(1, targetType));
|
||||
|
||||
InternalExpression = CreateBoundInvocation(context, null, operatorMethod, null,
|
||||
new BoundExpression[] {assignmentTarget, new BoundConstantExpression(incrementValue, TargetExpression.ValueType, node)});
|
||||
}
|
||||
|
||||
public override TypeSymbol ValueType => TargetExpression.ValueType;
|
||||
|
||||
public override Value EmitValue(EmitContext context)
|
||||
{
|
||||
if (InternalExpression.Method.ReturnType != TargetExpression.ValueType)
|
||||
return context.EmitSet(TargetExpression, new BoundCastExpression(null, InternalExpression, ValueType, true));
|
||||
|
||||
return context.EmitSet(TargetExpression, InternalExpression);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class BoundConstantInvocationExpression : BoundInvocationExpression
|
||||
{
|
||||
private IConstantValue Constant { get; }
|
||||
|
||||
public override IConstantValue ConstantValue => Constant;
|
||||
|
||||
public override TypeSymbol ValueType { get; }
|
||||
|
||||
public BoundConstantInvocationExpression(SyntaxNode node, IConstantValue constantValue, TypeSymbol constantValType)
|
||||
:base(node, null, null, Array.Empty<BoundExpression>())
|
||||
{
|
||||
Constant = constantValue;
|
||||
ValueType = constantValType;
|
||||
}
|
||||
|
||||
public override Value EmitValue(EmitContext context)
|
||||
{
|
||||
Value returnVal = context.GetReturnValue(ValueType);
|
||||
|
||||
context.EmitValueAssignment(returnVal,
|
||||
BoundAccessExpression.BindAccess(context.GetConstantValue(ValueType, ConstantValue.Value)));
|
||||
|
||||
return returnVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 45b01b776c06154438ab739241a968fa
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,22 @@
|
||||
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using UdonSharp.Compiler.Emit;
|
||||
|
||||
namespace UdonSharp.Compiler.Binder
|
||||
{
|
||||
internal sealed class BoundLocalDeclarationStatement : BoundStatement
|
||||
{
|
||||
private BoundVariableDeclarationStatement DeclarationStatement { get; }
|
||||
|
||||
public BoundLocalDeclarationStatement(LocalDeclarationStatementSyntax node, BoundVariableDeclarationStatement declarationStatement)
|
||||
: base(node)
|
||||
{
|
||||
DeclarationStatement = declarationStatement;
|
||||
}
|
||||
|
||||
public override void Emit(EmitContext context)
|
||||
{
|
||||
context.Emit(DeclarationStatement);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 99ab1957216e3a14a907ce6428284c0e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,24 @@
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
using UdonSharp.Compiler.Emit;
|
||||
|
||||
namespace UdonSharp.Compiler.Binder
|
||||
{
|
||||
/// <summary>
|
||||
/// Base for bound expressions and statements, largely mirroring the Roslyn internal layout for the compiler
|
||||
/// </summary>
|
||||
internal abstract class BoundNode
|
||||
{
|
||||
public SyntaxNode SyntaxNode { get; }
|
||||
|
||||
protected BoundNode(SyntaxNode node)
|
||||
{
|
||||
SyntaxNode = node;
|
||||
}
|
||||
|
||||
public virtual void Emit(EmitContext context)
|
||||
{
|
||||
throw new System.NotImplementedException($"Emit is not implemented on {GetType()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 81ccd6272f612a642bddbe21f975e0fe
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,263 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using UdonSharp.Compiler.Emit;
|
||||
using UdonSharp.Compiler.Symbols;
|
||||
using UnityEngine;
|
||||
using VRC.Udon.Common.Interfaces;
|
||||
|
||||
namespace UdonSharp.Compiler.Binder
|
||||
{
|
||||
internal abstract class BoundPropertyAccessExpression : BoundAccessExpression
|
||||
{
|
||||
protected PropertySymbol Property { get; }
|
||||
|
||||
private BoundExpression[] ParameterExpressions { get; }
|
||||
|
||||
public override TypeSymbol ValueType => Property.Type;
|
||||
|
||||
private bool _isBaseCall;
|
||||
public override void MarkForcedBaseCall() => _isBaseCall = true;
|
||||
|
||||
protected BoundPropertyAccessExpression(AbstractPhaseContext context, SyntaxNode node, PropertySymbol property, BoundExpression sourceExpression, BoundExpression[] parameterExpressions)
|
||||
: base(node, sourceExpression)
|
||||
{
|
||||
Property = property;
|
||||
ParameterExpressions = parameterExpressions;
|
||||
}
|
||||
|
||||
private BoundExpression[] GetParameters(EmitContext context, BoundExpression valueExpression = null)
|
||||
{
|
||||
using (context.InterruptAssignmentScope())
|
||||
{
|
||||
Value.CowValue[] propertyParams = context.GetExpressionCowValues(this, "propertyParams");
|
||||
|
||||
if (propertyParams == null)
|
||||
{
|
||||
if (ParameterExpressions != null)
|
||||
{
|
||||
propertyParams = new Value.CowValue[ParameterExpressions.Length];
|
||||
|
||||
for (int i = 0; i < propertyParams.Length; ++i)
|
||||
propertyParams[i] = context.EmitValue(ParameterExpressions[i]).GetCowValue(context);
|
||||
}
|
||||
else
|
||||
{
|
||||
propertyParams = Array.Empty<Value.CowValue>();
|
||||
}
|
||||
|
||||
context.RegisterCowValues(propertyParams, this, "propertyParams");
|
||||
}
|
||||
|
||||
List<BoundExpression> expressions = new List<BoundExpression>();
|
||||
expressions.AddRange(propertyParams.Select(BindAccess));
|
||||
if (valueExpression != null)
|
||||
expressions.Add(valueExpression);
|
||||
|
||||
return expressions.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
private BoundExpression GetInstanceExpression(EmitContext context)
|
||||
{
|
||||
if (SourceExpression == null)
|
||||
return null;
|
||||
|
||||
if (SourceExpression.IsThis)
|
||||
return SourceExpression;
|
||||
|
||||
Value.CowValue[] instance = context.GetExpressionCowValues(this, "propertyInstance");
|
||||
|
||||
if (instance == null)
|
||||
{
|
||||
using (context.InterruptAssignmentScope())
|
||||
instance = new[] {context.EmitValue(SourceExpression).GetCowValue(context)};
|
||||
context.RegisterCowValues(instance, this, "propertyInstance");
|
||||
}
|
||||
|
||||
return BindAccess(instance[0]);
|
||||
}
|
||||
|
||||
public override Value EmitValue(EmitContext context)
|
||||
{
|
||||
BoundInvocationExpression invocationExpression = BoundInvocationExpression.CreateBoundInvocation(context, SyntaxNode,
|
||||
Property.GetMethod,
|
||||
GetInstanceExpression(context), GetParameters(context));
|
||||
|
||||
if (_isBaseCall)
|
||||
invocationExpression.MarkForcedBaseCall();
|
||||
|
||||
return context.EmitValue(invocationExpression);
|
||||
}
|
||||
|
||||
public override Value EmitSet(EmitContext context, BoundExpression valueExpression)
|
||||
{
|
||||
BoundExpression instanceValue = GetInstanceExpression(context);
|
||||
|
||||
BoundInvocationExpression invocationExpression = BoundInvocationExpression.CreateBoundInvocation(context, SyntaxNode,
|
||||
Property.SetMethod,
|
||||
instanceValue, GetParameters(context, valueExpression));
|
||||
|
||||
if (_isBaseCall)
|
||||
invocationExpression.MarkForcedBaseCall();
|
||||
|
||||
invocationExpression.MarkPropertySetter();
|
||||
|
||||
Value resultVal = context.EmitValue(invocationExpression);
|
||||
|
||||
if (resultVal == null)
|
||||
throw new NullReferenceException();
|
||||
|
||||
if (instanceValue != null &&
|
||||
SourceExpression.ValueType.IsValueType &&
|
||||
SourceExpression is BoundArrayAccessExpression sourceAccessExpression)
|
||||
{
|
||||
context.EmitSet(sourceAccessExpression, instanceValue);
|
||||
}
|
||||
|
||||
return resultVal;
|
||||
}
|
||||
|
||||
private static readonly HashSet<Type> _allowedConstantPropertyTypes = new HashSet<Type>()
|
||||
{
|
||||
typeof(Vector2),
|
||||
typeof(Vector3),
|
||||
typeof(Vector4),
|
||||
typeof(Quaternion),
|
||||
typeof(Matrix4x4),
|
||||
typeof(Color),
|
||||
typeof(Mathf),
|
||||
typeof(Rect),
|
||||
};
|
||||
|
||||
public static BoundAccessExpression BindPropertyAccess(AbstractPhaseContext context, SyntaxNode node, PropertySymbol propertySymbol, BoundExpression sourceExpression, BoundExpression[] parameterExpressions = null)
|
||||
{
|
||||
if (propertySymbol is ExternPropertySymbol externProperty)
|
||||
{
|
||||
Type propertyType = externProperty.ContainingType.UdonType.SystemType;
|
||||
|
||||
if (_allowedConstantPropertyTypes.Contains(propertyType))
|
||||
{
|
||||
PropertyInfo property = propertyType.GetProperty(externProperty.Name,
|
||||
BindingFlags.Static | BindingFlags.Public);
|
||||
|
||||
if (property != null)
|
||||
return new BoundConstantExpression(property.GetValue(null), propertySymbol.Type);
|
||||
}
|
||||
|
||||
if (propertySymbol.ToString() == "UnityEngine.Behaviour.enabled")
|
||||
return new BoundEnabledPropertyExternAccessExpression(context, node, sourceExpression);
|
||||
|
||||
if (propertySymbol.ContainingType.ToString() == "TMPro.TMP_Text")
|
||||
return new BoundTMPPropertyExternAccessExpression(context, node, externProperty, sourceExpression);
|
||||
|
||||
return new BoundExternPropertyAccessExpression(context, node, externProperty, sourceExpression, parameterExpressions);
|
||||
}
|
||||
|
||||
return new BoundUserPropertyAccessExpression(context, node, propertySymbol, sourceExpression, parameterExpressions);
|
||||
}
|
||||
|
||||
private sealed class BoundUserPropertyAccessExpression : BoundPropertyAccessExpression
|
||||
{
|
||||
public BoundUserPropertyAccessExpression(AbstractPhaseContext context, SyntaxNode node, PropertySymbol property, BoundExpression sourceExpression, BoundExpression[] parameterExpressions)
|
||||
:base(context, node, property, sourceExpression, parameterExpressions)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class BoundExternPropertyAccessExpression : BoundPropertyAccessExpression
|
||||
{
|
||||
public BoundExternPropertyAccessExpression(AbstractPhaseContext context, SyntaxNode node, ExternPropertySymbol property, BoundExpression sourceExpression, BoundExpression[] parameterExpressions)
|
||||
: base(context, node, property, sourceExpression, parameterExpressions)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Optimize self refs to gameObject and transform since Udon supplies them
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <returns></returns>
|
||||
private Value TryHandleUnityEngineSelfReference(EmitContext context)
|
||||
{
|
||||
string propertyStr = Property.ToString();
|
||||
if (propertyStr != "UnityEngine.Component.transform" &&
|
||||
propertyStr != "UnityEngine.Component.gameObject")
|
||||
return null;
|
||||
|
||||
if (SourceExpression != null && !SourceExpression.IsThis)
|
||||
return null;
|
||||
|
||||
return context.GetUdonThisValue(Property.Type);
|
||||
}
|
||||
|
||||
public override Value EmitValue(EmitContext context)
|
||||
{
|
||||
Value returnValue = TryHandleUnityEngineSelfReference(context);
|
||||
|
||||
if (returnValue != null)
|
||||
return returnValue;
|
||||
|
||||
return base.EmitValue(context);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles how Udon does not expose the base Behaviour.enabled, but exposes the derived versions
|
||||
/// </summary>
|
||||
private sealed class BoundEnabledPropertyExternAccessExpression : BoundPropertyAccessExpression
|
||||
{
|
||||
public override TypeSymbol ValueType { get; }
|
||||
|
||||
public BoundEnabledPropertyExternAccessExpression(AbstractPhaseContext context, SyntaxNode node, BoundExpression sourceExpression)
|
||||
: base(context, node, BuildProperty(context, sourceExpression), sourceExpression, null)
|
||||
{
|
||||
ValueType = context.GetTypeSymbol(SpecialType.System_Boolean);
|
||||
}
|
||||
|
||||
private static PropertySymbol BuildProperty(AbstractPhaseContext context, BoundExpression sourceExpression)
|
||||
{
|
||||
TypeSymbol propertyType = sourceExpression.ValueType;
|
||||
|
||||
if (propertyType.UdonType.ExternSignature == "VRCUdonUdonBehaviour")
|
||||
propertyType = context.GetTypeSymbol(typeof(IUdonEventReceiver));
|
||||
|
||||
TypeSymbol boolType = context.GetTypeSymbol(SpecialType.System_Boolean);
|
||||
MethodSymbol setMethod = new ExternSynthesizedMethodSymbol(context, "set_enabled", propertyType,
|
||||
new[] {boolType}, null, false);
|
||||
MethodSymbol getMethod = new ExternSynthesizedMethodSymbol(context, "get_enabled", propertyType,
|
||||
new TypeSymbol[] {}, boolType, false);
|
||||
|
||||
return new SynthesizedPropertySymbol(context, getMethod, setMethod);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class BoundTMPPropertyExternAccessExpression : BoundPropertyAccessExpression
|
||||
{
|
||||
public override TypeSymbol ValueType { get; }
|
||||
|
||||
public BoundTMPPropertyExternAccessExpression(AbstractPhaseContext context, SyntaxNode node, PropertySymbol propertySymbol, BoundExpression sourceExpression)
|
||||
: base(context, node, BuildProperty(context, sourceExpression, propertySymbol), sourceExpression, null)
|
||||
{
|
||||
ValueType = propertySymbol.Type;
|
||||
}
|
||||
|
||||
private static PropertySymbol BuildProperty(AbstractPhaseContext context, BoundExpression sourceExpression, PropertySymbol propertySymbol)
|
||||
{
|
||||
TypeSymbol propertyType = sourceExpression.ValueType;
|
||||
|
||||
if (propertyType.UdonType.ExternSignature == "VRCUdonUdonBehaviour")
|
||||
propertyType = context.GetTypeSymbol(typeof(IUdonEventReceiver));
|
||||
|
||||
MethodSymbol setMethod = new ExternSynthesizedMethodSymbol(context, $"set_{propertySymbol.Name}", propertyType,
|
||||
new[] {propertySymbol.Type}, null, false);
|
||||
MethodSymbol getMethod = new ExternSynthesizedMethodSymbol(context, $"get_{propertySymbol.Name}", propertyType,
|
||||
new TypeSymbol[] {}, propertySymbol.Type, false);
|
||||
|
||||
return new SynthesizedPropertySymbol(context, getMethod, setMethod);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2ac35632a6bec5541ba54c611c7be6e4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,22 @@
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
using UdonSharp.Compiler.Emit;
|
||||
|
||||
namespace UdonSharp.Compiler.Binder
|
||||
{
|
||||
internal sealed class BoundReturnStatement : BoundStatement
|
||||
{
|
||||
private BoundExpression ReturnExpression { get; }
|
||||
|
||||
public BoundReturnStatement(SyntaxNode node, BoundExpression returnExpression)
|
||||
:base(node)
|
||||
{
|
||||
ReturnExpression = returnExpression;
|
||||
}
|
||||
|
||||
public override void Emit(EmitContext context)
|
||||
{
|
||||
context.EmitReturn(ReturnExpression);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 47b84e2be9d6eca4ead00c530f50485d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,72 @@
|
||||
|
||||
using System;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using UdonSharp.Compiler.Assembly;
|
||||
using UdonSharp.Compiler.Emit;
|
||||
using UdonSharp.Compiler.Symbols;
|
||||
|
||||
namespace UdonSharp.Compiler.Binder
|
||||
{
|
||||
internal sealed class BoundShortCircuitOperatorExpression : BoundExpression
|
||||
{
|
||||
private BoundExpression Lhs { get; }
|
||||
private BoundExpression Rhs { get; }
|
||||
|
||||
private BuiltinOperatorType OperatorType { get; }
|
||||
|
||||
public override TypeSymbol ValueType { get; }
|
||||
|
||||
public BoundShortCircuitOperatorExpression(BinaryExpressionSyntax node, BuiltinOperatorType operatorType, BoundExpression lhs, BoundExpression rhs, AbstractPhaseContext context)
|
||||
: base(node)
|
||||
{
|
||||
ValueType = context.GetTypeSymbol(SpecialType.System_Boolean);
|
||||
Lhs = lhs;
|
||||
Rhs = rhs;
|
||||
OperatorType = operatorType;
|
||||
}
|
||||
|
||||
public override Value EmitValue(EmitContext context)
|
||||
{
|
||||
// We don't want any references outside the flow control to be dirtied conditionally
|
||||
context.TopTable.DirtyAllValues();
|
||||
|
||||
Value resultValue = context.CreateInternalValue(ValueType);
|
||||
|
||||
if (OperatorType == BuiltinOperatorType.LogicalAnd)
|
||||
{
|
||||
JumpLabel failLabel = context.Module.CreateLabel();
|
||||
|
||||
context.EmitValueAssignment(resultValue, Lhs);
|
||||
|
||||
context.Module.AddJumpIfFalse(failLabel, resultValue);
|
||||
|
||||
context.EmitValueAssignment(resultValue, Rhs);
|
||||
|
||||
context.Module.LabelJump(failLabel);
|
||||
}
|
||||
else if (OperatorType == BuiltinOperatorType.LogicalOr)
|
||||
{
|
||||
JumpLabel failLabel = context.Module.CreateLabel();
|
||||
JumpLabel exitLabel = context.Module.CreateLabel();
|
||||
|
||||
context.EmitValueAssignment(resultValue, Lhs);
|
||||
|
||||
context.Module.AddJumpIfFalse(failLabel, resultValue);
|
||||
context.Module.AddJump(exitLabel);
|
||||
|
||||
context.Module.LabelJump(failLabel);
|
||||
|
||||
context.EmitValueAssignment(resultValue, Rhs);
|
||||
|
||||
context.Module.LabelJump(exitLabel);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Invalid operator type");
|
||||
}
|
||||
|
||||
return resultValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1ca2c2ebc93520e4fb348cac7cccaa5b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,14 @@
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace UdonSharp.Compiler.Binder
|
||||
{
|
||||
internal abstract class BoundStatement : BoundNode
|
||||
{
|
||||
protected BoundStatement(SyntaxNode node)
|
||||
:base(node)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4a01e89746ce9024ca0ea57d77f9113c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,14 @@
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
using UdonSharp.Compiler.Symbols;
|
||||
|
||||
namespace UdonSharp.Compiler.Binder
|
||||
{
|
||||
internal sealed class BoundStaticUserMethodInvocation : BoundUserMethodInvocationExpression
|
||||
{
|
||||
public BoundStaticUserMethodInvocation(SyntaxNode node, MethodSymbol method, BoundExpression[] parameterExpressions)
|
||||
:base(node, method, null, parameterExpressions)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9e900cfa7f9ba7448af747103b025bc4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,52 @@
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using UdonSharp.Compiler.Emit;
|
||||
using UdonSharp.Compiler.Symbols;
|
||||
|
||||
namespace UdonSharp.Compiler.Binder
|
||||
{
|
||||
internal sealed class BoundStringAccessExpression : BoundAccessExpression
|
||||
{
|
||||
private BoundExpression IndexerExpression { get; }
|
||||
private readonly MethodSymbol _toCharArraySymbol;
|
||||
|
||||
public BoundStringAccessExpression(AbstractPhaseContext context, SyntaxNode node, BoundExpression sourceExpression, BoundExpression indexerExpression)
|
||||
: base(node, sourceExpression)
|
||||
{
|
||||
IndexerExpression = indexerExpression;
|
||||
ValueType = context.GetTypeSymbol(SpecialType.System_Char);
|
||||
|
||||
_toCharArraySymbol = context.GetTypeSymbol(SpecialType.System_String).GetMembers<MethodSymbol>(nameof(string.ToCharArray), context).First(e => e.Parameters.Length == 2);
|
||||
}
|
||||
|
||||
public override TypeSymbol ValueType { get; }
|
||||
|
||||
public override Value EmitValue(EmitContext context)
|
||||
{
|
||||
Value returnValue = context.GetReturnValue(ValueType);
|
||||
|
||||
var charArray = BoundInvocationExpression.CreateBoundInvocation(context, SyntaxNode, _toCharArraySymbol,
|
||||
BindAccess(context.EmitValue(SourceExpression)), new[]
|
||||
{
|
||||
IndexerExpression,
|
||||
BindAccess(context.GetConstantValue(context.GetTypeSymbol(SpecialType.System_Int32), 1))
|
||||
});
|
||||
|
||||
context.EmitValueAssignment(returnValue,
|
||||
BindElementAccess(context, SyntaxNode, charArray,
|
||||
new BoundExpression[]
|
||||
{
|
||||
BindAccess(context.GetConstantValue(context.GetTypeSymbol(SpecialType.System_Int32), 0))
|
||||
}));
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
public override Value EmitSet(EmitContext context, BoundExpression valueExpression)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot set a character on a string");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1bd86ebdb185698468f73ebb4d4a57ca
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,245 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using UdonSharp.Compiler.Assembly;
|
||||
using UdonSharp.Compiler.Emit;
|
||||
using UdonSharp.Compiler.Symbols;
|
||||
|
||||
namespace UdonSharp.Compiler.Binder
|
||||
{
|
||||
internal sealed class BoundSwitchStatement : BoundStatement
|
||||
{
|
||||
private BoundExpression SwitchExpression { get; }
|
||||
private List<(List<BoundExpression>, List<BoundStatement>)> SwitchSections { get; }
|
||||
|
||||
private int DefaultSectionIdx { get; }
|
||||
|
||||
public BoundSwitchStatement(SyntaxNode node, BoundExpression expression, List<(List<BoundExpression>, List<BoundStatement>)> sections, int defaultSectionIdx)
|
||||
:base(node)
|
||||
{
|
||||
SwitchExpression = expression;
|
||||
SwitchSections = sections;
|
||||
DefaultSectionIdx = defaultSectionIdx;
|
||||
}
|
||||
|
||||
public override void Emit(EmitContext context)
|
||||
{
|
||||
// Todo: look at adding binary search and then dictionary lookups as fallbacks for especially large switches that can't use jump tables
|
||||
if (IsJumpTableCandidate())
|
||||
EmitJumpTableSwitchStatement(context);
|
||||
else
|
||||
EmitDefaultSwitchStatement(context);
|
||||
}
|
||||
|
||||
private void EmitDefaultSwitchStatement(EmitContext context)
|
||||
{
|
||||
JumpLabel breakLabel = context.PushBreakLabel();
|
||||
JumpLabel defaultLabel = context.Module.CreateLabel();
|
||||
|
||||
Value switchConditionVal = context.EmitValue(SwitchExpression);
|
||||
var conditionAccess = BoundAccessExpression.BindAccess(switchConditionVal);
|
||||
|
||||
TypeSymbol objectType = context.GetTypeSymbol(SpecialType.System_Object);
|
||||
MethodSymbol objectEqualityMethod = null;
|
||||
|
||||
// If switch is over object we need to check if it's null first and jump to the default if it is
|
||||
if (SwitchExpression.ValueType == objectType)
|
||||
{
|
||||
objectEqualityMethod = objectType.GetMember<MethodSymbol>("Equals", context);
|
||||
|
||||
Value conditionCheck = context.EmitValue(BoundInvocationExpression.CreateBoundInvocation(
|
||||
context, SyntaxNode,
|
||||
new ExternSynthesizedOperatorSymbol(BuiltinOperatorType.Inequality,
|
||||
objectType, context), null,
|
||||
new BoundExpression[] { conditionAccess, BoundAccessExpression.BindAccess(context.GetConstantValue(objectType, null)) }));
|
||||
|
||||
context.Module.AddJumpIfFalse(defaultLabel, conditionCheck);
|
||||
}
|
||||
|
||||
JumpLabel nextLabel = context.Module.CreateLabel();
|
||||
|
||||
using (context.OpenBlockScope())
|
||||
{
|
||||
for (int i = 0; i < SwitchSections.Count; ++i)
|
||||
{
|
||||
var section = SwitchSections[i];
|
||||
JumpLabel sectionBodyLabel = context.Module.CreateLabel();
|
||||
|
||||
foreach (BoundExpression labelExpression in section.Item1)
|
||||
{
|
||||
context.Module.LabelJump(nextLabel);
|
||||
|
||||
nextLabel = context.Module.CreateLabel();
|
||||
|
||||
Value conditionCheck;
|
||||
|
||||
if (SwitchExpression.ValueType == objectType)
|
||||
{
|
||||
conditionCheck = context.EmitValue(BoundInvocationExpression.CreateBoundInvocation(
|
||||
context, SyntaxNode, objectEqualityMethod, conditionAccess,
|
||||
new[] { labelExpression }));
|
||||
}
|
||||
else
|
||||
{
|
||||
conditionCheck = context.EmitValue(BoundInvocationExpression.CreateBoundInvocation(
|
||||
context, SyntaxNode,
|
||||
new ExternSynthesizedOperatorSymbol(BuiltinOperatorType.Equality,
|
||||
switchConditionVal.UdonType, context), null,
|
||||
new[] { conditionAccess, labelExpression }));
|
||||
}
|
||||
|
||||
context.Module.AddJumpIfFalse(nextLabel, conditionCheck);
|
||||
|
||||
if (section.Item1.Count > 1)
|
||||
context.Module.AddJump(sectionBodyLabel);
|
||||
}
|
||||
|
||||
if (i == DefaultSectionIdx)
|
||||
{
|
||||
context.Module.AddJump(nextLabel);
|
||||
context.Module.LabelJump(defaultLabel);
|
||||
}
|
||||
|
||||
context.Module.LabelJump(sectionBodyLabel);
|
||||
|
||||
foreach (BoundStatement statement in section.Item2)
|
||||
{
|
||||
context.Emit(statement);
|
||||
}
|
||||
}
|
||||
|
||||
context.Module.LabelJump(nextLabel);
|
||||
|
||||
if (DefaultSectionIdx != -1)
|
||||
context.Module.AddJump(defaultLabel);
|
||||
else
|
||||
context.Module.LabelJump(defaultLabel);
|
||||
|
||||
context.Module.LabelJump(breakLabel);
|
||||
}
|
||||
|
||||
context.PopBreakLabel();
|
||||
}
|
||||
|
||||
private void EmitJumpTableSwitchStatement(EmitContext context)
|
||||
{
|
||||
Value expressionValue = context.EmitValue(SwitchExpression);
|
||||
|
||||
JumpLabel exitLabel = context.PushBreakLabel();
|
||||
JumpLabel defaultJump = context.Module.CreateLabel();
|
||||
|
||||
int maxValue = 0;
|
||||
foreach (var switchSection in SwitchSections)
|
||||
{
|
||||
foreach (var expression in switchSection.Item1)
|
||||
maxValue = Math.Max(maxValue, Convert.ToInt32(expression.ConstantValue.Value));
|
||||
}
|
||||
|
||||
Value greaterThanZeroCondition = context.EmitValue(BoundInvocationExpression.CreateBoundInvocation(
|
||||
context, SyntaxNode,
|
||||
new ExternSynthesizedOperatorSymbol(BuiltinOperatorType.GreaterThanOrEqual,
|
||||
expressionValue.UdonType, context), null,
|
||||
new[]
|
||||
{
|
||||
BoundAccessExpression.BindAccess(expressionValue),
|
||||
BoundAccessExpression.BindAccess(context.GetConstantValue(expressionValue.UdonType,
|
||||
Convert.ChangeType(0, expressionValue.UdonType.SystemType)))
|
||||
}));
|
||||
|
||||
context.Module.AddJumpIfFalse(defaultJump, greaterThanZeroCondition);
|
||||
|
||||
Value lessThanMaxCondition = context.EmitValue(BoundInvocationExpression.CreateBoundInvocation(
|
||||
context, SyntaxNode,
|
||||
new ExternSynthesizedOperatorSymbol(BuiltinOperatorType.LessThanOrEqual,
|
||||
expressionValue.UdonType, context), null,
|
||||
new[]
|
||||
{
|
||||
BoundAccessExpression.BindAccess(expressionValue),
|
||||
BoundAccessExpression.BindAccess(context.GetConstantValue(expressionValue.UdonType,
|
||||
Convert.ChangeType(maxValue, expressionValue.UdonType.SystemType)))
|
||||
}));
|
||||
|
||||
context.Module.AddJumpIfFalse(defaultJump, lessThanMaxCondition);
|
||||
|
||||
Value convertedValue = context.CastValue(expressionValue, context.GetTypeSymbol(SpecialType.System_Int32), true);
|
||||
Value jumpTable = context.CreateGlobalInternalValue(context.GetTypeSymbol(SpecialType.System_UInt32).MakeArrayType(context));
|
||||
Value jumpAddress = context.EmitValue(BoundAccessExpression.BindElementAccess(context, SyntaxNode,
|
||||
BoundAccessExpression.BindAccess(jumpTable),
|
||||
new BoundExpression[] { BoundAccessExpression.BindAccess(convertedValue) }));
|
||||
|
||||
context.Module.AddJumpIndrect(jumpAddress);
|
||||
|
||||
uint[] jumpTableArr = new uint[maxValue + 1];
|
||||
|
||||
using (context.OpenBlockScope())
|
||||
{
|
||||
for (int i = 0; i < SwitchSections.Count; ++i)
|
||||
{
|
||||
var switchSection = SwitchSections[i];
|
||||
|
||||
JumpLabel currentPos = context.Module.CreateLabel();
|
||||
context.Module.LabelJump(currentPos);
|
||||
if (DefaultSectionIdx == i)
|
||||
context.Module.LabelJump(defaultJump);
|
||||
|
||||
foreach (BoundExpression labelExpression in switchSection.Item1)
|
||||
{
|
||||
int labelIdx = Convert.ToInt32(labelExpression.ConstantValue.Value);
|
||||
jumpTableArr[labelIdx] = currentPos.Address;
|
||||
}
|
||||
|
||||
foreach (BoundStatement statement in switchSection.Item2)
|
||||
{
|
||||
context.Emit(statement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (DefaultSectionIdx == -1)
|
||||
context.Module.LabelJump(defaultJump);
|
||||
|
||||
context.Module.LabelJump(exitLabel);
|
||||
|
||||
context.PopBreakLabel();
|
||||
|
||||
for (int i = 0; i < jumpTableArr.Length; ++i)
|
||||
{
|
||||
if (jumpTableArr[i] == 0)
|
||||
jumpTableArr[i] = defaultJump.Address;
|
||||
}
|
||||
|
||||
jumpTable.DefaultValue = jumpTableArr;
|
||||
}
|
||||
|
||||
private const int JUMP_TABLE_MAX = 256;
|
||||
|
||||
private bool IsJumpTableCandidate()
|
||||
{
|
||||
if (!UdonSharpUtils.IsIntegerType(SwitchExpression.ValueType.UdonType.SystemType))
|
||||
return false;
|
||||
|
||||
int labelCount = 0;
|
||||
|
||||
foreach (var switchSection in SwitchSections)
|
||||
{
|
||||
foreach (var expression in switchSection.Item1)
|
||||
{
|
||||
labelCount++;
|
||||
|
||||
if (expression.ConstantValue.Value is ulong ulongVal && (ulongVal > JUMP_TABLE_MAX))
|
||||
return false;
|
||||
|
||||
long intVal = Convert.ToInt64(expression.ConstantValue.Value);
|
||||
|
||||
if (intVal > JUMP_TABLE_MAX || intVal < 0)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (labelCount < 4)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 093f1060b48d746498a98d436f413b29
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,183 @@
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using UdonSharp.Compiler.Emit;
|
||||
using UdonSharp.Compiler.Symbols;
|
||||
using UdonSharp.Core;
|
||||
|
||||
namespace UdonSharp.Compiler.Binder
|
||||
{
|
||||
internal sealed class BoundUdonSharpBehaviourInvocationExpression : BoundUserMethodInvocationExpression
|
||||
{
|
||||
public BoundUdonSharpBehaviourInvocationExpression(SyntaxNode node, MethodSymbol method, BoundExpression instanceExpression, BoundExpression[] parameterExpressions)
|
||||
:base(node, method, instanceExpression, parameterExpressions)
|
||||
{
|
||||
}
|
||||
|
||||
public override Value EmitValue(EmitContext context)
|
||||
{
|
||||
// Make base calls to UdonSharpBehaviour events a noop
|
||||
if (IsBaseCall)
|
||||
{
|
||||
if (Method.ContainingType == context.GetTypeSymbol(typeof(UdonSharpBehaviour)))
|
||||
{
|
||||
if (Method.Name == "OnOwnershipRequest")
|
||||
return context.GetConstantValue(context.GetTypeSymbol(SpecialType.System_Boolean), true);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (SourceExpression == null || SourceExpression.IsThis)
|
||||
return base.EmitValue(context);
|
||||
|
||||
// Calls across UdonBehaviours
|
||||
CompilationContext.MethodExportLayout layout =
|
||||
context.CompileContext.GetUsbMethodLayout(Method, context);
|
||||
|
||||
Value.CowValue instanceCowValue = GetInstanceValue(context);
|
||||
Value instanceValue = instanceCowValue.Value;
|
||||
BoundAccessExpression instanceAccess = BoundAccessExpression.BindAccess(instanceValue);
|
||||
|
||||
TypeSymbol stringType = context.GetTypeSymbol(SpecialType.System_String);
|
||||
|
||||
Value[] recursiveValues = null;
|
||||
Value stackSizeCheckVal = null;
|
||||
bool isRecursiveCall = context.IsRecursiveMethodEmit;
|
||||
|
||||
if (isRecursiveCall)
|
||||
{
|
||||
stackSizeCheckVal = context.CreateGlobalInternalValue(context.GetTypeSymbol(SpecialType.System_Int32));
|
||||
}
|
||||
|
||||
if (Method.Parameters.Length > 0)
|
||||
{
|
||||
MethodSymbol setProgramVariableMethod = context.GetTypeSymbol(typeof(UdonSharpBehaviour))
|
||||
.GetMembers<MethodSymbol>("SetProgramVariable", context)
|
||||
.First(e => e.Parameters.Length == 2 &&
|
||||
e.Parameters[0].Type == stringType);
|
||||
|
||||
Value[] parameterValues = GetParameterValues(context);
|
||||
instanceValue = instanceCowValue.Value;
|
||||
instanceAccess = BoundAccessExpression.BindAccess(instanceValue); // Re-bind here since the parameters may have changed the cowvalue
|
||||
|
||||
if (isRecursiveCall)
|
||||
{
|
||||
EmitContext.MethodLinkage selfLinkage = context.GetMethodLinkage(context.CurrentEmitMethod, false);
|
||||
recursiveValues = selfLinkage.ParameterValues;
|
||||
|
||||
CheckStackSize(stackSizeCheckVal, context);
|
||||
PushRecursiveValues(recursiveValues, context);
|
||||
|
||||
for (int i = 0; i < parameterValues.Length; ++i)
|
||||
{
|
||||
Value paramIntermediate = context.CreateInternalValue(parameterValues[i].UserType);
|
||||
context.Module.AddCopy(parameterValues[i], paramIntermediate);
|
||||
parameterValues[i] = paramIntermediate;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
instanceCowValue.Dispose();
|
||||
}
|
||||
|
||||
context.TopTable.DirtyAllValues();
|
||||
|
||||
for (int i = 0; i < Method.Parameters.Length; ++i)
|
||||
{
|
||||
context.Emit(CreateBoundInvocation(context, SyntaxNode, setProgramVariableMethod, instanceAccess,
|
||||
new BoundExpression[]
|
||||
{
|
||||
BoundAccessExpression.BindAccess(context.GetConstantValue(stringType,layout.ParameterExportNames[i])),
|
||||
BoundAccessExpression.BindAccess(parameterValues[i])
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
if (isRecursiveCall)
|
||||
{
|
||||
if (recursiveValues == null)
|
||||
recursiveValues = Array.Empty<Value>();
|
||||
|
||||
Value[] scopeValues = context.CollectRecursiveValues();
|
||||
|
||||
PushRecursiveValues(scopeValues, context);
|
||||
|
||||
recursiveValues = recursiveValues.Concat(scopeValues).ToArray();
|
||||
|
||||
stackSizeCheckVal.DefaultValue = recursiveValues.Length;
|
||||
context.UpdateRecursiveStackMaxSize(recursiveValues.Length);
|
||||
}
|
||||
|
||||
MethodSymbol sendCustomEventMethod = context.GetTypeSymbol(typeof(UdonSharpBehaviour)).GetMember<MethodSymbol>("SendCustomEvent", context);
|
||||
context.Emit(CreateBoundInvocation(context, SyntaxNode, sendCustomEventMethod, BoundAccessExpression.BindAccess(instanceValue),
|
||||
new BoundExpression[]
|
||||
{
|
||||
BoundAccessExpression.BindAccess(context.GetConstantValue(stringType, layout.ExportMethodName))
|
||||
}));
|
||||
|
||||
if (isRecursiveCall)
|
||||
PopRecursiveValues(recursiveValues, context);
|
||||
|
||||
if (Method.Parameters.Length > 0 &&
|
||||
Method.Parameters.Any(e => e.IsOut))
|
||||
{
|
||||
if (isRecursiveCall)
|
||||
throw new CompilerException("U# does not yet support calling user methods with ref/out parameters from methods marked with RecursiveMethod");
|
||||
|
||||
MethodSymbol getProgramVariableMethod = context.GetTypeSymbol(typeof(UdonSharpBehaviour))
|
||||
.GetMembers<MethodSymbol>("GetProgramVariable", context)
|
||||
.First(e => e.Parameters.Length == 1 &&
|
||||
e.Parameters[0].Type == stringType);
|
||||
|
||||
// Copy out/ref parameters back
|
||||
for (int i = 0; i < Method.Parameters.Length; ++i)
|
||||
{
|
||||
ParameterSymbol parameterSymbol = Method.Parameters[i];
|
||||
|
||||
if (parameterSymbol.IsOut)
|
||||
{
|
||||
BoundAccessExpression currentAccessExpression = (BoundAccessExpression)ParameterExpressions[i];
|
||||
|
||||
currentAccessExpression.EmitSet(context, CreateBoundInvocation(context, SyntaxNode,
|
||||
getProgramVariableMethod, instanceAccess, new[]
|
||||
{
|
||||
BoundAccessExpression.BindAccess(context.GetConstantValue(stringType,
|
||||
layout.ParameterExportNames[i]))
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (IsPropertySetter)
|
||||
{
|
||||
return GetParameterValues(context).Last();
|
||||
}
|
||||
|
||||
if (Method.ReturnType != null)
|
||||
{
|
||||
MethodSymbol getProgramVariableMethod = context.GetTypeSymbol(typeof(UdonSharpBehaviour))
|
||||
.GetMembers<MethodSymbol>("GetProgramVariable", context)
|
||||
.First(e => e.Parameters.Length == 1 &&
|
||||
e.Parameters[0].Type == stringType);
|
||||
|
||||
Value returnVal = context.CreateInternalValue(Method.ReturnType);
|
||||
|
||||
BoundInvocationExpression boundGetReturn = CreateBoundInvocation(context, SyntaxNode,
|
||||
getProgramVariableMethod, instanceAccess,
|
||||
new BoundExpression[]
|
||||
{
|
||||
BoundAccessExpression.BindAccess(context.GetConstantValue(stringType,
|
||||
layout.ReturnExportName))
|
||||
});
|
||||
|
||||
context.EmitValueAssignment(returnVal, boundGetReturn);
|
||||
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b31b1704bbe04d6478cc201357e1d414
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,133 @@
|
||||
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using UdonSharp.Compiler.Assembly;
|
||||
using UdonSharp.Compiler.Emit;
|
||||
using UdonSharp.Compiler.Symbols;
|
||||
using UdonSharp.Core;
|
||||
|
||||
namespace UdonSharp.Compiler.Binder
|
||||
{
|
||||
internal abstract class BoundUserMethodInvocationExpression : BoundInvocationExpression
|
||||
{
|
||||
protected BoundUserMethodInvocationExpression(SyntaxNode node, MethodSymbol method, BoundExpression instanceExpression, BoundExpression[] parameterExpressions)
|
||||
: base(node, method, instanceExpression, parameterExpressions)
|
||||
{
|
||||
}
|
||||
|
||||
protected bool IsBaseCall { get; private set; }
|
||||
|
||||
public override void MarkForcedBaseCall()
|
||||
{
|
||||
IsBaseCall = true;
|
||||
}
|
||||
|
||||
public override Value EmitValue(EmitContext context)
|
||||
{
|
||||
JumpLabel returnPoint = context.Module.CreateLabel();
|
||||
Value returnPointVal =
|
||||
context.CreateGlobalInternalValue(context.GetTypeSymbol(SpecialType.System_UInt32));
|
||||
|
||||
context.Module.AddPush(returnPointVal);
|
||||
var linkage = context.GetMethodLinkage(Method, !IsBaseCall);
|
||||
|
||||
Value[] parameterValues = GetParameterValues(context);
|
||||
|
||||
Value[] recursiveValues = null;
|
||||
bool isRecursiveCall = context.IsRecursiveMethodEmit;
|
||||
|
||||
Value stackSizeCheckVal = null;
|
||||
|
||||
if (isRecursiveCall)
|
||||
{
|
||||
EmitContext.MethodLinkage selfLinkage = context.GetMethodLinkage(context.CurrentEmitMethod, false);
|
||||
recursiveValues = selfLinkage.ParameterValues;
|
||||
|
||||
stackSizeCheckVal = context.CreateGlobalInternalValue(context.GetTypeSymbol(SpecialType.System_Int32));
|
||||
|
||||
CheckStackSize(stackSizeCheckVal, context);
|
||||
PushRecursiveValues(selfLinkage.ParameterValues, context);
|
||||
}
|
||||
|
||||
ReleaseCowReferences(context);
|
||||
|
||||
if (isRecursiveCall)
|
||||
{
|
||||
Value.CowValue[] paramCows = parameterValues.Select(e => e.GetCowValue(context)).ToArray();
|
||||
|
||||
for (int i = 0; i < linkage.ParameterValues.Length; ++i)
|
||||
context.EmitValueAssignment(linkage.ParameterValues[i], BoundAccessExpression.BindAccess(paramCows[i]));
|
||||
|
||||
foreach (var paramCow in paramCows)
|
||||
paramCow.Dispose();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < linkage.ParameterValues.Length; ++i)
|
||||
context.Module.AddCopy(parameterValues[i], linkage.ParameterValues[i]);
|
||||
}
|
||||
|
||||
context.TopTable.DirtyAllValues();
|
||||
|
||||
if (isRecursiveCall)
|
||||
{
|
||||
Value[] collectedValues = context.CollectRecursiveValues().Where(e => !recursiveValues.Contains(e)).ToArray();
|
||||
|
||||
PushRecursiveValues(collectedValues, context);
|
||||
|
||||
recursiveValues = recursiveValues.Concat(collectedValues).ToArray();
|
||||
|
||||
stackSizeCheckVal.DefaultValue = recursiveValues.Length;
|
||||
context.UpdateRecursiveStackMaxSize(recursiveValues.Length);
|
||||
}
|
||||
|
||||
context.Module.AddCommentTag($"Calling {Method}");
|
||||
context.Module.AddJump(linkage.MethodLabel);
|
||||
|
||||
context.Module.LabelJump(returnPoint);
|
||||
returnPointVal.DefaultValue = returnPoint.Address;
|
||||
|
||||
Value recursiveRet = null;
|
||||
|
||||
if (isRecursiveCall)
|
||||
{
|
||||
if (linkage.ReturnValue != null)
|
||||
{
|
||||
recursiveRet = context.CreateInternalValue(linkage.ReturnValue.UserType);
|
||||
context.Module.AddCopy(linkage.ReturnValue, recursiveRet);
|
||||
}
|
||||
|
||||
PopRecursiveValues(recursiveValues, context);
|
||||
}
|
||||
|
||||
// Handle out/ref parameters
|
||||
for (int i = 0; i < Method.Parameters.Length; ++i)
|
||||
{
|
||||
if (!Method.Parameters[i].IsOut) continue;
|
||||
|
||||
if (isRecursiveCall)
|
||||
throw new CompilerException("U# does not yet support calling user methods with ref/out parameters from methods marked with RecursiveMethod");
|
||||
|
||||
BoundAccessExpression paramAccess = (BoundAccessExpression)ParameterExpressions[i];
|
||||
|
||||
paramAccess.EmitSet(context, BoundAccessExpression.BindAccess(linkage.ParameterValues[i]));
|
||||
}
|
||||
|
||||
// Properties need to return the value that they are set to for assignment expressions
|
||||
if (IsPropertySetter)
|
||||
{
|
||||
return parameterValues.Last();
|
||||
}
|
||||
|
||||
if (Method.ReturnType != null)
|
||||
{
|
||||
if (isRecursiveCall)
|
||||
return recursiveRet;
|
||||
|
||||
return linkage.ReturnValue;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8abfdc03d4bdc4943883d8165c9947d0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,23 @@
|
||||
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using UdonSharp.Compiler.Emit;
|
||||
|
||||
namespace UdonSharp.Compiler.Binder
|
||||
{
|
||||
internal sealed class BoundVariableDeclarationStatement : BoundStatement
|
||||
{
|
||||
private BoundVariableDeclaratorStatement[] Declarations { get; }
|
||||
|
||||
public BoundVariableDeclarationStatement(VariableDeclarationSyntax node, BoundVariableDeclaratorStatement[] declarations)
|
||||
: base(node)
|
||||
{
|
||||
Declarations = declarations;
|
||||
}
|
||||
|
||||
public override void Emit(EmitContext context)
|
||||
{
|
||||
foreach (BoundVariableDeclaratorStatement declaration in Declarations)
|
||||
context.Emit(declaration);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4f22848a9d58a4e4387fc73b6dc122e1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,32 @@
|
||||
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using UdonSharp.Compiler.Emit;
|
||||
using UdonSharp.Compiler.Symbols;
|
||||
|
||||
namespace UdonSharp.Compiler.Binder
|
||||
{
|
||||
internal sealed class BoundVariableDeclaratorStatement : BoundStatement
|
||||
{
|
||||
public Symbol UserSymbol { get; }
|
||||
public BoundExpression Initializer { get; }
|
||||
|
||||
public BoundVariableDeclaratorStatement(VariableDeclaratorSyntax node, Symbol userSymbol, BoundExpression initializer)
|
||||
: base(node)
|
||||
{
|
||||
UserSymbol = userSymbol;
|
||||
Initializer = initializer;
|
||||
}
|
||||
|
||||
public override void Emit(EmitContext context)
|
||||
{
|
||||
if (UserSymbol is LocalSymbol localSymbol && localSymbol.IsConst)
|
||||
return;
|
||||
|
||||
Value userValue = context.GetUserValue(UserSymbol);
|
||||
|
||||
if (Initializer == null) return;
|
||||
|
||||
context.EmitValueAssignment(userValue, Initializer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c0145f9ed82b8d148ab8d61814523e8a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,39 @@
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
using UdonSharp.Compiler.Assembly;
|
||||
using UdonSharp.Compiler.Emit;
|
||||
|
||||
namespace UdonSharp.Compiler.Binder
|
||||
{
|
||||
internal sealed class BoundWhileStatement : BoundStatement
|
||||
{
|
||||
private BoundExpression Condition { get; }
|
||||
private BoundStatement Body { get; }
|
||||
|
||||
public BoundWhileStatement(SyntaxNode node, BoundExpression condition, BoundStatement body)
|
||||
:base(node)
|
||||
{
|
||||
Condition = condition;
|
||||
Body = body;
|
||||
}
|
||||
|
||||
public override void Emit(EmitContext context)
|
||||
{
|
||||
JumpLabel continueLabel = context.PushContinueLabel();
|
||||
JumpLabel breakLabel = context.PushBreakLabel();
|
||||
|
||||
context.Module.LabelJump(continueLabel);
|
||||
|
||||
context.Module.AddJumpIfFalse(breakLabel, context.EmitValue(Condition));
|
||||
|
||||
context.Emit(Body);
|
||||
|
||||
context.Module.AddJump(continueLabel);
|
||||
|
||||
context.Module.LabelJump(breakLabel);
|
||||
|
||||
context.PopBreakLabel();
|
||||
context.PopContinueLabel();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b8b1da9db75d2ce44b62a3ac12e77199
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user