diff --git a/src/mono/sample/mbr/DeltaHelper/DeltaHelper.cs b/src/mono/sample/mbr/DeltaHelper/DeltaHelper.cs index 89d025403bd4d..c199e3020faa3 100644 --- a/src/mono/sample/mbr/DeltaHelper/DeltaHelper.cs +++ b/src/mono/sample/mbr/DeltaHelper/DeltaHelper.cs @@ -7,48 +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); } DeltaHelper () { } 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..2b040dab21f00 --- /dev/null +++ b/src/mono/sample/mbr/apple/AppleDelta.csproj @@ -0,0 +1,95 @@ + + + Exe + bin + $(NetCoreAppToolCurrent) + $(ArtifactsBinDir)microsoft.netcore.app.runtime.$(TargetOS.ToLower())-$(TargetArchitecture)\$(Configuration)\runtimes\$(TargetOS.ToLower())-$(TargetArchitecture)\ + false + $(TargetOS.ToLower())-$(TargetArchitecture) + $(DefineConstants);CI_TEST + true + false + + + + + + + + + + + + + deltascript.json + 1 + + + + - + + + + + + + $(ArtifactsBinDir)microsoft.netcore.app.runtime.$(RuntimeIdentifier)\$(Configuration) + + + + + + + + + + + $(MSBuildThisFileDirectory)$(PublishDir)\app + iPhone 11 + True + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mono/sample/mbr/apple/ChangeablePart.cs b/src/mono/sample/mbr/apple/ChangeablePart.cs new file mode 100644 index 0000000000000..b084ac631282d --- /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..9dc830b27b59f --- /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/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..6c0b619db525f --- /dev/null +++ b/src/mono/sample/mbr/apple/Program.cs @@ -0,0 +1,59 @@ +// 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); + + [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! #" + 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++) + { + // 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/deltascript.json b/src/mono/sample/mbr/apple/deltascript.json new file mode 100644 index 0000000000000..46bd57ac8cd90 --- /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 new file mode 100644 index 0000000000000..05089ef5cd497 --- /dev/null +++ b/src/mono/sample/mbr/apple/main.m @@ -0,0 +1,106 @@ +// 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" + +@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); +void (*clickHandlerApplyUpdatePtr)(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; + 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, 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 (); + }); +} +-(void) buttonClicked:(UIButton*)sender +{ + if (clickHandlerPtr) + clickHandlerPtr(); +} + +-(void) applyUpdateButtonClicked:(UIButton*)sender +{ + if (clickHandlerApplyUpdatePtr) + clickHandlerApplyUpdatePtr(); +} + +@end + +// called from C# sample +void +ios_register_button_click (void* ptr) +{ + 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) +{ + NSString* nsstr = [NSString stringWithUTF8String:strdup(value)]; + dispatch_async(dispatch_get_main_queue(), ^{ + label.text = nsstr; + }); +} + +int main(int argc, char * argv[]) { + @autoreleasepool { + setenv("DOTNET_MODIFIABLE_ASSEMBLIES", "Debug", 1); + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} +