mirror of
https://github.com/HeyPuter/gex.git
synced 2026-11-13 20:12:43 -10:00
unity-dotnet P4: Muck runs its menu scene's scripts on .NET Mono (batch mode)
- mono_class_get_field_from_name retries Unity mscorlib's m_x as .NET's _x (AttributeScannerQuery reads AttributeUsageAttribute's fields by name); misses are logged. - mono_custom_attrs_from_field uses the field's own class: UnityPlayer's serializer passes the class it serializes (List`1 for an element's fields). - Interp-to-native wrappers for every function type in the provider: the provider is linked once, its type section read, m2n.c regenerated, linked again (fixes CANNOT HANDLE INTERP ICALL SIG LLLLLLLL). - build.sh: JOBS (default 12, nice 19) per the gooner rules. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01PeRutwFgZ66XpxjasKrPn1
This commit is contained in:
1 parent
1fda117438
commit
21dbdeab3c
3 files changed
+110
-16
No files matched your search
@@ -20,7 +20,7 @@
|
||||
# em-runtime em-native em-smoke P1's emscripten build and its node smoke test
|
||||
#
|
||||
# Needs clang/wasm-ld (22), llvm-ar, dotnet, git, ninja, cmake; /usr/lib/emsdk only for em-*.
|
||||
# Builds at nice 19 with at most 4 jobs (stream rules).
|
||||
# Builds at nice 19 with JOBS (default 12) jobs (stream rules).
|
||||
set -euo pipefail
|
||||
|
||||
FORK=https://github.com/r58Playz/dotnet-runtime.git
|
||||
@@ -33,6 +33,7 @@ OUT=$(realpath -m "$1"); shift
|
||||
STEPS=${*:-src sysroot runtime native corelib provider}
|
||||
MUSL_SRC=${MUSL_SRC:-$(realpath -m "$UD/../../../musl-wasm64")}
|
||||
RUN="nice -n 19"
|
||||
JOBS=${JOBS:-12} # gooner rules: a heavy nice-19 build may use 12 jobs
|
||||
# CMake for dotnet/runtime wants llvm-strings, llvm-readelf, llvm-link: from the versioned LLVM if not on PATH.
|
||||
command -v llvm-strings >/dev/null || PATH=$PATH:/usr/lib/llvm-22/bin
|
||||
SR=$OUT/sysroot/sysroot
|
||||
@@ -63,7 +64,7 @@ step_runtime() {
|
||||
-DFEATURE_PERFTRACING_DISABLE_PERFTRACING_LISTEN_PORTS=1 -DFEATURE_PERFTRACING_DISABLE_DEFAULT_LISTEN_PORT=1 \
|
||||
-DFEATURE_PERFTRACING_DISABLE_CONNECT_PORTS=1 -DWITH_OPTIM=OFF \
|
||||
"$OUT/src/src/mono" > "$OUT/runtime-configure.log"
|
||||
$RUN ninja -j4 > "$OUT/runtime-build.log"
|
||||
$RUN ninja -j"$JOBS" > "$OUT/runtime-build.log"
|
||||
}
|
||||
|
||||
step_native() {
|
||||
@@ -73,7 +74,7 @@ step_native() {
|
||||
-DCLR_CMAKE_TARGET_ARCH=wasm -DCLR_CMAKE_TARGET_OS=linux -DWITH_OPTIM=OFF -DGEX_NO_GLOBALIZATION_NATIVE=1 \
|
||||
> "$OUT/native-configure.log"
|
||||
cd "$OUT/objnative"
|
||||
$RUN ninja -j4 System.Native/libSystem.Native.a > "$OUT/native-build.log"
|
||||
$RUN ninja -j"$JOBS" System.Native/libSystem.Native.a > "$OUT/native-build.log"
|
||||
}
|
||||
|
||||
step_corelib() {
|
||||
@@ -100,13 +101,21 @@ step_provider() {
|
||||
done
|
||||
clang $WASM64_CFLAGS $INC -I"$N/libs/Common" -I"$OUT/objnative/Common" -I"$N/libs/System.Native" -I"$N" -I"$N/libs" \
|
||||
-c "$N/libs/System.Native/entrypoints.c" -o entrypoints-System.Native.o
|
||||
wasm-ld -mwasm64 --experimental-pic -pie --no-entry --shared-memory --import-memory --max-memory=17179869184 -o mono.wasm \
|
||||
gexmono-provider.o gexmono-unity.o gexprov64.o gexprov64-table.o adapters.o m2n.o entrypoints-System.Native.o "$OUT/objnative/System.Native/libSystem.Native.a" \
|
||||
"$L"/mini/libmonosgen-2.0.a "$L"/mini/libmono-ee-interp.a "$L"/mini/libmono-component-marshal-ilgen-static.a \
|
||||
"$L"/mini/libmono-component-hot_reload-stub-static.a "$L"/mini/libmono-component-debugger-stub-static.a \
|
||||
"$L"/mini/libmono-component-diagnostics_tracing-stub-static.a "$L"/mini/libmono-wasm-nosimd.a \
|
||||
"$L"/minipal/minipal/libminipal.a "$OUT/obj/_deps/fetchzlibng-build/libz.a" \
|
||||
-L"$SR/lib" -lc "$SR/lib/libclang_rt.builtins-wasm64.a"
|
||||
link() {
|
||||
wasm-ld -mwasm64 --experimental-pic -pie --no-entry --shared-memory --import-memory --max-memory=17179869184 -o mono.wasm \
|
||||
gexmono-provider.o gexmono-unity.o gexprov64.o gexprov64-table.o adapters.o m2n.o entrypoints-System.Native.o "$OUT/objnative/System.Native/libSystem.Native.a" \
|
||||
"$L"/mini/libmonosgen-2.0.a "$L"/mini/libmono-ee-interp.a "$L"/mini/libmono-component-marshal-ilgen-static.a \
|
||||
"$L"/mini/libmono-component-hot_reload-stub-static.a "$L"/mini/libmono-component-debugger-stub-static.a \
|
||||
"$L"/mini/libmono-component-diagnostics_tracing-stub-static.a "$L"/mini/libmono-wasm-nosimd.a \
|
||||
"$L"/minipal/minipal/libminipal.a "$OUT/obj/_deps/fetchzlibng-build/libz.a" \
|
||||
-L"$SR/lib" -lc "$SR/lib/libclang_rt.builtins-wasm64.a"
|
||||
}
|
||||
# Interp-to-native wrappers for every function type in the provider (gen-m2n.py): link, read
|
||||
# the types, regenerate them, link again.
|
||||
link
|
||||
python3 "$HERE/host/gen-m2n.py" "$HERE/host/m2n-extra.txt" --types-from mono.wasm > m2n.c
|
||||
clang $WASM64_CFLAGS $INC -Wno-unused-command-line-argument -c m2n.c -o m2n.o
|
||||
link
|
||||
echo "provider: $W/mono.wasm; $(tail -1 adapters.log | cut -d, -f1)"
|
||||
}
|
||||
|
||||
@@ -125,7 +134,7 @@ step_em-runtime() {
|
||||
-DFEATURE_PERFTRACING_DISABLE_DEFAULT_LISTEN_PORT=1 \
|
||||
"-DCMAKE_ASM_FLAGS=$F" "-DCMAKE_C_FLAGS=$F -D_GNU_SOURCE=1 -mnontrapping-fptoint" \
|
||||
"-DCMAKE_CXX_FLAGS=$F -D_GNU_SOURCE=1 -mnontrapping-fptoint" "$OUT/src/src/mono" > "$OUT/em-runtime-configure.log"
|
||||
$RUN ninja -j4 > "$OUT/em-runtime-build.log"
|
||||
$RUN ninja -j"$JOBS" > "$OUT/em-runtime-build.log"
|
||||
}
|
||||
|
||||
step_em-native() {
|
||||
@@ -136,7 +145,7 @@ step_em-native() {
|
||||
-DCLR_CMAKE_TARGET_OS=browser -DCLR_CMAKE_TARGET_ARCH=wasm -DCLR_CMAKE_HOST_ARCH=wasm -DMONO_WASM_MT=1 \
|
||||
"-DCMAKE_C_FLAGS=$F" "-DCMAKE_CXX_FLAGS=$F" > "$OUT/em-native-configure.log"
|
||||
cd "$OUT/objnative-em"
|
||||
$RUN ninja -j4 System.Native/libSystem.Native.a > "$OUT/em-native-build.log"
|
||||
$RUN ninja -j"$JOBS" System.Native/libSystem.Native.a > "$OUT/em-native-build.log"
|
||||
local N="$OUT/src/src/native"
|
||||
emcc $F -O2 -c "$N/libs/System.Native/entrypoints.c" -I"$N/libs/Common" -I"$OUT/objnative-em/Common" \
|
||||
-I"$N/libs/System.Native" -I"$N" -I"$N/libs" -o "$OUT/objnative-em/entrypoints-System.Native.o"
|
||||
|
||||
@@ -3,8 +3,12 @@
|
||||
|
||||
A cookie names the callee's wasm signature: the return, then each argument, with
|
||||
I = i32, L = i64 (pointer-sized values included on wasm64), F = f32, D = f64, V = void.
|
||||
Every cookie up to MAX_FULL arguments is generated; longer ones come from the extra list.
|
||||
On a 64-bit runtime each integer argument (I or L) takes one slot of margs->iargs."""
|
||||
Every cookie up to MAX_FULL arguments is generated; longer ones come from the extra list and,
|
||||
with --types-from WASM, from every function type of a first link of the provider (a native
|
||||
callee in the provider has one of them), so build.sh links twice.
|
||||
On a 64-bit runtime each integer argument (I or L) takes one slot of margs->iargs.
|
||||
|
||||
gen-m2n.py [EXTRA] [--types-from WASM] > m2n.c"""
|
||||
import itertools, sys
|
||||
|
||||
MAX_FULL = 4
|
||||
@@ -37,8 +41,41 @@ def wrapper(c):
|
||||
body = f"\t{call};" if ret == 'V' else f"\t*({CT[ret]} *)mono_wasm_interp_method_args_get_retval (margs) = {call};"
|
||||
return f"static void m2n_{c} (void *fn, InterpMethodArguments *margs)\n{{\n{body}\n}}\n"
|
||||
|
||||
def leb(d, i):
|
||||
r = sh = 0
|
||||
while True:
|
||||
b = d[i]; i += 1
|
||||
r |= (b & 0x7f) << sh; sh += 7
|
||||
if b < 0x80:
|
||||
return r, i
|
||||
|
||||
def wasm_cookies(path):
|
||||
"""Cookies for the function types in a wasm module's type section."""
|
||||
d = open(path, 'rb').read()
|
||||
VT = {0x7f: 'I', 0x7e: 'L', 0x7d: 'F', 0x7c: 'D'}
|
||||
i, out = 8, set()
|
||||
while i < len(d):
|
||||
sid = d[i]; size, i = leb(d, i + 1)
|
||||
if sid == 1:
|
||||
n, j = leb(d, i)
|
||||
for _ in range(n):
|
||||
if d[j] != 0x60:
|
||||
raise SystemExit(f"{path}: unexpected type form {d[j]:#x}")
|
||||
np_, j = leb(d, j + 1); ps = d[j:j + np_]; j += np_
|
||||
nr, j = leb(d, j); rs = d[j:j + nr]; j += nr
|
||||
if nr <= 1 and all(b in VT for b in ps + rs):
|
||||
out.add((VT[rs[0]] if nr else 'V') + ''.join(VT[b] for b in ps))
|
||||
return out
|
||||
i += size
|
||||
return out
|
||||
|
||||
def main():
|
||||
extra = [l.strip() for l in open(sys.argv[1]) if l.strip() and not l.startswith('#')] if len(sys.argv) > 1 else []
|
||||
args = sys.argv[1:]
|
||||
types = set()
|
||||
if '--types-from' in args:
|
||||
k = args.index('--types-from'); types = wasm_cookies(args[k + 1]); del args[k:k + 2]
|
||||
extra = [l.strip() for l in open(args[0]) if l.strip() and not l.startswith('#')] if args else []
|
||||
extra += [c for c in types if len(c) <= 24]
|
||||
cs = cookies(extra)
|
||||
print("/* GENERATED by gen-m2n.py: Mono interp-to-native wrappers for wasm64 (gex). */")
|
||||
print("#include <stdint.h>\n#include <string.h>\n#include <stdlib.h>")
|
||||
|
||||
@@ -102,6 +102,54 @@ U(mono_unity_type_get_name_full_chunked)
|
||||
RETV;
|
||||
}
|
||||
|
||||
/* Unity's mscorlib is .NET Framework's reference source, whose private
|
||||
* fields are m_name; .NET's CoreLib names most of the same fields _name.
|
||||
* UnityPlayer reads a few of them by name (AttributeUsageAttribute's
|
||||
* m_attributeTarget, m_inherited, m_allowMultiple). A miss on m_x retries
|
||||
* _x, then the table; a final miss is logged. */
|
||||
static const char *const field_alias [][3] = {
|
||||
{ NULL, NULL, NULL },
|
||||
};
|
||||
U(mono_class_get_field_from_name)
|
||||
{
|
||||
ENTER;
|
||||
MonoClass *k = P (0);
|
||||
const char *name = P (1);
|
||||
MonoClassField *f = mono_class_get_field_from_name (k, name);
|
||||
if (!f && name && name [0] == 'm' && name [1] == '_') {
|
||||
char *alt = g_strdup (name + 1);
|
||||
f = mono_class_get_field_from_name (k, alt);
|
||||
g_free (alt);
|
||||
}
|
||||
for (int i = 0; !f && name && field_alias [i][0]; i++)
|
||||
if (!strcmp (name, field_alias [i][1]) && !strcmp (m_class_get_name (k), field_alias [i][0]))
|
||||
f = mono_class_get_field_from_name (k, field_alias [i][2]);
|
||||
if (!f && k)
|
||||
fprintf (stderr, "[gexmono] no field %s.%s::%s\n", m_class_get_name_space (k), m_class_get_name (k), name ? name : "(null)");
|
||||
RET (f);
|
||||
}
|
||||
|
||||
/* mono_custom_attrs_from_field (klass, field): Mono finds the field's
|
||||
* metadata row from its index in klass's field array, so klass must be the
|
||||
* class that defines the field. UnityPlayer's serializer passes the class it
|
||||
* is serializing, which for an inherited field is a subclass; use the
|
||||
* field's own parent. */
|
||||
U(mono_custom_attrs_from_field)
|
||||
{
|
||||
ENTER;
|
||||
MonoClass *k = P (0);
|
||||
MonoClassField *f = P (1);
|
||||
MonoClass *parent = f ? m_field_get_parent (f) : NULL;
|
||||
if (parent && parent != k) {
|
||||
static int logged;
|
||||
if (gexmono_trace_on || logged++ < 8)
|
||||
fprintf (stderr, "[gexmono] custom_attrs_from_field: %s::%s is %s's, not %s's\n", m_class_get_name (parent),
|
||||
mono_field_get_name (f), m_class_get_name (parent), k ? m_class_get_name (k) : "?");
|
||||
k = parent;
|
||||
}
|
||||
RET (mono_custom_attrs_from_field (k, f));
|
||||
}
|
||||
|
||||
U(unity_mono_method_is_generic) { ENTER; RET (((MonoMethod *)P (0))->is_generic); }
|
||||
U(unity_mono_method_is_inflated) { ENTER; RET (((MonoMethod *)P (0))->is_inflated); }
|
||||
U(unity_mono_reflection_method_get_method) { ENTER; MonoReflectionMethod *m = P (0); RET (m ? m->method : NULL); }
|
||||
@@ -367,6 +415,6 @@ __attribute__((used)) gexp_body_t const gexmono_unity_adapters [] = {
|
||||
L(mono_profiler_reset_all_coverage) L(mono_assembly_foreach) L(mono_stack_walk) L(mono_stack_walk_no_il)
|
||||
L(mono_trace_set_log_handler) L(mono_set_dirs) L(mono_set_assemblies_path) L(mono_set_assemblies_path_null_separated)
|
||||
L(mono_set_find_plugin_callback) L(mono_dl_fallback_register)
|
||||
L(unity_mono_method_is_generic) L(unity_mono_method_is_inflated) L(unity_mono_reflection_method_get_method)
|
||||
L(mono_class_get_field_from_name) L(mono_custom_attrs_from_field) L(unity_mono_method_is_generic) L(unity_mono_method_is_inflated) L(unity_mono_reflection_method_get_method)
|
||||
0
|
||||
};
|
||||
Reference in new issue
Block a user