From 621d79835548348d2618e087e62b922fa04619ff Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Fri, 2 Apr 2021 16:43:15 -0400 Subject: [PATCH 1/4] [mbr] Start adding an Apple sample Runs, but doesn't do deltas yet --- src/mono/sample/mbr/README.md | 27 ++++++- src/mono/sample/mbr/apple/AppleDelta.csproj | 76 ++++++++++++++++++++ src/mono/sample/mbr/apple/Makefile | 14 ++++ src/mono/sample/mbr/apple/Program.cs | 45 ++++++++++++ src/mono/sample/mbr/apple/main.m | 80 +++++++++++++++++++++ 5 files changed, 241 insertions(+), 1 deletion(-) create mode 100644 src/mono/sample/mbr/apple/AppleDelta.csproj create mode 100644 src/mono/sample/mbr/apple/Makefile create mode 100644 src/mono/sample/mbr/apple/Program.cs create mode 100644 src/mono/sample/mbr/apple/main.m diff --git a/src/mono/sample/mbr/README.md b/src/mono/sample/mbr/README.md index ea118382f4395..6e78382e10387 100644 --- a/src/mono/sample/mbr/README.md +++ b/src/mono/sample/mbr/README.md @@ -30,7 +30,19 @@ For WebAssembly: Make sure `EMSDK_PATH` is set (see [workflow](../../../../docs/workflow/building/libraries/webassembly-instructions.md)) ```console -build.sh --os browser /p:MonoMetadataUpdate=true +build.sh --os browser +``` + +For Apple targets: + +```console +build.sh --os MacCatalyst -s Mono+Libs +``` + +or + +```console +build.sh --os iOSSimulator -s Mono+Libs ``` ## Running @@ -50,3 +62,16 @@ make CONFIG=Debug && make CONFIG=Debug run ``` Then go to http://localhost:8000/ and click the button once or twice (the example has 2 updates prebuilt) + +For Apple targets: + +for ios simulator +``` +make run-sim +``` + +for Mac Catalyst + +``` +make run-catalyst +``` diff --git a/src/mono/sample/mbr/apple/AppleDelta.csproj b/src/mono/sample/mbr/apple/AppleDelta.csproj new file mode 100644 index 0000000000000..c0850d766a4b4 --- /dev/null +++ b/src/mono/sample/mbr/apple/AppleDelta.csproj @@ -0,0 +1,76 @@ + + + Exe + bin + $(NetCoreAppToolCurrent) + $(ArtifactsBinDir)microsoft.netcore.app.runtime.$(TargetOS.ToLower())-$(TargetArchitecture)\$(Configuration)\runtimes\$(TargetOS.ToLower())-$(TargetArchitecture)\ + false + $(TargetOS.ToLower())-$(TargetArchitecture) + $(DefineConstants);CI_TEST + true + + + + - + + + + + + + $(ArtifactsBinDir)microsoft.netcore.app.runtime.$(RuntimeIdentifier)\$(Configuration) + + + + + + + + + + + $(MSBuildThisFileDirectory)$(PublishDir)\app + iPhone 11 + True + true + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mono/sample/mbr/apple/Makefile b/src/mono/sample/mbr/apple/Makefile new file mode 100644 index 0000000000000..2f162bf6b5bcb --- /dev/null +++ b/src/mono/sample/mbr/apple/Makefile @@ -0,0 +1,14 @@ +CONFIG=Debug +MONO_ARCH=x64 +DOTNET := ../../../../../dotnet.sh + +run-sim: + $(DOTNET) publish -c $(CONFIG) /p:TargetOS=iOSSimulator /p:TargetArchitecture=$(MONO_ARCH) \ + /p:UseLLVM=False /p:ForceAOT=False /p:MonoForceInterpreter=true + +run-catalyst: + $(DOTNET) publish -c $(CONFIG) /p:TargetOS=MacCatalyst /p:TargetArchitecture=$(MONO_ARCH) \ + /p:UseLLVM=False /p:ForceAOT=False /p:MonoForceInterpreter=true + +clean: + rm -rf bin diff --git a/src/mono/sample/mbr/apple/Program.cs b/src/mono/sample/mbr/apple/Program.cs new file mode 100644 index 0000000000000..4a62ba5d33d1d --- /dev/null +++ b/src/mono/sample/mbr/apple/Program.cs @@ -0,0 +1,45 @@ +// 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.Threading; +using System.Threading.Tasks; +using System.Runtime.InteropServices; + +public static class Program +{ + // Defined in main.m + [DllImport("__Internal")] + private static extern void ios_set_text(string value); + + [DllImport("__Internal")] + unsafe private static extern void ios_register_button_click(delegate* unmanaged callback); + + private static int counter = 0; + + // Called by native code, see main.m + [UnmanagedCallersOnly] + private static void OnButtonClick() + { + ios_set_text("OnButtonClick! #" + counter++); + } + + public static async Task Main(string[] args) + { + unsafe { + // Register a managed callback (will be called by UIButton, see main.m) + delegate* unmanaged unmanagedPtr = &OnButtonClick; + ios_register_button_click(unmanagedPtr); + } + const string msg = "Hello World!\n.NET 5.0"; + for (int i = 0; i < msg.Length; i++) + { + // a kind of an animation + ios_set_text(msg.Substring(0, i + 1)); + await Task.Delay(100); + } + + Console.WriteLine("Done!"); + await Task.Delay(-1); + } +} diff --git a/src/mono/sample/mbr/apple/main.m b/src/mono/sample/mbr/apple/main.m new file mode 100644 index 0000000000000..0ab407e839868 --- /dev/null +++ b/src/mono/sample/mbr/apple/main.m @@ -0,0 +1,80 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#import +#import "runtime.h" + +@interface ViewController : UIViewController +@end + +@interface AppDelegate : UIResponder +@property (strong, nonatomic) UIWindow *window; +@property (strong, nonatomic) ViewController *controller; +@end + +@implementation AppDelegate +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + self.controller = [[ViewController alloc] initWithNibName:nil bundle:nil]; + self.window.rootViewController = self.controller; + [self.window makeKeyAndVisible]; + return YES; +} +@end + +UILabel *label; +void (*clickHandlerPtr)(void); + +@implementation ViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + + label = [[UILabel alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + label.textColor = [UIColor greenColor]; + label.font = [UIFont boldSystemFontOfSize: 30]; + label.numberOfLines = 2; + label.textAlignment = NSTextAlignmentCenter; + [self.view addSubview:label]; + + UIButton *button = [UIButton buttonWithType:UIButtonTypeInfoDark]; + [button addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside]; + [button setFrame:CGRectMake(50, 300, 200, 50)]; + [button setTitle:@"Click me" forState:UIControlStateNormal]; + [button setExclusiveTouch:YES]; + [self.view addSubview:button]; + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + mono_ios_runtime_init (); + }); +} +-(void) buttonClicked:(UIButton*)sender +{ + if (clickHandlerPtr) + clickHandlerPtr(); +} + +@end + +// called from C# sample +void +ios_register_button_click (void* ptr) +{ + clickHandlerPtr = ptr; +} + +// called from C# sample +void +ios_set_text (const char* value) +{ + NSString* nsstr = [NSString stringWithUTF8String:strdup(value)]; + dispatch_async(dispatch_get_main_queue(), ^{ + label.text = nsstr; + }); +} + +int main(int argc, char * argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} From 7670acacd7a62b450661faa572125b159e80181f Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Mon, 5 Apr 2021 15:12:57 -0400 Subject: [PATCH 2/4] [mbr] Add an apple sample Works on iOS simulator and Mac Catalyst --- .../sample/mbr/DeltaHelper/DeltaHelper.cs | 3 +- src/mono/sample/mbr/apple/AppleDelta.csproj | 21 ++++++++++++- src/mono/sample/mbr/apple/ChangeablePart.cs | 9 ++++++ .../sample/mbr/apple/ChangeablePart_v1.cs | 9 ++++++ src/mono/sample/mbr/apple/Program.cs | 16 +++++++++- src/mono/sample/mbr/apple/deltascript.json | 5 ++++ src/mono/sample/mbr/apple/main.m | 30 +++++++++++++++++-- 7 files changed, 88 insertions(+), 5 deletions(-) create mode 100644 src/mono/sample/mbr/apple/ChangeablePart.cs create mode 100644 src/mono/sample/mbr/apple/ChangeablePart_v1.cs create mode 100644 src/mono/sample/mbr/apple/deltascript.json diff --git a/src/mono/sample/mbr/DeltaHelper/DeltaHelper.cs b/src/mono/sample/mbr/DeltaHelper/DeltaHelper.cs index 89d025403bd4d..1deb8e798a35f 100644 --- a/src/mono/sample/mbr/DeltaHelper/DeltaHelper.cs +++ b/src/mono/sample/mbr/DeltaHelper/DeltaHelper.cs @@ -48,7 +48,8 @@ private static Action MakeUpdateMethod (Method private static void LoadMetadataUpdate (Assembly assm, byte[] dmeta_data, byte[] dil_data, byte[] dpdb_data) { - UpdateMethod (assm, dmeta_data, dil_data, dpdb_data); + // UpdateMethod (assm, dmeta_data, dil_data, dpdb_data); + System.Reflection.Metadata.AssemblyExtensions.ApplyUpdate (assm, dmeta_data, dil_data, dpdb_data); } DeltaHelper () { } diff --git a/src/mono/sample/mbr/apple/AppleDelta.csproj b/src/mono/sample/mbr/apple/AppleDelta.csproj index c0850d766a4b4..2b040dab21f00 100644 --- a/src/mono/sample/mbr/apple/AppleDelta.csproj +++ b/src/mono/sample/mbr/apple/AppleDelta.csproj @@ -8,6 +8,21 @@ $(TargetOS.ToLower())-$(TargetArchitecture) $(DefineConstants);CI_TEST true + false + + + + + + + + + + + + + deltascript.json + 1 @@ -50,6 +65,7 @@ ProjectName="AppleDelta" MonoRuntimeHeaders="$(MicrosoftNetCoreAppRuntimePackDir)native\include\mono-2.0" Assemblies="@(BundleAssemblies)" + NativeMainSource="$(MSBuildThisFileDirectory)\main.m" MainLibraryFileName="AppleDelta.dll" GenerateXcodeProject="True" BuildAppBundle="True" @@ -66,11 +82,14 @@ - + + + + diff --git a/src/mono/sample/mbr/apple/ChangeablePart.cs b/src/mono/sample/mbr/apple/ChangeablePart.cs new file mode 100644 index 0000000000000..fae03c01143dc --- /dev/null +++ b/src/mono/sample/mbr/apple/ChangeablePart.cs @@ -0,0 +1,9 @@ +using System; + +public class ChangeablePart +{ + public static int UpdateCounter (ref int counter) + { + return ++counter; + } +} diff --git a/src/mono/sample/mbr/apple/ChangeablePart_v1.cs b/src/mono/sample/mbr/apple/ChangeablePart_v1.cs new file mode 100644 index 0000000000000..07f0d59321380 --- /dev/null +++ b/src/mono/sample/mbr/apple/ChangeablePart_v1.cs @@ -0,0 +1,9 @@ +using System; + +public class ChangeablePart +{ + public static int UpdateCounter (ref int counter) + { + return --counter; + } +} diff --git a/src/mono/sample/mbr/apple/Program.cs b/src/mono/sample/mbr/apple/Program.cs index 4a62ba5d33d1d..d7c9a54f7563f 100644 --- a/src/mono/sample/mbr/apple/Program.cs +++ b/src/mono/sample/mbr/apple/Program.cs @@ -15,22 +15,36 @@ public static class Program [DllImport("__Internal")] unsafe private static extern void ios_register_button_click(delegate* unmanaged callback); + [DllImport("__Internal")] + unsafe private static extern void ios_register_applyupdate_click(delegate* unmanaged callback); + private static int counter = 0; // Called by native code, see main.m [UnmanagedCallersOnly] private static void OnButtonClick() { - ios_set_text("OnButtonClick! #" + counter++); + ios_set_text("OnButtonClick! #" + ChangeablePart.UpdateCounter (ref counter)); } + [UnmanagedCallersOnly] + private static void OnApplyUpdateClick() + { + deltaHelper.Update (typeof(ChangeablePart).Assembly); + } + + static MonoDelta.DeltaHelper deltaHelper; + public static async Task Main(string[] args) { unsafe { // Register a managed callback (will be called by UIButton, see main.m) delegate* unmanaged unmanagedPtr = &OnButtonClick; ios_register_button_click(unmanagedPtr); + delegate* unmanaged unmanagedPtr2 = &OnApplyUpdateClick; + ios_register_applyupdate_click(unmanagedPtr2); } + deltaHelper = MonoDelta.DeltaHelper.Make(); const string msg = "Hello World!\n.NET 5.0"; for (int i = 0; i < msg.Length; i++) { diff --git a/src/mono/sample/mbr/apple/deltascript.json b/src/mono/sample/mbr/apple/deltascript.json new file mode 100644 index 0000000000000..7d1d3547d8ce1 --- /dev/null +++ b/src/mono/sample/mbr/apple/deltascript.json @@ -0,0 +1,5 @@ +{ + "changes": [ + {"document": "ChangeablePart.cs", "update": "ChangeablePart_v1.cs"}, + ] +} diff --git a/src/mono/sample/mbr/apple/main.m b/src/mono/sample/mbr/apple/main.m index 0ab407e839868..05089ef5cd497 100644 --- a/src/mono/sample/mbr/apple/main.m +++ b/src/mono/sample/mbr/apple/main.m @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#include + #import #import "runtime.h" @@ -24,6 +26,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( UILabel *label; void (*clickHandlerPtr)(void); +void (*clickHandlerApplyUpdatePtr)(void); @implementation ViewController @@ -35,15 +38,23 @@ - (void)viewDidLoad { label.font = [UIFont boldSystemFontOfSize: 30]; label.numberOfLines = 2; label.textAlignment = NSTextAlignmentCenter; + label.text = @"Hello, wire me up!\n(dllimport ios_set_text)"; [self.view addSubview:label]; UIButton *button = [UIButton buttonWithType:UIButtonTypeInfoDark]; [button addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside]; - [button setFrame:CGRectMake(50, 300, 200, 50)]; - [button setTitle:@"Click me" forState:UIControlStateNormal]; + [button setFrame:CGRectMake(50, 250, 200, 50)]; + [button setTitle:@"Click me (wire me up)" forState:UIControlStateNormal]; [button setExclusiveTouch:YES]; [self.view addSubview:button]; + UIButton *apply_button = [UIButton buttonWithType:UIButtonTypeInfoDark]; + [apply_button addTarget:self action:@selector(applyUpdateButtonClicked:) forControlEvents:UIControlEventTouchUpInside]; + [apply_button setFrame:CGRectMake(50, 300, 200, 50)]; + [apply_button setTitle:@"ApplyUpdate" forState:UIControlStateNormal]; + [apply_button setExclusiveTouch:YES]; + [self.view addSubview:apply_button]; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ mono_ios_runtime_init (); }); @@ -54,6 +65,12 @@ -(void) buttonClicked:(UIButton*)sender clickHandlerPtr(); } +-(void) applyUpdateButtonClicked:(UIButton*)sender +{ + if (clickHandlerApplyUpdatePtr) + clickHandlerApplyUpdatePtr(); +} + @end // called from C# sample @@ -63,6 +80,13 @@ -(void) buttonClicked:(UIButton*)sender clickHandlerPtr = ptr; } +// called from C# sample +void +ios_register_applyupdate_click (void* ptr) +{ + clickHandlerApplyUpdatePtr = ptr; +} + // called from C# sample void ios_set_text (const char* value) @@ -75,6 +99,8 @@ -(void) buttonClicked:(UIButton*)sender int main(int argc, char * argv[]) { @autoreleasepool { + setenv("DOTNET_MODIFIABLE_ASSEMBLIES", "Debug", 1); return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } } + From f36e0706c8b30084d46a27d228a68c2d87252bb1 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Mon, 5 Apr 2021 16:56:52 -0400 Subject: [PATCH 3/4] delete unused DeltaHelper code Now that System.Reflection.Metadata.AssemblyExtensions.ApplyUpdate is in the BCL officially, I don't need the reflection-based invoke code anymore --- .../sample/mbr/DeltaHelper/DeltaHelper.cs | 42 +------------------ 1 file changed, 1 insertion(+), 41 deletions(-) diff --git a/src/mono/sample/mbr/DeltaHelper/DeltaHelper.cs b/src/mono/sample/mbr/DeltaHelper/DeltaHelper.cs index 1deb8e798a35f..c199e3020faa3 100644 --- a/src/mono/sample/mbr/DeltaHelper/DeltaHelper.cs +++ b/src/mono/sample/mbr/DeltaHelper/DeltaHelper.cs @@ -7,49 +7,9 @@ namespace MonoDelta { public class DeltaHelper { - private static Action _updateMethod; - - private static Action UpdateMethod => _updateMethod ?? InitUpdateMethod(); - - private static Action InitUpdateMethod () - { - var monoType = typeof(System.Reflection.Metadata.AssemblyExtensions); - const string methodName = "ApplyUpdate"; - var mi = monoType.GetMethod (methodName, BindingFlags.Public | BindingFlags.Static); - if (mi == null) - throw new Exception ($"Couldn't get {methodName} from {monoType.FullName}"); - _updateMethod = MakeUpdateMethod (mi); //Delegate.CreateDelegate (typeof(Action), mi) as Action; - return _updateMethod; - } - - private static Action MakeUpdateMethod (MethodInfo applyUpdate) - { - // Make - // void ApplyUpdateArray (Assembly a, byte[] dmeta, byte[] dil, byte[] dpdb) - // { - // ApplyUpdate (a, (ReadOnlySpan)dmeta, (ReadOnlySpan)dil, (ReadOnlySpan)dpdb); - // } - var dm = new DynamicMethod ("CallApplyUpdate", typeof(void), new Type[] { typeof(Assembly), typeof(byte[]), typeof(byte[]), typeof(byte[])}, typeof (DeltaHelper).Module); - var ilg = dm.GetILGenerator (); - var conv = typeof(ReadOnlySpan).GetMethod("op_Implicit", new Type[] {typeof(byte[])}); - - ilg.Emit (OpCodes.Ldarg_0); - ilg.Emit (OpCodes.Ldarg_1); - ilg.Emit (OpCodes.Call, conv); - ilg.Emit (OpCodes.Ldarg_2); - ilg.Emit (OpCodes.Call, conv); - ilg.Emit (OpCodes.Ldarg_3); - ilg.Emit (OpCodes.Call, conv); - ilg.Emit (OpCodes.Call, applyUpdate); - ilg.Emit (OpCodes.Ret); - - return dm.CreateDelegate(typeof(Action)) as Action; - } - private static void LoadMetadataUpdate (Assembly assm, byte[] dmeta_data, byte[] dil_data, byte[] dpdb_data) { - // UpdateMethod (assm, dmeta_data, dil_data, dpdb_data); - System.Reflection.Metadata.AssemblyExtensions.ApplyUpdate (assm, dmeta_data, dil_data, dpdb_data); + System.Reflection.Metadata.AssemblyExtensions.ApplyUpdate (assm, dmeta_data, dil_data, dpdb_data); } DeltaHelper () { } From faf707d3eabd01d8b6f9decdfdb3498a3293b42f Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Mon, 5 Apr 2021 16:57:36 -0400 Subject: [PATCH 4/4] fix indentation --- src/mono/sample/mbr/apple/ChangeablePart.cs | 2 +- src/mono/sample/mbr/apple/ChangeablePart_v1.cs | 2 +- src/mono/sample/mbr/apple/Program.cs | 8 ++++---- src/mono/sample/mbr/apple/deltascript.json | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/mono/sample/mbr/apple/ChangeablePart.cs b/src/mono/sample/mbr/apple/ChangeablePart.cs index fae03c01143dc..b084ac631282d 100644 --- a/src/mono/sample/mbr/apple/ChangeablePart.cs +++ b/src/mono/sample/mbr/apple/ChangeablePart.cs @@ -4,6 +4,6 @@ public class ChangeablePart { public static int UpdateCounter (ref int counter) { - return ++counter; + return ++counter; } } diff --git a/src/mono/sample/mbr/apple/ChangeablePart_v1.cs b/src/mono/sample/mbr/apple/ChangeablePart_v1.cs index 07f0d59321380..9dc830b27b59f 100644 --- a/src/mono/sample/mbr/apple/ChangeablePart_v1.cs +++ b/src/mono/sample/mbr/apple/ChangeablePart_v1.cs @@ -4,6 +4,6 @@ public class ChangeablePart { public static int UpdateCounter (ref int counter) { - return --counter; + return --counter; } } diff --git a/src/mono/sample/mbr/apple/Program.cs b/src/mono/sample/mbr/apple/Program.cs index d7c9a54f7563f..6c0b619db525f 100644 --- a/src/mono/sample/mbr/apple/Program.cs +++ b/src/mono/sample/mbr/apple/Program.cs @@ -30,7 +30,7 @@ private static void OnButtonClick() [UnmanagedCallersOnly] private static void OnApplyUpdateClick() { - deltaHelper.Update (typeof(ChangeablePart).Assembly); + deltaHelper.Update (typeof(ChangeablePart).Assembly); } static MonoDelta.DeltaHelper deltaHelper; @@ -41,10 +41,10 @@ public static async Task Main(string[] args) // Register a managed callback (will be called by UIButton, see main.m) delegate* unmanaged unmanagedPtr = &OnButtonClick; ios_register_button_click(unmanagedPtr); - delegate* unmanaged unmanagedPtr2 = &OnApplyUpdateClick; - ios_register_applyupdate_click(unmanagedPtr2); + delegate* unmanaged unmanagedPtr2 = &OnApplyUpdateClick; + ios_register_applyupdate_click(unmanagedPtr2); } - deltaHelper = MonoDelta.DeltaHelper.Make(); + deltaHelper = MonoDelta.DeltaHelper.Make(); const string msg = "Hello World!\n.NET 5.0"; for (int i = 0; i < msg.Length; i++) { diff --git a/src/mono/sample/mbr/apple/deltascript.json b/src/mono/sample/mbr/apple/deltascript.json index 7d1d3547d8ce1..46bd57ac8cd90 100644 --- a/src/mono/sample/mbr/apple/deltascript.json +++ b/src/mono/sample/mbr/apple/deltascript.json @@ -1,5 +1,5 @@ { "changes": [ - {"document": "ChangeablePart.cs", "update": "ChangeablePart_v1.cs"}, + {"document": "ChangeablePart.cs", "update": "ChangeablePart_v1.cs"}, ] }