154 lines
9.2 KiB
C#
154 lines
9.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Reflection;
|
|
using UnityEngine;
|
|
|
|
namespace Unity.Pipeline.Compilation
|
|
{
|
|
/// <summary>
|
|
/// Shared IlInterpreter <see cref="IlInterpreter.Interpreter.HostBinding"/> surface used by the
|
|
/// interpreter-backed code-reload path. Mirrors IlInterpreter's
|
|
/// <c>UnityHostBinding.Prototyping()</c> minus the Input System block, Audio, and physics —
|
|
/// anything outside UnityEngine's core module binds on demand via <see cref="ResolveAutoBindType"/>,
|
|
/// so the package forces no engine-module dependency. Reference types use <c>AllowType<T>()</c>
|
|
/// and blittable structs <c>AllowTypeStruct<T>()</c> for AOT/IL2CPP safety; static classes
|
|
/// and enums use <c>AllowType(typeof(...))</c>.
|
|
/// </summary>
|
|
static class IlInterpreterHostBindings
|
|
{
|
|
// Used by the interpreter-backed code-reload sink; available in dev players (incl. IL2CPP).
|
|
#if UNITY_EDITOR || DEVELOPMENT_BUILD || ENABLE_RUNTIME_PIPELINE
|
|
/// <summary>
|
|
/// Single source of truth for the interpreter's host surface: BCL + the standard UnityEngine
|
|
/// types + <c>Debug</c>. Every consumer — eval binding, code-reload binding, and the build-time
|
|
/// <c>link.xml</c> preservation set — is a projection of this one builder, so they cannot
|
|
/// drift. Callers overlay only what is site-specific: eval overlays capturing <c>Debug.Log</c>
|
|
/// variants; code reload overlays <c>AllowType(targetType)</c>; link.xml reads
|
|
/// <see cref="IlInterpreter.Interpreter.HostBinding.RegisteredTypes"/> off the result.
|
|
/// </summary>
|
|
public static IlInterpreter.Interpreter.HostBinding CreateStandard()
|
|
{
|
|
// Route IlInterpreter registration warnings to the Unity console (the default sink is
|
|
// stderr, which Unity players never surface).
|
|
IlInterpreter.Interpreter.HostBinding.Warn = msg => Debug.LogWarning(msg);
|
|
|
|
var binding = AddStandardUnity(new IlInterpreter.Interpreter.HostBinding().AllowBcl())
|
|
.AllowType(typeof(Debug)); // logging from eval/reloaded bodies (eval overlays capture on top)
|
|
// Demand-time auto-bind: the interpreter resolves every TypeRef a loaded script carries
|
|
// through this policy, so engine/game types outside the curated surface bind on first use
|
|
// instead of throwing "not registered". The curated list above stays the fast-path core
|
|
// (typed delegates, flat structs) and the link.xml baseline — a floor, not a ceiling.
|
|
// Types the policy declines (see ResolveAutoBindType) are not bindable — the
|
|
// project-extension seam that could promote them was deferred to a future release.
|
|
binding.AutoBindResolver = ResolveAutoBindType;
|
|
return binding;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Auto-bind policy for <see cref="CreateStandard"/>: resolve a CLR full name from the loaded
|
|
/// assemblies, skipping (a) the BCL — the hand-rolled <c>AllowBcl</c> shims collapse doubles
|
|
/// into the script's float number space and must stay authoritative — and (b) this package's
|
|
/// own assemblies. On IL2CPP dev players this self-limits to types the stripper kept (see the
|
|
/// dev-build link.xml widening in <c>CodeReloadLinkXmlGenerator</c>).
|
|
/// </summary>
|
|
static Type ResolveAutoBindType(string fullName)
|
|
{
|
|
foreach (var asm in PipelineUtils.GetLoadedAssemblies())
|
|
{
|
|
if (IsAutoBindSkippedAssembly(asm.GetName().Name ?? string.Empty)) continue;
|
|
Type t = null;
|
|
try { t = asm.GetType(fullName, throwOnError: false); } catch { }
|
|
if (t != null) return t;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// Internal for tests: the classification itself is the contract (a misclassified game
|
|
// assembly silently loses auto-bind), and no test can load a real assembly by these names.
|
|
internal static bool IsAutoBindSkippedAssembly(string name) =>
|
|
name == "System" ||
|
|
name.StartsWith("System.", StringComparison.Ordinal) ||
|
|
name.StartsWith("Microsoft.", StringComparison.Ordinal) ||
|
|
name.StartsWith("Mono.", StringComparison.Ordinal) ||
|
|
name == "mscorlib" ||
|
|
name == "netstandard" ||
|
|
name.StartsWith("nunit.", StringComparison.Ordinal) ||
|
|
name.StartsWith("Unity.Pipeline", StringComparison.Ordinal);
|
|
|
|
/// <summary>
|
|
/// Register the standard UnityEngine type surface onto <paramref name="binding"/> (fluent).
|
|
/// Private: <see cref="CreateStandard"/> is the only supported way to build the surface,
|
|
/// closing the compose-it-yourself seam where per-site drift used to enter.
|
|
/// </summary>
|
|
static IlInterpreter.Interpreter.HostBinding AddStandardUnity(IlInterpreter.Interpreter.HostBinding binding)
|
|
{
|
|
return binding
|
|
.AllowType(typeof(Resources))
|
|
.AllowType(typeof(PrimitiveType))
|
|
// The base Object surface carries the fake-null operators (op_Equality,
|
|
// op_Implicit -> bool) every `target == null` / `if (component)` guard compiles
|
|
// against, plus Destroy/Instantiate/name. Explicit, not auto-bound: the guards
|
|
// must work on IL2CPP without relying on the on-demand resolver.
|
|
.AllowType<UnityEngine.Object>()
|
|
.AllowType<GameObject>()
|
|
.AllowType<Transform>()
|
|
.AllowType<Camera>()
|
|
.AllowType<Renderer>()
|
|
.AllowType<MeshRenderer>()
|
|
.AllowType<SpriteRenderer>()
|
|
.AllowType<LineRenderer>()
|
|
.AllowType<TrailRenderer>()
|
|
.AllowType<Material>()
|
|
.AllowType<TextMesh>()
|
|
.AllowType<Sprite>()
|
|
.AllowType(typeof(Shader)) // Shader.Find for material creation
|
|
.AllowType(typeof(CameraClearFlags)) // cam.clearFlags
|
|
.AllowType(typeof(TextAnchor)) // TextMesh.anchor
|
|
.AllowType(typeof(TextAlignment)) // TextMesh.alignment
|
|
.AllowType<Vector2>()
|
|
.AllowTypeStruct<Vector3>()
|
|
.AllowType<Quaternion>()
|
|
.AllowType<Color>()
|
|
.AllowType(typeof(Mathf))
|
|
.AllowType(typeof(Time))
|
|
// 3D physics (Rigidbody, Collider, Physics, ForceMode, RaycastHit, ...) is NOT
|
|
// registered here: those types live in UnityEngine.PhysicsModule, and a compile-time
|
|
// reference would force a physics module dependency on every consumer. They bind on
|
|
// demand through AutoBindResolver instead — reflection-tier, and present in a player
|
|
// only when the game's own code kept the module alive.
|
|
.AllowType(typeof(Space))
|
|
.AllowType<Ray>()
|
|
.AllowType(typeof(LayerMask))
|
|
.AllowType(typeof(Cursor))
|
|
.AllowType(typeof(CursorLockMode))
|
|
.AllowType(typeof(Screen))
|
|
.AllowType(typeof(Rect))
|
|
.AllowType(typeof(ScreenOrientation))
|
|
.AllowType(typeof(Application))
|
|
.AllowType(typeof(PlayerPrefs))
|
|
.AllowType(typeof(Color32))
|
|
.AllowType(typeof(System.Collections.IEnumerator))
|
|
.AllowType(typeof(UnityEngine.Random))
|
|
// UITK value-changed callbacks read evt.newValue/previousValue on ChangeEvent<T>.
|
|
// Those are members on an OPEN generic type, which auto-bind can't resolve at Load
|
|
// (there's no closed instantiation to invoke against) — so reflect on the receiver's
|
|
// concrete type at call time. Covers Slider/Toggle/TextField/DropdownField/... the
|
|
// whole RegisterValueChangedCallback surface, without a compile-time UITK dependency.
|
|
.Allow("ChangeEvent`1", "get_newValue",
|
|
(recv, _) => recv?.GetType().GetProperty("newValue")?.GetValue(recv), 0)
|
|
.Allow("ChangeEvent`1", "get_previousValue",
|
|
(recv, _) => recv?.GetType().GetProperty("previousValue")?.GetValue(recv), 0)
|
|
// AllowType registers a type's methods/fields/props but not its constructors, so the
|
|
// common ctors are wired explicitly (Vector3's comes from AllowTypeStruct above).
|
|
.AllowConstructor("Vector2",
|
|
(_, args) => (object)new Vector2(Convert.ToSingle(args[0]), Convert.ToSingle(args[1])), 2)
|
|
.AllowConstructor("Color",
|
|
(_, args) => (object)new Color(Convert.ToSingle(args[0]), Convert.ToSingle(args[1]), Convert.ToSingle(args[2]), Convert.ToSingle(args[3])), 4)
|
|
.AllowConstructor("GameObject", (_, args) => (object)new GameObject(), 0)
|
|
.AllowConstructor("GameObject", (_, args) => (object)new GameObject((string)args[0]), 1)
|
|
.AllowConstructor("Material", (_, args) => (object)new Material((Shader)args[0]), 1);
|
|
}
|
|
#endif
|
|
}
|
|
}
|