Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 7 additions & 63 deletions docs/website/binding_types_reference_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -1306,28 +1306,9 @@ This should map to Objective-C/clang use of `__attribute__((objc_designated_init

### DisableZeroCopyAttribute

This attribute is applied to string parameters or string properties and
instructs the code generator to not use the zero-copy string marshaling for
this parameter, and instead create a new NSString instance from the C# string.
This attribute is only required on strings if you instruct the generator to use
zero-copy string marshaling using either the `--zero-copy` command
line option or setting the assembly-level attribute `ZeroCopyStringsAttribute`.

This is necessary in cases where the property is declared in Objective-C to
be a `retain` or `assign` property instead of a `copy` property. These typically
happen in third-party libraries that have been wrongly "optimized" by
developers. In general, `retain` or `assign` `NSString` properties are incorrect
since `NSMutableString` or user-derived classes of `NSString` might alter the
contents of the strings without the knowledge of the library code, subtly
breaking the application. Typically this happens due to premature
optimization.

The following shows two such properties in Objective-C:

```csharp
@property(nonatomic,retain) NSString *name;
@property(nonatomic,assign) NSString *name2;
```
> **Note:** This attribute is obsolete and has no effect. Zero-copy string
> marshaling is no longer supported. The attribute is preserved for source
> compatibility but can be safely removed from binding definitions.

<a name="DisposeAttribute"></a>

Expand Down Expand Up @@ -2435,47 +2416,10 @@ This corresponds to `clang` [`__attribute__((objc_requires_super))`](https://cla

### ZeroCopyStringsAttribute

Only available in Xamarin.iOS 5.4 and newer.

This attribute instructs the generator that the binding for this specific
library (if applied with `[assembly:]`) or type should use the fast
zero-copy string marshaling. This attribute is equivalent to passing the
command line option `--zero-copy` to the generator.

When using zero-copy for strings, the generator effectively uses the same C#
string as the string that Objective-C consumes without incurring the creation of
a new `NSString` object and avoiding copying the data from the C# strings to the
Objective-C string. The only drawback of using Zero Copy strings is that you
must ensure that any string property that you wrap that happens to be flagged as
`retain` or `copy` has the `[DisableZeroCopy]` attribute set. This is
require because the handle for zero-copy strings is allocated on the stack and
is invalid upon the function return.

Example:

```csharp
[ZeroCopyStrings]
[BaseType (typeof (NSObject))]
interface MyBinding {
[Export ("name")]
string Name { get; set; }

[Export ("domain"), NullAllowed]
string Domain { get; set; }

[DisablZeroCopy]
[Export ("someRetainedNSString")]
string RetainedProperty { get; set; }
}

```

You can also apply the attribute at the assembly level, and it will apply to
all the types of the assembly:

```csharp
[assembly:ZeroCopyStrings]
```
> **Note:** This attribute is obsolete and has no effect. Zero-copy string
> marshaling is no longer supported. The `--use-zero-copy` command line option
> is also no longer supported. The attribute is preserved for source
> compatibility but can be safely removed from binding definitions.

## Strongly-typed dictionaries

Expand Down
2 changes: 1 addition & 1 deletion docs/website/generator-errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ Please go to [[FieldAttribute]](https://developer.xamarin.com/guides/cross-platf

### <a name='BI1026'/>BI1026: `*`: Enums attributed with [\*] must have an underlying type of `long` or `ulong`

### <a name='BI1027'/>BI1027: Support for ZeroCopy strings is not implemented. Strings will be marshalled as NSStrings.
### <a name='BI1027'/>BI1027: Support for ZeroCopy strings is not implemented. The --use-zero-copy option is not supported and will be ignored.

### <a name='BI1028'/>BI1028: Internal sanity check failed, please file a bug report (https://github.com/dotnet/macios/issues/new) with a test case.

Expand Down
15 changes: 4 additions & 11 deletions src/ObjCBindings/ExportTag.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;

#nullable enable
Expand Down Expand Up @@ -169,18 +170,10 @@ public enum Property : Int64 {
CustomMarshalDirective = 1 << 5,

/// <summary>
/// Apply to strings parameters that are merely retained or assigned,
/// not copied this is an exception as it is advised in the coding
/// standard for Objective-C to avoid this, but a few properties do use
/// this. Use this falg for properties flagged with `retain' or
/// `assign', which look like this:
///
/// @property (retain) NSString foo;
/// @property (assign) NSString assigned;
///
/// This forced the generator to create an NSString before calling the
/// API instead of using the fast string marshalling code.
/// This flag is obsolete and has no effect. Zero-copy string marshaling is no longer supported.
/// </summary>
[EditorBrowsable (EditorBrowsableState.Never)]
[Obsolete ("Zero-copy string marshaling is no longer supported. This flag has no effect.")]
DisableZeroCopy = 1 << 6,

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion src/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@
</data>

<data name="BI1027" xml:space="preserve">
<value>Support for ZeroCopy strings is not implemented. Strings will be marshalled as NSStrings.</value>
<value>The --use-zero-copy option is not supported and will be ignored.</value>
</data>

<data name="BI1028" xml:space="preserve">
Expand Down
40 changes: 9 additions & 31 deletions src/bgen/Attributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -517,20 +517,14 @@ public class NoDefaultValueAttribute : Attribute {
public class IgnoredInDelegateAttribute : Attribute {
}

// Apply to strings parameters that are merely retained or assigned,
// not copied this is an exception as it is advised in the coding
// standard for Objective-C to avoid this, but a few properties do use
// this. Use this attribtue for properties flagged with `retain' or
// `assign', which look like this:
//
// @property (retain) NSString foo;
// @property (assign) NSString assigned;
//
// This forced the generator to create an NSString before calling the
// API instead of using the fast string marshalling code.
#if !XAMCORE_5_0
// This attribute is obsolete and has no effect. Zero-copy string marshaling is no longer supported.
[Obsolete ("Zero-copy string marshaling is no longer supported. This attribute has no effect.")]
[AttributeUsage (AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = true)]
public class DisableZeroCopyAttribute : Attribute {
public DisableZeroCopyAttribute () { }
}
#endif

// Apply this attribute to methods that need a custom binding method.
//
Expand Down Expand Up @@ -561,29 +555,13 @@ public class MarshalDirectiveAttribute : Attribute {
public string? Library { get; set; }
}

//
// By default, the generator will not do Zero Copying of strings, as most
// third party libraries do not follow Apple's design guidelines of making
// string properties and parameters copy parameters, instead many libraries
// "retain" as a broken optimization [1].
//
// The consumer of the generator can force this by passing
// --use-zero-copy or setting the [assembly:ZeroCopyStrings] attribute.
// When these are set, the generator assumes the library perform
// copies over any NSStrings it keeps instead of retains/assigns and
// that any property that happens to be a retain/assign has the
// [DisableZeroCopyAttribute] attribute applied.
//
// [1] It is broken because consumer code can pass an NSMutableString, the
// library retains the value, but does not have a way of noticing changes
// that might happen to the mutable string behind its back.
//
// In the ZeroCopy case it is a problem because we pass handles to stack-allocated
// strings that stop existing after the invocation is over.
//
#if !XAMCORE_5_0
// This attribute is obsolete and has no effect. Zero-copy string marshaling is no longer supported.
[Obsolete ("Zero-copy string marshaling is no longer supported. This attribute has no effect.")]
[AttributeUsage (AttributeTargets.Assembly | AttributeTargets.Method | AttributeTargets.Interface, AllowMultiple = true)]
public class ZeroCopyStringsAttribute : Attribute {
}
#endif

[AttributeUsage (AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = true)]
public class SnippetAttribute : Attribute {
Expand Down
5 changes: 3 additions & 2 deletions src/bgen/BindingTouch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,9 @@ public bool TryCreateOptionSet (BindingTouchConfig config, string [] args)
{ "p", "Sets private mode", v => config.IsPublicMode = false },
{ "baselib=", "Sets the base library", v => config.Baselibdll = v },
{ "attributelib=", "Sets the attribute library", v => config.Attributedll = v },
{ "use-zero-copy", v=> config.UseZeroCopy = true },
#if !XAMCORE_5_0
{ "use-zero-copy", v=> ErrorHelper.Warning (1027) },
#endif
{ "nostdlib", "Does not reference mscorlib.dll library", l => config.OmitStandardLibrary = true },
#if !XAMCORE_5_0
{ "no-mono-path", "Launches compiler with empty MONO_PATH", l => { }, true },
Expand Down Expand Up @@ -364,7 +366,6 @@ bool TryGenerate (BindingTouchConfig config, Api api)
try {
var g = new Generator (this, api, config.IsPublicMode, config.IsExternal, config.IsDebug) {
BaseDir = config.BindingFilesOutputDirectory ?? config.TemporaryFileDirectory!,
ZeroCopyStrings = config.UseZeroCopy,
InlineSelectors = config.InlineSelectors ?? (CurrentPlatform != PlatformName.MacOSX),
};

Expand Down
91 changes: 9 additions & 82 deletions src/bgen/Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,6 @@ Nomenclator Nomenclator {
string? is_direct_binding_value; // An expression that calculates the IsDirectBinding value. Might not be a constant expression. This will be added to every constructor for a type.
bool? is_direct_binding; // If a constant value for IsDirectBinding is known, it's stored here. Will be null if no constant value is known.

// Whether to use ZeroCopy for strings, defaults to false
public bool ZeroCopyStrings;

public bool BindThirdPartyLibrary { get { return BindingTouch.BindThirdPartyLibrary; } }
public bool InlineSelectors;
public string BaseDir { get { return basedir; } set { basedir = value; } }
Expand All @@ -124,11 +121,6 @@ Nomenclator Nomenclator {
//
bool type_needs_thread_checks;

//
// If set, the members of this type will get zero copy
//
internal bool type_wants_zero_copy;

//
// Used by the public binding generator to populate the
// class with types that do not exist
Expand Down Expand Up @@ -843,16 +835,7 @@ public TrampolineInfo MakeTrampoline (Type t)
if (mai.PlainString)
return safe_name;
else {
bool allow_null = null_allowed_override || AttributeManager.IsNullable (pi);

if (mai.ZeroCopyStringMarshal) {
if (allow_null)
return String.Format ("{0} is null ? IntPtr.Zero : (IntPtr)(&_s{0})", pi.Name);
else
return String.Format ("(IntPtr)(&_s{0})", pi.Name);
} else {
return "ns" + pi.Name;
}
return "ns" + pi.Name;
}
}

Expand Down Expand Up @@ -3097,13 +3080,8 @@ void GenerateInvoke (bool stret, bool supercall, MethodInfo mi, MemberInformatio
if (mai.PlainString)
ErrorHelper.Warning (1101);

if (mai.ZeroCopyStringMarshal) {
target_name = "(IntPtr)(&_s" + pi.Name + ")";
handle = "";
} else {
target_name = "ns" + pi.Name;
handle = "";
}
target_name = "ns" + pi.Name;
handle = "";
} else
target_name = pi.Name.GetSafeParamName ();
break;
Expand Down Expand Up @@ -3248,48 +3226,16 @@ bool IsOptimizable (MemberInfo method)
// @probe_null: determines whether null is allowed, and
// whether we need to generate code to handle this
//
// @must_copy: determines whether to create a new NSString, necessary
// for NSString properties that are flagged with "retain" instead of "copy"
//
// @prefix: prefix to prepend on each line
//
// @property: the name of the property
//
public string GenerateMarshalString (bool probe_null, bool must_copy)
{
if (must_copy) {
return "var ns{0} = CFString.CreateNative ({1});\n";
}
return
"ObjCRuntime.NSStringStruct _s{0}; Console.WriteLine (\"" + CurrentMethod + ": Marshalling: {{1}}\", {1}); \n" +
"_s{0}.ClassPtr = ObjCRuntime.NSStringStruct.ReferencePtr;\n" +
"_s{0}.Flags = 0x010007d1; // RefCount=1, Unicode, InlineContents = 0, DontFreeContents\n" +
"_s{0}.UnicodePtr = _p{0};\n" +
"_s{0}.Length = " + (probe_null ? "{1} is null ? 0 : {1}.Length;" : "{1}.Length;\n");
}

public string GenerateDisposeString (bool probe_null, bool must_copy)
public string GenerateMarshalString (bool probe_null)
{
if (must_copy) {
return "CFString.ReleaseNative (ns{0});\n";
} else
return "if (_s{0}.Flags != 0x010007d1) throw new Exception (\"String was retained, not copied\");";
return "var ns{0} = CFString.CreateNative ({1});\n";
Comment on lines 3226 to +3233
}

List<string>? CollectFastStringMarshalParameters (MethodInfo mi)
public string GenerateDisposeString (bool probe_null)
{
List<string>? stringParameters = null;

foreach (var pi in mi.GetParameters ()) {
var mai = new MarshalInfo (this, mi, pi);

if (mai.ZeroCopyStringMarshal) {
if (stringParameters is null)
stringParameters = new List<string> ();
stringParameters.Add (pi.Name.GetSafeParamName () ?? "");
}
}
return stringParameters;
return "CFString.ReleaseNative (ns{0});\n";
}

AvailabilityBaseAttribute? GetIntroduced (Type? type, string methodName)
Expand Down Expand Up @@ -3371,8 +3317,8 @@ void GenerateTypeLowering (MethodInfo mi, bool null_allowed_override, out String
if (mai.Type == TypeCache.System_String && !mai.PlainString) {
bool probe_null = null_allowed_override || AttributeManager.IsNullable (pi);

convs.AppendFormat (GenerateMarshalString (probe_null, !mai.ZeroCopyStringMarshal), pi.Name, pi.Name.GetSafeParamName ());
disposes.AppendFormat (GenerateDisposeString (probe_null, !mai.ZeroCopyStringMarshal), pi.Name);
convs.AppendFormat (GenerateMarshalString (probe_null), pi.Name, pi.Name.GetSafeParamName ());
disposes.AppendFormat (GenerateDisposeString (probe_null), pi.Name);
} else if (mai.Type.TryIsArray (out var etype)) {
if (HasBindAsAttribute (pi)) {
convs.AppendFormat ("using var nsb_{0} = {1}\n", pi.Name, GetToBindAsWrapper (mi, null, pi));
Expand Down Expand Up @@ -3615,9 +3561,6 @@ public void GenerateMethodBody (MemberInformation minfo, MethodInfo mi, string?

GenerateArgumentChecks (mi, false, propInfo, out bool needsGCKeepAlives);

// Collect all strings that can be fast-marshalled
var stringParameters = CollectFastStringMarshalParameters (mi);

GenerateTypeLowering (mi, null_allowed_override, out var args, out var convs, out var disposes, out var by_ref_processing, out var by_ref_init, out var post_return, propInfo);

if (minfo.is_protocol_member && minfo.is_static) {
Expand All @@ -3629,12 +3572,6 @@ public void GenerateMethodBody (MemberInformation minfo, MethodInfo mi, string?
if (by_ref_init.Length > 0)
print (by_ref_init.ToString ());

if (stringParameters is not null) {
print ("fixed (char * {0}){{",
stringParameters.Select (name => "_p" + name + " = " + name).Aggregate ((first, second) => first + ", " + second));
indent++;
}

if (propInfo is not null && IsSetter (mi) && HasBindAsAttribute (propInfo)) {
convs.AppendFormat ("using var nsb_{0} = {1}\n", propInfo.Name, GetToBindAsWrapper (mi, minfo, null));
}
Expand Down Expand Up @@ -3834,10 +3771,6 @@ public void GenerateMethodBody (MemberInformation minfo, MethodInfo mi, string?
WriteMarkDirtyIfDerived (sw, mi.DeclaringType!);
if (post_return?.Length > 0)
print (post_return.ToString ());
if (stringParameters is not null) {
indent--;
print ("}");
}
indent--;
}

Expand Down Expand Up @@ -5769,12 +5702,6 @@ public void Generate (Type type)
if (is_rgen_type)
return;

if (ZeroCopyStrings) {
ErrorHelper.Warning (1027);
ZeroCopyStrings = false;
}

type_wants_zero_copy = AttributeManager.HasAttribute<ZeroCopyStringsAttribute> (type) || ZeroCopyStrings;
var tsa = AttributeManager.GetCustomAttribute<ThreadSafeAttribute> (type);
// if we're inside a special namespace then default is non-thread safe, otherwise default is thread safe
if (NamespaceCache.UINamespaces.Contains (type.Namespace!)) {
Expand Down
1 change: 0 additions & 1 deletion src/bgen/Models/BindingTouchConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

public class BindingTouchConfig {
public bool ShowHelp = false;
public bool UseZeroCopy = false;
public string? BindingFilesOutputDirectory = null;
public string? TemporaryFileDirectory = null;
public string? HelperClassNamespace = null;
Expand Down
Loading
Loading