diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Reflection/MethodBaseTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Reflection/MethodBaseTests.cs
index 61c811fbbd8ef..f10dc27b86b80 100644
--- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Reflection/MethodBaseTests.cs
+++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Reflection/MethodBaseTests.cs
@@ -55,27 +55,44 @@ public static void TestMethodBody()
{
MethodBase mbase = typeof(MethodBaseTests).GetMethod("MyOtherMethod", BindingFlags.Static | BindingFlags.Public);
MethodBody mb = mbase.GetMethodBody();
+ var codeSize = mb.GetILAsByteArray().Length;
Assert.True(mb.InitLocals); // local variables are initialized
-#if DEBUG
- Assert.Equal(2, mb.MaxStackSize);
- Assert.Equal(3, mb.LocalVariables.Count);
- foreach (LocalVariableInfo lvi in mb.LocalVariables)
+ if (codeSize == 0)
{
- if (lvi.LocalIndex == 0) { Assert.Equal(typeof(int), lvi.LocalType); }
- if (lvi.LocalIndex == 1) { Assert.Equal(typeof(string), lvi.LocalType); }
- if (lvi.LocalIndex == 2) { Assert.Equal(typeof(bool), lvi.LocalType); }
- }
+ // This condition is needed for running this test under WASM AOT mode.
+ // Because IL trim is enabled be default for WASM apps whenever AOT is enabled.
+ // And the method body of "MyOtherMethod" will be trimmed.
+#if DEBUG
+ Assert.Equal(2, mb.MaxStackSize);
#else
- Assert.Equal(1, mb.MaxStackSize);
- Assert.Equal(2, mb.LocalVariables.Count);
-
- foreach (LocalVariableInfo lvi in mb.LocalVariables)
- {
- if (lvi.LocalIndex == 0) { Assert.Equal(typeof(int), lvi.LocalType); }
- if (lvi.LocalIndex == 1) { Assert.Equal(typeof(string), lvi.LocalType); }
+ Assert.Equal(1, mb.MaxStackSize);
+#endif
+ Assert.Equal(0, mb.LocalVariables.Count);
}
+ else
+ {
+#if DEBUG
+ Assert.Equal(2, mb.MaxStackSize);
+ Assert.Equal(3, mb.LocalVariables.Count);
+
+ foreach (LocalVariableInfo lvi in mb.LocalVariables)
+ {
+ if (lvi.LocalIndex == 0) { Assert.Equal(typeof(int), lvi.LocalType); }
+ if (lvi.LocalIndex == 1) { Assert.Equal(typeof(string), lvi.LocalType); }
+ if (lvi.LocalIndex == 2) { Assert.Equal(typeof(bool), lvi.LocalType); }
+ }
+#else
+ Assert.Equal(1, mb.MaxStackSize);
+ Assert.Equal(2, mb.LocalVariables.Count);
+
+ foreach (LocalVariableInfo lvi in mb.LocalVariables)
+ {
+ if (lvi.LocalIndex == 0) { Assert.Equal(typeof(int), lvi.LocalType); }
+ if (lvi.LocalIndex == 1) { Assert.Equal(typeof(string), lvi.LocalType); }
+ }
#endif
+ }
}
private static int MyAnotherMethod(int x)
diff --git a/src/mono/mono/eglib/gfile.c b/src/mono/mono/eglib/gfile.c
index 6dac38dc52fbf..73597ad5b208d 100644
--- a/src/mono/mono/eglib/gfile.c
+++ b/src/mono/mono/eglib/gfile.c
@@ -104,3 +104,28 @@ g_file_error_from_errno (gint err_no)
return G_FILE_ERROR_FAILED;
}
}
+
+FILE *
+g_fopen (const char *path, const char *mode)
+{
+ FILE *fp;
+
+ if (!path)
+ return NULL;
+
+#ifndef HOST_WIN32
+ fp = fopen (path, mode);
+#else
+ gunichar2 *wPath = g_utf8_to_utf16 (path, -1, 0, 0, 0);
+ gunichar2 *wMode = g_utf8_to_utf16 (mode, -1, 0, 0, 0);
+
+ if (!wPath || !wMode)
+ return NULL;
+
+ fp = _wfopen ((wchar_t *) wPath, (wchar_t *) wMode);
+ g_free (wPath);
+ g_free (wMode);
+#endif
+
+ return fp;
+}
diff --git a/src/mono/mono/eglib/glib.h b/src/mono/mono/eglib/glib.h
index f645f8431b5fa..68192b2e6baeb 100644
--- a/src/mono/mono/eglib/glib.h
+++ b/src/mono/mono/eglib/glib.h
@@ -962,6 +962,7 @@ typedef enum {
G_ENUM_FUNCTIONS (GFileTest)
+FILE * g_fopen (const char *path, const char *mode);
gboolean g_file_get_contents (const gchar *filename, gchar **contents, gsize *length, GError **gerror);
GFileError g_file_error_from_errno (gint err_no);
gint g_file_open_tmp (const gchar *tmpl, gchar **name_used, GError **gerror);
diff --git a/src/mono/mono/mini/aot-compiler.c b/src/mono/mono/mini/aot-compiler.c
index 2cd2dbc234f21..bc9e690f05fe0 100644
--- a/src/mono/mono/mini/aot-compiler.c
+++ b/src/mono/mono/mini/aot-compiler.c
@@ -9885,10 +9885,15 @@ compile_method (MonoAotCompile *acfg, MonoMethod *method)
mono_atomic_inc_i32 (&acfg->stats.ccount);
if (acfg->aot_opts.trimming_eligible_methods_outfile && acfg->trimming_eligible_methods_outfile != NULL) {
- if (!mono_method_is_generic_impl (method) && method->token != 0 && !cfg->deopt && !cfg->interp_entry_only && mini_get_interp_callbacks ()->jit_call_can_be_supported (method, mono_method_signature_internal (method), acfg->aot_opts.llvm_only)) {
- // The call back to jit_call_can_be_supported is necessary for WASM, because it would still interprete some methods sometimes even though they were already AOT'ed.
+ if (!mono_method_is_generic_impl (method) && method->token != 0 && !cfg->deopt && !cfg->interp_entry_only) {
+ // The call to mono_jit_call_can_be_supported_by_interp is necessary for WASM, because it would still interprete some methods sometimes even though they were already AOT'ed.
// When that happens, interpreter needs to have the capability to call the AOT'ed version of that method, since the method body has already been trimmed.
- fprintf (acfg->trimming_eligible_methods_outfile, "%x\n", method->token);
+ gboolean skip_trim = FALSE;
+ if (acfg->aot_opts.interp) {
+ skip_trim = (!mono_jit_call_can_be_supported_by_interp (method, mono_method_signature_internal (method), acfg->aot_opts.llvm_only) || (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED));
+ }
+ if (!skip_trim)
+ fprintf (acfg->trimming_eligible_methods_outfile, "%x\n", method->token);
}
}
}
@@ -14917,7 +14922,7 @@ aot_assembly (MonoAssembly *ass, guint32 jit_opts, MonoAotOptions *aot_options)
}
if (acfg->aot_opts.trimming_eligible_methods_outfile && acfg->dedup_phase != DEDUP_COLLECT) {
- acfg->trimming_eligible_methods_outfile = fopen (acfg->aot_opts.trimming_eligible_methods_outfile, "w+");
+ acfg->trimming_eligible_methods_outfile = g_fopen (acfg->aot_opts.trimming_eligible_methods_outfile, "w");
if (!acfg->trimming_eligible_methods_outfile)
aot_printerrf (acfg, "Unable to open trimming-eligible-methods-outfile specified file %s\n", acfg->aot_opts.trimming_eligible_methods_outfile);
else {
diff --git a/src/mono/mono/mini/ee.h b/src/mono/mono/mini/ee.h
index 1e88e9aaaa9d8..caa04bef85872 100644
--- a/src/mono/mono/mini/ee.h
+++ b/src/mono/mono/mini/ee.h
@@ -65,7 +65,6 @@ typedef gpointer MonoInterpFrameHandle;
MONO_EE_CALLBACK (void, entry_llvmonly, (gpointer res, gpointer *args, gpointer imethod)) \
MONO_EE_CALLBACK (gpointer, get_interp_method, (MonoMethod *method)) \
MONO_EE_CALLBACK (MonoJitInfo*, compile_interp_method, (MonoMethod *method, MonoError *error)) \
- MONO_EE_CALLBACK (gboolean, jit_call_can_be_supported, (MonoMethod *method, MonoMethodSignature *sig, gboolean is_llvm_only)) \
typedef struct _MonoEECallbacks {
diff --git a/src/mono/mono/mini/interp-stubs.c b/src/mono/mono/mini/interp-stubs.c
index 0ade0c95e0440..8d1487c8876ea 100644
--- a/src/mono/mono/mini/interp-stubs.c
+++ b/src/mono/mono/mini/interp-stubs.c
@@ -252,12 +252,6 @@ stub_compile_interp_method (MonoMethod *method, MonoError *error)
return NULL;
}
-static gboolean
-stub_jit_call_can_be_supported (MonoMethod *method, MonoMethodSignature *sig, gboolean is_llvm_only)
-{
- return TRUE;
-}
-
#undef MONO_EE_CALLBACK
#define MONO_EE_CALLBACK(ret, name, sig) stub_ ## name,
diff --git a/src/mono/mono/mini/interp/interp-internals.h b/src/mono/mono/mini/interp/interp-internals.h
index 4e0be7db04334..9be34b46d8457 100644
--- a/src/mono/mono/mini/interp/interp-internals.h
+++ b/src/mono/mono/mini/interp/interp-internals.h
@@ -320,9 +320,6 @@ mono_mint_type (MonoType *type);
int
mono_interp_type_size (MonoType *type, int mt, int *align_p);
-gboolean
-interp_jit_call_can_be_supported (MonoMethod *method, MonoMethodSignature *sig, gboolean is_llvm_only);
-
#if HOST_BROWSER
gboolean
diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c
index 295861c5703e1..c35a61ded1167 100644
--- a/src/mono/mono/mini/interp/interp.c
+++ b/src/mono/mono/mini/interp/interp.c
@@ -3837,7 +3837,7 @@ mono_interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClause
ip += 3;
MINT_IN_BREAK;
MINT_IN_CASE(MINT_NIY)
- g_printf ("MONO interpreter: NIY encountered in method %s\n", frame->imethod->method->name);
+ g_printf ("MONO interpreter: NIY encountered in method %s\n", mono_method_full_name (frame->imethod->method, TRUE));
g_assert_not_reached ();
MINT_IN_BREAK;
MINT_IN_CASE(MINT_BREAK)
@@ -3937,20 +3937,49 @@ mono_interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClause
guint16 params_offset = ip [1];
guint16 params_size = ip [3];
- // Copy the params to their location at the start of the frame
- memmove (frame->stack, (guchar*)frame->stack + params_offset, params_size);
new_method = (InterpMethod*)frame->imethod->data_items [ip [2]];
if (*ip == MINT_TAILCALL_VIRT) {
gint16 slot = (gint16)ip [4];
- MonoObject *this_arg = LOCAL_VAR (0, MonoObject*);
+ MonoObject **this_arg_p = (MonoObject **)((guchar*)frame->stack + params_offset);
+ MonoObject *this_arg = *this_arg_p;
new_method = get_virtual_method_fast (new_method, this_arg->vtable, slot);
if (m_class_is_valuetype (this_arg->vtable->klass) && m_class_is_valuetype (new_method->method->klass)) {
/* unbox */
gpointer unboxed = mono_object_unbox_internal (this_arg);
- LOCAL_VAR (0, gpointer) = unboxed;
+ *this_arg_p = unboxed;
+ }
+
+ InterpMethodCodeType code_type = new_method->code_type;
+
+ g_assert (code_type == IMETHOD_CODE_UNKNOWN ||
+ code_type == IMETHOD_CODE_INTERP ||
+ code_type == IMETHOD_CODE_COMPILED);
+
+ if (G_UNLIKELY (code_type == IMETHOD_CODE_UNKNOWN)) {
+ // FIXME push/pop LMF
+ MonoMethodSignature *sig = mono_method_signature_internal (new_method->method);
+ if (mono_interp_jit_call_supported (new_method->method, sig))
+ code_type = IMETHOD_CODE_COMPILED;
+ else
+ code_type = IMETHOD_CODE_INTERP;
+ new_method->code_type = code_type;
+ }
+
+ if (code_type == IMETHOD_CODE_COMPILED) {
+ error_init_reuse (error);
+ do_jit_call (context, frame->retval, (stackval*)((guchar*)frame->stack + params_offset), frame, new_method, error);
+ if (!is_ok (error)) {
+ MonoException *call_ex = interp_error_convert_to_exception (frame, error, ip);
+ THROW_EX (call_ex, ip);
+ }
+
+ goto exit_frame;
}
}
+
+ // Copy the params to their location at the start of the frame
+ memmove (frame->stack, (guchar*)frame->stack + params_offset, params_size);
} else {
new_method = (InterpMethod*)frame->imethod->data_items [ip [1]];
}
@@ -4057,39 +4086,7 @@ mono_interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClause
}
ip += 6;
- InterpMethodCodeType code_type = cmethod->code_type;
-
- g_assert (code_type == IMETHOD_CODE_UNKNOWN ||
- code_type == IMETHOD_CODE_INTERP ||
- code_type == IMETHOD_CODE_COMPILED);
-
- if (G_UNLIKELY (code_type == IMETHOD_CODE_UNKNOWN)) {
- // FIXME push/pop LMF
- MonoMethodSignature *sig = mono_method_signature_internal (cmethod->method);
- if (mono_interp_jit_call_supported (cmethod->method, sig))
- code_type = IMETHOD_CODE_COMPILED;
- else
- code_type = IMETHOD_CODE_INTERP;
- cmethod->code_type = code_type;
- }
-
- if (code_type == IMETHOD_CODE_INTERP) {
-
- goto call;
-
- } else if (code_type == IMETHOD_CODE_COMPILED) {
- frame->state.ip = ip;
- error_init_reuse (error);
- do_jit_call (context, (stackval*)(locals + return_offset), (stackval*)(locals + call_args_offset), frame, cmethod, error);
- if (!is_ok (error)) {
- MonoException *call_ex = interp_error_convert_to_exception (frame, error, ip);
- THROW_EX (call_ex, ip);
- }
-
- CHECK_RESUME_STATE (context);
- }
-
- MINT_IN_BREAK;
+ goto jit_call;
}
MINT_IN_CASE(MINT_CALLI) {
gboolean need_unbox;
@@ -4111,7 +4108,7 @@ mono_interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClause
}
ip += 4;
- goto call;
+ goto jit_call;
}
MINT_IN_CASE(MINT_CALLI_NAT_FAST) {
MintICallSig icall_sig = (MintICallSig)ip [4];
@@ -4141,7 +4138,7 @@ mono_interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClause
cmethod = mono_interp_get_native_func_wrapper (frame->imethod, csignature, code);
ip += 5;
- goto call;
+ goto jit_call;
}
MINT_IN_CASE(MINT_CALLI_NAT) {
MonoMethodSignature *csignature = (MonoMethodSignature*)frame->imethod->data_items [ip [4]];
@@ -4181,39 +4178,41 @@ mono_interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClause
LOCAL_VAR (call_args_offset, gpointer) = unboxed;
}
- InterpMethodCodeType code_type = cmethod->code_type;
+jit_call:
+ {
+ InterpMethodCodeType code_type = cmethod->code_type;
- g_assert (code_type == IMETHOD_CODE_UNKNOWN ||
- code_type == IMETHOD_CODE_INTERP ||
- code_type == IMETHOD_CODE_COMPILED);
+ g_assert (code_type == IMETHOD_CODE_UNKNOWN ||
+ code_type == IMETHOD_CODE_INTERP ||
+ code_type == IMETHOD_CODE_COMPILED);
- if (G_UNLIKELY (code_type == IMETHOD_CODE_UNKNOWN)) {
- // FIXME push/pop LMF
- MonoMethodSignature *sig = mono_method_signature_internal (cmethod->method);
- if (mono_interp_jit_call_supported (cmethod->method, sig))
- code_type = IMETHOD_CODE_COMPILED;
- else
- code_type = IMETHOD_CODE_INTERP;
- cmethod->code_type = code_type;
- }
+ if (G_UNLIKELY (code_type == IMETHOD_CODE_UNKNOWN)) {
+ // FIXME push/pop LMF
+ MonoMethodSignature *sig = mono_method_signature_internal (cmethod->method);
+ if (mono_interp_jit_call_supported (cmethod->method, sig))
+ code_type = IMETHOD_CODE_COMPILED;
+ else
+ code_type = IMETHOD_CODE_INTERP;
+ cmethod->code_type = code_type;
+ }
- if (code_type == IMETHOD_CODE_INTERP) {
+ if (code_type == IMETHOD_CODE_INTERP) {
- goto call;
+ goto interp_call;
- } else if (code_type == IMETHOD_CODE_COMPILED) {
- frame->state.ip = ip;
- error_init_reuse (error);
- do_jit_call (context, (stackval*)(locals + return_offset), (stackval*)(locals + call_args_offset), frame, cmethod, error);
- if (!is_ok (error)) {
- MonoException *call_ex = interp_error_convert_to_exception (frame, error, ip);
- THROW_EX (call_ex, ip);
- }
+ } else if (code_type == IMETHOD_CODE_COMPILED) {
+ frame->state.ip = ip;
+ error_init_reuse (error);
+ do_jit_call (context, (stackval*)(locals + return_offset), (stackval*)(locals + call_args_offset), frame, cmethod, error);
+ if (!is_ok (error)) {
+ MonoException *call_ex = interp_error_convert_to_exception (frame, error, ip);
+ THROW_EX (call_ex, ip);
+ }
- CHECK_RESUME_STATE (context);
+ CHECK_RESUME_STATE (context);
+ }
+ MINT_IN_BREAK;
}
-
- MINT_IN_BREAK;
}
MINT_IN_CASE(MINT_CALL_VARARG) {
// Same as MINT_CALL, except at ip [4] we have the index for the csignature,
@@ -4222,7 +4221,7 @@ mono_interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClause
return_offset = ip [1];
call_args_offset = ip [2];
ip += 6;
- goto call;
+ goto jit_call;
}
MINT_IN_CASE(MINT_CALL) {
@@ -4235,7 +4234,8 @@ mono_interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClause
#else
ip += 4;
#endif
-call:
+
+interp_call:
/*
* Make a non-recursive call by loading the new interpreter state based on child frame,
* and going back to the main loop.
@@ -5651,7 +5651,7 @@ MINT_IN_CASE(MINT_BRTRUE_I8_SP) ZEROP_SP(gint64, !=); MINT_IN_BREAK;
// by the call, even though the call has void return (?!).
LOCAL_VAR (call_args_offset, gpointer) = NULL;
ip += 4;
- goto call;
+ goto jit_call;
}
MINT_IN_CASE(MINT_NEWOBJ_STRING_UNOPT) {
// Same as MINT_NEWOBJ_STRING but copy params into right place on stack
@@ -5666,7 +5666,7 @@ MINT_IN_CASE(MINT_BRTRUE_I8_SP) ZEROP_SP(gint64, !=); MINT_IN_BREAK;
call_args_offset = aligned_call_args_offset;
LOCAL_VAR (call_args_offset, gpointer) = NULL;
ip += 4;
- goto call;
+ goto jit_call;
}
MINT_IN_CASE(MINT_NEWOBJ) {
MonoVTable *vtable = (MonoVTable*) frame->imethod->data_items [ip [4]];
@@ -5689,8 +5689,8 @@ MINT_IN_CASE(MINT_BRTRUE_I8_SP) ZEROP_SP(gint64, !=); MINT_IN_BREAK;
ip += 5;
cmethod = (InterpMethod*)frame->imethod->data_items [imethod_index];
- goto call;
- MINT_IN_BREAK;
+
+ goto jit_call;
}
MINT_IN_CASE(MINT_NEWOBJ_INLINED) {
MonoVTable *vtable = (MonoVTable*) frame->imethod->data_items [ip [2]];
@@ -5723,8 +5723,7 @@ MINT_IN_CASE(MINT_BRTRUE_I8_SP) ZEROP_SP(gint64, !=); MINT_IN_BREAK;
ip += 5;
cmethod = (InterpMethod*)frame->imethod->data_items [imethod_index];
- goto call;
- MINT_IN_BREAK;
+ goto jit_call;
}
MINT_IN_CASE(MINT_NEWOBJ_VT_INLINED) {
guint16 ret_size = ip [3];
@@ -5766,7 +5765,7 @@ MINT_IN_CASE(MINT_BRTRUE_I8_SP) ZEROP_SP(gint64, !=); MINT_IN_BREAK;
mono_interp_error_cleanup (error); // FIXME: do not swallow the error
EXCEPTION_CHECKPOINT;
ip += 4;
- goto call;
+ goto jit_call;
}
MINT_IN_CASE(MINT_ROL_I4_IMM) {
@@ -8602,31 +8601,6 @@ interp_sufficient_stack (gsize size)
return (context->stack_pointer + size) < (context->stack_start + INTERP_STACK_SIZE);
}
-gboolean
-interp_jit_call_can_be_supported (MonoMethod *method, MonoMethodSignature *sig, gboolean is_llvm_only)
-{
- if (sig->param_count > 10)
- return FALSE;
- if (sig->pinvoke)
- return FALSE;
- if (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)
- return FALSE;
- if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL)
- return FALSE;
- if (!is_llvm_only && method->is_inflated)
- return FALSE;
- if (method->string_ctor)
- return FALSE;
- if (method->wrapper_type != MONO_WRAPPER_NONE)
- return FALSE;
-
- if (method->flags & METHOD_ATTRIBUTE_REQSECOBJ)
- /* Used to mark methods containing StackCrawlMark locals */
- return FALSE;
-
- return TRUE;
-}
-
static void
interp_cleanup (void)
{
diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c
index 0df4de58a075d..aabe294d172c2 100644
--- a/src/mono/mono/mini/interp/transform.c
+++ b/src/mono/mono/mini/interp/transform.c
@@ -1214,7 +1214,7 @@ mono_interp_jit_call_supported (MonoMethod *method, MonoMethodSignature *sig)
{
GSList *l;
- if (!interp_jit_call_can_be_supported (method, sig, mono_llvm_only))
+ if (!mono_jit_call_can_be_supported_by_interp (method, sig, mono_llvm_only))
return FALSE;
if (mono_aot_only && m_class_get_image (method->klass)->aot_module && !(method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)) {
@@ -3550,10 +3550,20 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target
}
CHECK_STACK_RET (td, csignature->param_count + csignature->hasthis, FALSE);
+
+ gboolean skip_tailcall = FALSE;
+ if (tailcall && !is_virtual && target_method != NULL) {
+ MonoMethodHeader *mh = interp_method_get_header (target_method, error);
+ if (mh != NULL && mh->code_size == 0)
+ skip_tailcall = TRUE;
+ mono_metadata_free_mh (mh);
+ }
+
if (tailcall && !td->gen_sdb_seq_points && !calli && op == -1 &&
(target_method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) == 0 &&
(target_method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) == 0 &&
- !(target_method->iflags & METHOD_IMPL_ATTRIBUTE_NOINLINING)) {
+ !(target_method->iflags & METHOD_IMPL_ATTRIBUTE_NOINLINING) &&
+ !skip_tailcall) {
(void)mono_class_vtable_checked (target_method->klass, error);
return_val_if_nok (error, FALSE);
diff --git a/src/mono/mono/mini/mini-runtime.c b/src/mono/mono/mini/mini-runtime.c
index b7aabc74549ce..257ca8fb0d4c3 100644
--- a/src/mono/mono/mini/mini-runtime.c
+++ b/src/mono/mono/mini/mini-runtime.c
@@ -5562,3 +5562,28 @@ mono_invoke_runtime_init_callback (void)
mono_atomic_xchg_i64 ((volatile gint64 *)&runtime_init_thread_id, (gint64)G_MAXUINT64);
}
}
+
+gboolean
+mono_jit_call_can_be_supported_by_interp (MonoMethod *method, MonoMethodSignature *sig, gboolean is_llvm_only)
+{
+ if (sig->param_count > 10)
+ return FALSE;
+ if (sig->pinvoke)
+ return FALSE;
+ if (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)
+ return FALSE;
+ if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL)
+ return FALSE;
+ if (!is_llvm_only && method->is_inflated)
+ return FALSE;
+ if (method->string_ctor)
+ return FALSE;
+ if (method->wrapper_type != MONO_WRAPPER_NONE)
+ return FALSE;
+
+ if (method->flags & METHOD_ATTRIBUTE_REQSECOBJ)
+ /* Used to mark methods containing StackCrawlMark locals */
+ return FALSE;
+
+ return TRUE;
+}
diff --git a/src/mono/mono/mini/mini-runtime.h b/src/mono/mono/mini/mini-runtime.h
index 19826d4c3f86b..1e4cb5dc1eb8e 100644
--- a/src/mono/mono/mini/mini-runtime.h
+++ b/src/mono/mono/mini/mini-runtime.h
@@ -659,6 +659,9 @@ mono_post_native_crash_handler (const char *signal, MonoContext *mctx, MONO_SIG_
gboolean
mono_is_addr_implicit_null_check (void *addr);
+gboolean
+mono_jit_call_can_be_supported_by_interp (MonoMethod *method, MonoMethodSignature *sig, gboolean is_llvm_only);
+
/*
* Signal handling
*/
diff --git a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets
index b15c7298c8f26..13a8719e62240 100644
--- a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets
+++ b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets
@@ -414,8 +414,16 @@ Copyright (c) .NET Foundation. All rights reserved.
<_WasmEmitSourceMapPublish Condition="'$(_WasmEmitSourceMapPublish)' == ''">false
+
+ <_WasmResolvedFilesToPublish Include="@(ResolvedFileToPublish)" />
+
+
+ <_WasmResolvedFilesToPublish Include="@(WasmAssembliesFinal)" />
+ <_WasmResolvedFilesToPublish Include="@(ResolvedFileToPublish)" Condition="'%(Extension)' != '.dll'" />
+
+
-
+
diff --git a/src/mono/wasi/build/WasiApp.targets b/src/mono/wasi/build/WasiApp.targets
index 2dc3755948814..1811e61146446 100644
--- a/src/mono/wasi/build/WasiApp.targets
+++ b/src/mono/wasi/build/WasiApp.targets
@@ -382,6 +382,7 @@
ExtraFilesToDeploy="@(WasmExtraFilesToDeploy)"
NativeAssets="@(WasmNativeAsset)"
DebugLevel="$(WasmDebugLevel)"
+ RuntimeConfigJsonPath="$(_WasmRuntimeConfigFilePath)"
/>
diff --git a/src/mono/wasm/Wasm.Build.Tests/Blazor/BuildPublishTests.cs b/src/mono/wasm/Wasm.Build.Tests/Blazor/BuildPublishTests.cs
index 2f5ef58e6bf29..28130327e719b 100644
--- a/src/mono/wasm/Wasm.Build.Tests/Blazor/BuildPublishTests.cs
+++ b/src/mono/wasm/Wasm.Build.Tests/Blazor/BuildPublishTests.cs
@@ -84,8 +84,8 @@ public void DefaultTemplate_NoAOT_WithWorkload(string config, bool testUnicode)
public void DefaultTemplate_AOT_WithWorkload(string config, bool testUnicode)
{
string id = testUnicode ?
- $"blz_no_aot_{config}_{GetRandomId()}_{s_unicodeChar}" :
- $"blz_no_aot_{config}_{GetRandomId()}";
+ $"blz_aot_{config}_{GetRandomId()}_{s_unicodeChar}" :
+ $"blz_aot_{config}_{GetRandomId()}";
CreateBlazorWasmTemplateProject(id);
BlazorBuild(new BlazorBuildOptions(id, config, NativeFilesType.FromRuntimePack));
@@ -173,4 +173,28 @@ void AssertResourcesDlls(string basePath)
}
}
}
+
+ [Theory]
+ [InlineData("", false)] // Default case
+ [InlineData("true", true)] // the other case
+ public async Task Test_WasmStripILAfterAOT(string stripILAfterAOT, bool expectILStripping)
+ {
+ string config = "Release";
+ string id = $"blz_WasmStripILAfterAOT_{config}_{GetRandomId()}";
+ string projectFile = CreateBlazorWasmTemplateProject(id);
+ string projectDirectory = Path.GetDirectoryName(projectFile)!;
+
+ string extraProperties = "true";
+ if (!string.IsNullOrEmpty(stripILAfterAOT))
+ extraProperties += $"{stripILAfterAOT}";
+ AddItemsPropertiesToProject(projectFile, extraProperties);
+
+ BlazorPublish(new BlazorBuildOptions(id, config, NativeFilesType.AOT, AssertAppBundle : false));
+ await BlazorRunForPublishWithWebServer(new BlazorRunOptions() { Config = config });
+
+ string frameworkDir = Path.Combine(projectDirectory, "bin", config, BuildTestBase.DefaultTargetFrameworkForBlazor, "publish", "wwwroot", "_framework");
+ string objBuildDir = Path.Combine(projectDirectory, "obj", config, BuildTestBase.DefaultTargetFrameworkForBlazor, "wasm", "for-publish");
+
+ WasmTemplateTests.TestWasmStripILAfterAOTOutput(objBuildDir, frameworkDir, expectILStripping, _testOutput);
+ }
}
diff --git a/src/mono/wasm/Wasm.Build.Tests/Common/Utils.cs b/src/mono/wasm/Wasm.Build.Tests/Common/Utils.cs
index d064829fe289b..b6d087a013575 100644
--- a/src/mono/wasm/Wasm.Build.Tests/Common/Utils.cs
+++ b/src/mono/wasm/Wasm.Build.Tests/Common/Utils.cs
@@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
+using System.IO.Compression;
using System.Runtime.InteropServices;
using System.Text;
using Xunit;
@@ -58,4 +59,16 @@ public static void DirectoryCopy(string sourceDirName, string destDirName, Func<
}
}
+ public static string GZipCompress(string fileSelected)
+ {
+ FileInfo fileToCompress = new FileInfo(fileSelected);
+ string compressedFileName = fileToCompress.FullName + ".gz";
+
+ using FileStream originalFileStream = fileToCompress.OpenRead();
+ using FileStream compressedFileStream = File.Create(compressedFileName);
+ using GZipStream compressionStream = new(compressedFileStream, CompressionMode.Compress);
+ originalFileStream.CopyTo(compressionStream);
+
+ return compressedFileName;
+ }
}
diff --git a/src/mono/wasm/Wasm.Build.Tests/NativeRebuildTests/FlagsChangeRebuildTest.cs b/src/mono/wasm/Wasm.Build.Tests/NativeRebuildTests/FlagsChangeRebuildTest.cs
index 8d495f923626c..9f18293b3c848 100644
--- a/src/mono/wasm/Wasm.Build.Tests/NativeRebuildTests/FlagsChangeRebuildTest.cs
+++ b/src/mono/wasm/Wasm.Build.Tests/NativeRebuildTests/FlagsChangeRebuildTest.cs
@@ -1,7 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
diff --git a/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTests.cs b/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTests.cs
index 9b14bedd59b50..626ae725b1524 100644
--- a/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTests.cs
+++ b/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTests.cs
@@ -491,5 +491,103 @@ public void BuildAndRunForDifferentOutputPaths(string config, bool appendRID, bo
.ExecuteWithCapturedOutput($"run --no-silent --no-build -c {config} x y z")
.EnsureSuccessful();
}
+
+ [Theory]
+ [InlineData("", false)] // Default case
+ [InlineData("true", true)] // the other case
+ public void Test_WasmStripILAfterAOT(string stripILAfterAOT, bool expectILStripping)
+ {
+ string config = "Release";
+ string id = $"strip_{config}_{GetRandomId()}";
+ string projectFile = CreateWasmTemplateProject(id, "wasmconsole");
+ string projectName = Path.GetFileNameWithoutExtension(projectFile);
+ string projectDirectory = Path.GetDirectoryName(projectFile)!;
+ bool aot = true;
+
+ UpdateProgramCS();
+ UpdateConsoleMainJs();
+
+ string extraProperties = "true";
+ if (!string.IsNullOrEmpty(stripILAfterAOT))
+ extraProperties += $"{stripILAfterAOT}";
+ AddItemsPropertiesToProject(projectFile, extraProperties);
+
+ var buildArgs = new BuildArgs(projectName, config, aot, id, null);
+ buildArgs = ExpandBuildArgs(buildArgs);
+
+ BuildTemplateProject(buildArgs,
+ id: id,
+ new BuildProjectOptions(
+ CreateProject: false,
+ HasV8Script: false,
+ MainJS: "main.mjs",
+ Publish: true,
+ TargetFramework: BuildTestBase.DefaultTargetFramework,
+ UseCache: false,
+ IsBrowserProject: false,
+ AssertAppBundle: false));
+
+ string runArgs = $"run --no-silent --no-build -c {config}";
+ var res = new RunCommand(s_buildEnv, _testOutput, label: id)
+ .WithWorkingDirectory(_projectDir!)
+ .ExecuteWithCapturedOutput(runArgs)
+ .EnsureExitCode(42);
+
+ string frameworkDir = Path.Combine(projectDirectory, "bin", config, BuildTestBase.DefaultTargetFramework, "browser-wasm", "AppBundle", "_framework");
+ string objBuildDir = Path.Combine(projectDirectory, "obj", config, BuildTestBase.DefaultTargetFramework, "browser-wasm", "wasm", "for-publish");
+ TestWasmStripILAfterAOTOutput(objBuildDir, frameworkDir, expectILStripping, _testOutput);
+ }
+
+ internal static void TestWasmStripILAfterAOTOutput(string objBuildDir, string frameworkDir, bool expectILStripping, ITestOutputHelper testOutput)
+ {
+ string origAssemblyDir = Path.Combine(objBuildDir, "aot-in");
+ string strippedAssemblyDir = Path.Combine(objBuildDir, "stripped");
+ Assert.True(Directory.Exists(origAssemblyDir), $"Could not find the original AOT input assemblies dir: {origAssemblyDir}");
+ if (expectILStripping)
+ Assert.True(Directory.Exists(strippedAssemblyDir), $"Could not find the stripped assemblies dir: {strippedAssemblyDir}");
+ else
+ Assert.False(Directory.Exists(strippedAssemblyDir), $"Expected {strippedAssemblyDir} to not exist");
+
+ string assemblyToExamine = "System.Private.CoreLib.dll";
+ string originalAssembly = Path.Combine(objBuildDir, origAssemblyDir, assemblyToExamine);
+ string strippedAssembly = Path.Combine(objBuildDir, strippedAssemblyDir, assemblyToExamine);
+ string bundledAssembly = Path.Combine(frameworkDir, Path.ChangeExtension(assemblyToExamine, ProjectProviderBase.WasmAssemblyExtension));
+ Assert.True(File.Exists(originalAssembly), $"Expected {nameof(originalAssembly)} {originalAssembly} to exist");
+ Assert.True(File.Exists(bundledAssembly), $"Expected {nameof(bundledAssembly)} {bundledAssembly} to exist");
+ if (expectILStripping)
+ Assert.True(File.Exists(strippedAssembly), $"Expected {nameof(strippedAssembly)} {strippedAssembly} to exist");
+ else
+ Assert.False(File.Exists(strippedAssembly), $"Expected {strippedAssembly} to not exist");
+
+ string compressedOriginalAssembly = Utils.GZipCompress(originalAssembly);
+ string compressedBundledAssembly = Utils.GZipCompress(bundledAssembly);
+ FileInfo compressedOriginalAssembly_fi = new FileInfo(compressedOriginalAssembly);
+ FileInfo compressedBundledAssembly_fi = new FileInfo(compressedBundledAssembly);
+
+ testOutput.WriteLine ($"compressedOriginalAssembly_fi: {compressedOriginalAssembly_fi.Length}, {compressedOriginalAssembly}");
+ testOutput.WriteLine ($"compressedBundledAssembly_fi: {compressedBundledAssembly_fi.Length}, {compressedBundledAssembly}");
+
+ if (expectILStripping)
+ {
+ if (!UseWebcil)
+ {
+ string compressedStrippedAssembly = Utils.GZipCompress(strippedAssembly);
+ FileInfo compressedStrippedAssembly_fi = new FileInfo(compressedStrippedAssembly);
+ testOutput.WriteLine ($"compressedStrippedAssembly_fi: {compressedStrippedAssembly_fi.Length}, {compressedStrippedAssembly}");
+ Assert.True(compressedOriginalAssembly_fi.Length > compressedStrippedAssembly_fi.Length, $"Expected original assembly({compressedOriginalAssembly}) size ({compressedOriginalAssembly_fi.Length}) " +
+ $"to be bigger than the stripped assembly ({compressedStrippedAssembly}) size ({compressedStrippedAssembly_fi.Length})");
+ Assert.True(compressedBundledAssembly_fi.Length == compressedStrippedAssembly_fi.Length, $"Expected bundled assembly({compressedBundledAssembly}) size ({compressedBundledAssembly_fi.Length}) " +
+ $"to be the same as the stripped assembly ({compressedStrippedAssembly}) size ({compressedStrippedAssembly_fi.Length})");
+ }
+ }
+ else
+ {
+ if (!UseWebcil)
+ {
+ // FIXME: The bundled file would be .wasm in case of webcil, so can't compare size
+ Assert.True(compressedOriginalAssembly_fi.Length == compressedBundledAssembly_fi.Length);
+ }
+ }
+ }
}
}
diff --git a/src/mono/wasm/build/WasmApp.LocalBuild.targets b/src/mono/wasm/build/WasmApp.LocalBuild.targets
index 15d3a7b584938..d41a00d53853f 100644
--- a/src/mono/wasm/build/WasmApp.LocalBuild.targets
+++ b/src/mono/wasm/build/WasmApp.LocalBuild.targets
@@ -22,6 +22,7 @@
+
diff --git a/src/mono/wasm/build/WasmApp.Native.targets b/src/mono/wasm/build/WasmApp.Native.targets
index 6f4b748b3fbec..9c22d0d7be94a 100644
--- a/src/mono/wasm/build/WasmApp.Native.targets
+++ b/src/mono/wasm/build/WasmApp.Native.targets
@@ -714,14 +714,15 @@
-
+
-
+
+
+ <_WasmAssembliesInternal Remove="@(_WasmAssembliesInternal)"/>
+ <_WasmAssembliesInternal Include="@(_UpdatedAssembliesAfterILStrip)"/>
+
<_BitcodeFile Include="%(_WasmAssembliesInternal.LlvmBitcodeFile)" />
diff --git a/src/mono/wasm/build/WasmApp.targets b/src/mono/wasm/build/WasmApp.targets
index db47100639c4f..6da897b0e81ce 100644
--- a/src/mono/wasm/build/WasmApp.targets
+++ b/src/mono/wasm/build/WasmApp.targets
@@ -87,7 +87,7 @@
- AppBundle directly contains user files
- AppBundle/_framework contains generated files (dlls, runtime scripts, icu)
- AppBundle/_content contains web files from nuget packages (css, js, etc)
- - $(WasmStripILAfterAOT) - Set to true to enable trimming away AOT compiled methods body (IL code)
+ - $(WasmStripILAfterAOT) - Set to true to enable trimming away AOT compiled methods body (IL code)
Defaults to false.
Public items:
@@ -150,7 +150,6 @@
.wasm
.dll
-
false
_framework
@@ -456,6 +455,7 @@
WasmIcuDataFileName="$(WasmIcuDataFileName)"
RuntimeAssetsLocation="$(WasmRuntimeAssetsLocation)"
CacheBootResources="$(BlazorCacheBootResources)"
+ RuntimeConfigJsonPath="$(_WasmRuntimeConfigFilePath)"
>
@@ -509,6 +509,7 @@
+
diff --git a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs
index 0a761b44a4c8f..bee003cada5b4 100644
--- a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs
+++ b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs
@@ -15,7 +15,6 @@
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using System.Reflection.PortableExecutable;
-using System.Text.Json.Serialization;
public class MonoAOTCompiler : Microsoft.Build.Utilities.Task
{
@@ -296,6 +295,7 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task
private IList? _assembliesToCompile;
private ConcurrentDictionary compiledAssemblies = new();
+ private BuildPropertiesTable? _propertiesTable;
private MonoAotMode parsedAotMode;
private MonoAotOutputType parsedOutputType;
@@ -467,6 +467,7 @@ private bool ProcessAndValidateArguments()
return !Log.HasLoggedErrors;
}
+
public override bool Execute()
{
try
@@ -491,6 +492,9 @@ private bool ExecuteInternal()
if (!ProcessAndValidateArguments())
return false;
+ string propertiesTableFilePath = Path.Combine(IntermediateOutputPath, "monoAotPropertyValues.txt");
+ _propertiesTable = new BuildPropertiesTable(propertiesTableFilePath);
+
IEnumerable managedAssemblies = FilterOutUnmanagedAssemblies(Assemblies);
managedAssemblies = EnsureAllAssembliesInTheSameDir(managedAssemblies);
_assembliesToCompile = managedAssemblies.Where(f => !ShouldSkipForAOT(f)).ToList();
@@ -567,6 +571,8 @@ all assigned to that one partition.
}
CheckExportSymbolsFile(_assembliesToCompile);
+ _propertiesTable.Table[nameof(CollectTrimmingEligibleMethods)] = CollectTrimmingEligibleMethods.ToString();
+ _propertiesTable.Save(propertiesTableFilePath, Log);
CompiledAssemblies = ConvertAssembliesDictToOrderedList(compiledAssemblies, _assembliesToCompile).ToArray();
return !Log.HasLoggedErrors;
}
@@ -654,7 +660,7 @@ private PrecompileArguments GetPrecompileArgumentsFor(ITaskItem assemblyItem, st
{
string assembly = assemblyItem.GetMetadata("FullPath");
string assemblyDir = Path.GetDirectoryName(assembly)!;
- var aotAssembly = new TaskItem(assembly);
+ var aotAssembly = new TaskItem(assembly, assemblyItem.CloneCustomMetadata());
var aotArgs = new List();
var processArgs = new List();
bool isDedup = Path.GetFileName(assembly) == Path.GetFileName(DedupAssembly);
@@ -1272,6 +1278,45 @@ public PrecompileArguments(string ResponseFilePath, IDictionary
public ITaskItem AOTAssembly { get; private set; }
public IList ProxyFiles { get; private set; }
}
+
+ private sealed class BuildPropertiesTable
+ {
+ public Dictionary Table { get; private set; }
+
+ public BuildPropertiesTable(string propertiesFilePath)
+ {
+ Table = Read(propertiesFilePath) ?? new();
+ }
+
+ public bool GetBool(string propertyName, bool defaultValue)
+ => bool.TryParse(Table[propertyName], out bool outValue) ? outValue : defaultValue;
+
+ private static Dictionary? Read(string propertiesFilePath)
+ {
+ if (!File.Exists(propertiesFilePath))
+ return null;
+
+ string text = File.ReadAllText(propertiesFilePath);
+ if (text.Length == 0)
+ return null;
+
+ try
+ {
+ return JsonSerializer.Deserialize>(text);
+ }
+ catch (Exception e)
+ {
+ throw new LogAsErrorException($"Failed to parse properties table from {propertiesFilePath}: {e}");
+ }
+ }
+
+ public void Save(string filePath, TaskLoggingHelper log)
+ {
+ string jsonString = JsonSerializer.Serialize(Table);
+ File.WriteAllText(filePath, jsonString);
+ log.LogMessage(MessageImportance.Low, $"Logged Mono AOT Properties in {filePath}");
+ }
+ }
}
public enum MonoAotMode
diff --git a/src/tasks/MonoTargetsTasks/ILStrip/ILStrip.cs b/src/tasks/MonoTargetsTasks/ILStrip/ILStrip.cs
index 719064d9387b8..6a25bd26168b9 100644
--- a/src/tasks/MonoTargetsTasks/ILStrip/ILStrip.cs
+++ b/src/tasks/MonoTargetsTasks/ILStrip/ILStrip.cs
@@ -16,6 +16,7 @@
using System.Reflection.Metadata.Ecma335;
using System.Reflection.PortableExecutable;
using System.Buffers;
+using System.Collections.Concurrent;
public class ILStrip : Microsoft.Build.Utilities.Task
{
@@ -37,15 +38,22 @@ public class ILStrip : Microsoft.Build.Utilities.Task
public bool TrimIndividualMethods { get; set; }
///
- /// Assembilies got trimmed successfully.
+ /// The location to store the trimmed assemblies, when provided.
+ ///
+ public string? IntermediateOutputPath { get; set; }
+
+ ///
+ /// Contains the updated list of assemblies comparing to the input variable Assemblies.
+ /// Replaced the trimmed ones with their new location.
///
- /// Successful trimming will set the following metadata on the items:
- /// - TrimmedAssemblyFileName
+ /// Added two metadata for trimmed items:
+ /// - UntrimmedAssemblyFilePath
+ /// - ILStripped: set to true to indicate this item is trimmed
///
[Output]
- public ITaskItem[]? TrimmedAssemblies { get; set; }
+ public ITaskItem[]? UpdatedAssemblies { get; set; }
- private readonly List _trimmedAssemblies = new();
+ private ConcurrentDictionary _processedAssemblies = new();
public override bool Execute()
{
@@ -54,6 +62,18 @@ public override bool Execute()
throw new ArgumentException($"'{nameof(Assemblies)}' is required.", nameof(Assemblies));
}
+ string trimmedAssemblyFolder = string.Empty;
+ if (TrimIndividualMethods)
+ {
+ trimmedAssemblyFolder = string.IsNullOrEmpty(IntermediateOutputPath) ? "stripped" : Path.Combine(IntermediateOutputPath, "stripped");
+ if (!Directory.Exists(trimmedAssemblyFolder))
+ {
+ Directory.CreateDirectory(trimmedAssemblyFolder);
+ }
+ }
+
+ Log.LogMessage(MessageImportance.High, "IL stripping assemblies");
+
int allowedParallelism = DisableParallelStripping ? 1 : Math.Min(Assemblies.Length, Environment.ProcessorCount);
if (BuildEngine is IBuildEngine9 be9)
allowedParallelism = be9.RequestCores(allowedParallelism);
@@ -68,14 +88,14 @@ public override bool Execute()
}
else
{
- if (!TrimMethods(assemblyItem))
+ if (!TrimMethods(assemblyItem, trimmedAssemblyFolder))
state.Stop();
}
});
if (TrimIndividualMethods)
{
- TrimmedAssemblies = _trimmedAssemblies.ToArray();
+ UpdatedAssemblies = ConvertAssembliesDictToOrderedList(_processedAssemblies, Assemblies).ToArray();
}
if (!result.IsCompleted && !Log.HasLoggedErrors)
@@ -114,7 +134,7 @@ private bool StripAssembly(ITaskItem assemblyItem)
return true;
}
- private bool TrimMethods(ITaskItem assemblyItem)
+ private bool TrimMethods(ITaskItem assemblyItem, string trimmedAssemblyFolder)
{
string assemblyFilePathArg = assemblyItem.ItemSpec;
string methodTokenFile = assemblyItem.GetMetadata("MethodTokenFile");
@@ -143,7 +163,27 @@ private bool TrimMethods(ITaskItem assemblyItem)
return true;
}
- string trimmedAssemblyFilePath = ComputeTrimmedAssemblyPath(assemblyFilePath);
+ string trimmedAssemblyFilePath = ComputeTrimmedAssemblyPath(trimmedAssemblyFolder, assemblyFilePath);
+ if (File.Exists(trimmedAssemblyFilePath))
+ {
+ if (IsInputNewerThanOutput(assemblyFilePath, trimmedAssemblyFilePath))
+ {
+ Log.LogMessage(MessageImportance.Low, $"Re-trimming {assemblyFilePath} because {trimmedAssemblyFilePath} is older than {assemblyFilePath} .");
+ Log.LogMessage(MessageImportance.Low, $"Deleting {trimmedAssemblyFilePath} .");
+ File.Delete(trimmedAssemblyFilePath);
+ }
+ else
+ {
+ Log.LogMessage(MessageImportance.Low, $"Skip trimming {assemblyFilePath} because {trimmedAssemblyFilePath} is newer than {assemblyFilePath} .");
+ _processedAssemblies.GetOrAdd(assemblyItem.ItemSpec, GetTrimmedAssemblyItem(assemblyItem, trimmedAssemblyFilePath, assemblyFilePathArg));
+ return true;
+ }
+ }
+ else
+ {
+ Log.LogMessage(MessageImportance.Low, $"Trimming {assemblyFilePath} .");
+ }
+
bool isTrimmed = false;
using FileStream fs = File.Open(assemblyFilePath, FileMode.Open);
using PEReader peReader = new(fs, PEStreamOptions.LeaveOpen);
@@ -164,26 +204,30 @@ private bool TrimMethods(ITaskItem assemblyItem)
CreateTrimmedAssembly(peReader, trimmedAssemblyFilePath, fs, methodBodyUses);
}
- if (isTrimmed)
- {
- AddItemToTrimmedList(assemblyFilePathArg, trimmedAssemblyFilePath);
- }
+ var outAssemblyItem = isTrimmed ? GetTrimmedAssemblyItem(assemblyItem, trimmedAssemblyFilePath, assemblyFilePathArg) : assemblyItem;
+ _processedAssemblies.GetOrAdd(assemblyItem.ItemSpec, outAssemblyItem);
return true;
}
- private static string ComputeTrimmedAssemblyPath(string assemblyFilePath)
+ private static string ComputeTrimmedAssemblyPath(string trimmedAssemblyFolder, string assemblyFilePath)
{
- string? assemblyPath = Path.GetDirectoryName(assemblyFilePath);
- string? assemblyName = Path.GetFileNameWithoutExtension(assemblyFilePath);
- if (string.IsNullOrEmpty(assemblyPath))
- {
- return (assemblyName + "_trimmed.dll");
- }
- else
+ string? assemblyName = Path.GetFileName(assemblyFilePath);
+ return Path.Combine(trimmedAssemblyFolder, assemblyName);
+ }
+
+ private static bool IsInputNewerThanOutput(string inFile, string outFile)
+ => File.GetLastWriteTimeUtc(inFile) > File.GetLastWriteTimeUtc(outFile);
+
+ private static List ConvertAssembliesDictToOrderedList(ConcurrentDictionary dict, IList originalAssemblies)
+ {
+ List outItems = new(originalAssemblies.Count);
+ foreach (ITaskItem item in originalAssemblies)
{
- return Path.Combine(assemblyPath, (assemblyName + "_trimmed.dll"));
+ if (dict.TryGetValue(item.GetMetadata("FullPath"), out ITaskItem? dictItem))
+ outItems.Add(dictItem);
}
+ return outItems;
}
private static string ComputeGuid(MetadataReader mr)
@@ -303,10 +347,12 @@ private static void ZeroOutMethodBody(ref MemoryStream memStream, int methodSize
ArrayPool.Shared.Return(zeroBuffer);
}
- private void AddItemToTrimmedList(string assemblyFilePath, string trimmedAssemblyFilePath)
+ private static TaskItem GetTrimmedAssemblyItem(ITaskItem assemblyItem, string trimmedAssemblyFilePath, string originAssemblyFilePath)
{
- var trimmedAssemblyItem = new TaskItem(assemblyFilePath);
- trimmedAssemblyItem.SetMetadata("TrimmedAssemblyFileName", trimmedAssemblyFilePath);
- _trimmedAssemblies.Add(trimmedAssemblyItem);
+ TaskItem newAssemblyItem = new(assemblyItem);
+ newAssemblyItem.ItemSpec = trimmedAssemblyFilePath;
+ newAssemblyItem.SetMetadata("UntrimmedAssemblyFilePath", originAssemblyFilePath);
+ newAssemblyItem.SetMetadata("ILStripped", "true");
+ return newAssemblyItem;
}
}
diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilderBaseTask.cs b/src/tasks/WasmAppBuilder/WasmAppBuilderBaseTask.cs
index c533e404c4173..c580fcd80eff1 100644
--- a/src/tasks/WasmAppBuilder/WasmAppBuilderBaseTask.cs
+++ b/src/tasks/WasmAppBuilder/WasmAppBuilderBaseTask.cs
@@ -22,6 +22,8 @@ public abstract class WasmAppBuilderBaseTask : Task
[Required]
public string[] Assemblies { get; set; } = Array.Empty();
+ public string? RuntimeConfigJsonPath { get; set; }
+
// files like dotnet.native.wasm, icudt.dat etc
[NotNull]
[Required]
@@ -89,6 +91,15 @@ protected void ProcessSatelliteAssemblies(Action<(string fullPath, string cultur
protected virtual void UpdateRuntimeConfigJson()
{
+ if (string.IsNullOrEmpty(RuntimeConfigJsonPath))
+ return;
+
+ if (!File.Exists(RuntimeConfigJsonPath))
+ {
+ Log.LogMessage(MessageImportance.Low, $"Could not find {nameof(RuntimeConfigJsonPath)}={RuntimeConfigJsonPath}. Ignoring.");
+ return;
+ }
+
string[] matchingAssemblies = Assemblies.Where(asm => Path.GetFileName(asm) == MainAssemblyName).ToArray();
if (matchingAssemblies.Length == 0)
throw new LogAsErrorException($"Could not find main assembly named {MainAssemblyName} in the list of assemblies");
@@ -96,23 +107,16 @@ protected virtual void UpdateRuntimeConfigJson()
if (matchingAssemblies.Length > 1)
throw new LogAsErrorException($"Found more than one assembly matching the main assembly name {MainAssemblyName}: {string.Join(",", matchingAssemblies)}");
- string runtimeConfigPath = Path.ChangeExtension(matchingAssemblies[0], ".runtimeconfig.json");
- if (!File.Exists(runtimeConfigPath))
- {
- Log.LogMessage(MessageImportance.Low, $"Could not find {runtimeConfigPath}. Ignoring.");
- return;
- }
-
- var rootNode = JsonNode.Parse(File.ReadAllText(runtimeConfigPath),
+ var rootNode = JsonNode.Parse(File.ReadAllText(RuntimeConfigJsonPath),
new JsonNodeOptions { PropertyNameCaseInsensitive = true });
if (rootNode == null)
- throw new LogAsErrorException($"Failed to parse {runtimeConfigPath}");
+ throw new LogAsErrorException($"Failed to parse {RuntimeConfigJsonPath}");
JsonObject? rootObject = rootNode.AsObject();
if (!rootObject.TryGetPropertyValue("runtimeOptions", out JsonNode? runtimeOptionsNode)
|| !(runtimeOptionsNode is JsonObject runtimeOptionsObject))
{
- throw new LogAsErrorException($"Could not find node named 'runtimeOptions' in {runtimeConfigPath}");
+ throw new LogAsErrorException($"Could not find node named 'runtimeOptions' in {RuntimeConfigJsonPath}");
}
JsonObject wasmHostProperties = runtimeOptionsObject.GetOrCreate("wasmHostProperties", () => new JsonObject());
@@ -151,13 +155,13 @@ protected virtual void UpdateRuntimeConfigJson()
AddToRuntimeConfig(wasmHostProperties: wasmHostProperties, runtimeArgsArray: runtimeArgsArray, perHostConfigs: perHostConfigs);
- string dstPath = Path.Combine(AppDir!, Path.GetFileName(runtimeConfigPath));
+ string dstPath = Path.Combine(AppDir!, Path.GetFileName(RuntimeConfigJsonPath));
using FileStream? fs = new FileStream(dstPath, FileMode.Create, FileAccess.Write, FileShare.None);
using var writer = new Utf8JsonWriter(fs, new JsonWriterOptions { Indented = true });
rootObject.WriteTo(writer);
_fileWrites.Add(dstPath);
- Log.LogMessage(MessageImportance.Low, $"Generated {dstPath} from {runtimeConfigPath}");
+ Log.LogMessage(MessageImportance.Low, $"Generated {dstPath} from {RuntimeConfigJsonPath}");
}
protected virtual void AddToRuntimeConfig(JsonObject wasmHostProperties, JsonArray runtimeArgsArray, JsonArray perHostConfigs)