diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /cli_ure/source | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream.tar.xz libreoffice-upstream.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
37 files changed, 10109 insertions, 0 deletions
diff --git a/cli_ure/source/basetypes/assembly.cs b/cli_ure/source/basetypes/assembly.cs new file mode 100644 index 000000000..ac99fffd8 --- /dev/null +++ b/cli_ure/source/basetypes/assembly.cs @@ -0,0 +1,21 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +[assembly:System.Reflection.AssemblyDescription( "CLI-UNO: Language Binding specific types" )] +[assembly:System.Reflection.AssemblyCompany( "OpenOffice.org" )] +[assembly:System.Reflection.AssemblyVersion( "@CLI_BASETYPES_NEW_VERSION@" )] diff --git a/cli_ure/source/basetypes/cli_basetypes_config b/cli_ure/source/basetypes/cli_basetypes_config new file mode 100644 index 000000000..44ef60600 --- /dev/null +++ b/cli_ure/source/basetypes/cli_basetypes_config @@ -0,0 +1,11 @@ +<?xml version="1.0"?> +<configuration> + <runtime> + <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> + <dependentAssembly> + <assemblyIdentity name="cli_basetypes" publicKeyToken="ce2cb7e279207b9e"/> + <bindingRedirect oldVersion="CLI_BASETYPES_OLD_VERSION" newVersion="CLI_BASETYPES_NEW_VERSION" /> + </dependentAssembly> + </assemblyBinding> + </runtime> +</configuration>
\ No newline at end of file diff --git a/cli_ure/source/basetypes/uno/Any.cs b/cli_ure/source/basetypes/uno/Any.cs new file mode 100644 index 000000000..ea44d0f3b --- /dev/null +++ b/cli_ure/source/basetypes/uno/Any.cs @@ -0,0 +1,202 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +using System; +using System.Text; + +namespace uno +{ + +/** This class can be used as a base class for UNO objects. + It implements the capability to be kept weakly + (unoidl.com.sun.star.uno.XWeak) and it implements + unoidl.com.sun.star.lang.XTypeProvider which is necessary for + using the object from StarBasic. +*/ +public struct Any +{ + private object _value; + private Type _type; + + public static Any VOID = new Any(typeof(void), null); + + private static void checkArgs(Type type, Object value) + { + //value can only be null if type == void + if (type == null + || (value == null + && type != typeof(void) + && type != typeof(object) + && type.IsInterface == false)) + throw new System.Exception( + "uno.Any: Constructor called with illegal arguments!"); + //If value is a polymorphic struct then type must be + //uno.Polymorphic + if (value != null) + { + TypeParametersAttribute t = (TypeParametersAttribute) Attribute.GetCustomAttribute( + value.GetType(), typeof(TypeParametersAttribute)); + if (t != null && !(type is PolymorphicType)) + throw new System.Exception( + "uno.Any: The value has a polymorphic type but the type argument is not " + + "uno.PolymorphicType. Please use the constructor Any(Type, object) and " + + "supply a uno.PolymorphicType as type argument!"); + } + } + + /** constructs an instance. + + <p>If the arguments are invalid then an exception is thrown.</p> + @exception System.Exception + */ + public Any(Type type, object value) + { + checkArgs(type, value); + _type = type; + _value = value; + } + + /** sets the type and value. + <p>If the arguments ar invalid then an exception is thrown.</p> + @exception System.Exception + */ + public void setValue(Type type, object value) + { + checkArgs(type, value); + _type = type; + _value = value; + } + + public Type Type + { + get + { + if (_type == null) + _type = typeof(void); + return _type; + } + } + + public Object Value + { + get + { + return _value; + } + } + + public Any(char value): this(typeof(char), value) + { + } + + public Any(bool value): this(typeof(bool), value) + { + } + + public Any(byte value): this(typeof(byte), value) + { + } + + public Any(short value): this(typeof(short), value) + { + } + + public Any(ushort value): this(typeof(ushort), value) + { + } + + public Any(int value): this(typeof(int), value) + { + } + + public Any(uint value): this(typeof(uint), value) + { + } + + public Any(long value): this(typeof(long), value) + { + } + + public Any(ulong value): this(typeof(ulong), value) + { + } + + public Any(float value): this(typeof(float), value) + { + } + + public Any(double value): this(typeof(double), value) + { + } + + public Any(Type value): this(typeof(Type), value) + { + } + + public Any(string value): this(typeof(string), value) + { + } + + public override string ToString() + { + StringBuilder msg = new StringBuilder("uno.Any { Type= "); + msg.Append(Type.ToString()); + msg.Append(", Value="); + msg.Append(Value.ToString()); + msg.Append("}"); + return msg.ToString(); + } + + public bool hasValue() + { + if (Type == null || Type == typeof(void)) + return false; + return true; + } + + public override bool Equals(object obj) + { + if (obj != null) + { + try + { + return Equals((Any) obj); + } + catch (InvalidCastException) + { + } + } + return false; + } + + public bool Equals(Any obj) + { + return Type.Equals(obj.Type) + && (Value == null ? + obj.Value == null : + Value.Equals(obj.Value)); + } + + public override int GetHashCode() + { + return Type.GetHashCode() ^ (Value != null ? Value.GetHashCode() : 0); + } +} + +} + diff --git a/cli_ure/source/basetypes/uno/BoundAttribute.cs b/cli_ure/source/basetypes/uno/BoundAttribute.cs new file mode 100644 index 000000000..d89925739 --- /dev/null +++ b/cli_ure/source/basetypes/uno/BoundAttribute.cs @@ -0,0 +1,37 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +using System; + + +namespace uno +{ +/** is used to mark UNO interface attributes as "bound". + + <p>It corresponds to <const + scope="com::sun::star::beans">PropertyAttribute::BOUND</const>. + </p> + UNO interface attributes are mapped to CLI properties. + */ +[AttributeUsage(AttributeTargets.Property, Inherited=false)] +public sealed class BoundAttribute: System.Attribute +{ +} + +} + diff --git a/cli_ure/source/basetypes/uno/ExceptionAttribute.cs b/cli_ure/source/basetypes/uno/ExceptionAttribute.cs new file mode 100644 index 000000000..1c8847b44 --- /dev/null +++ b/cli_ure/source/basetypes/uno/ExceptionAttribute.cs @@ -0,0 +1,61 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +using System; + + +namespace uno +{ +/** is used to mark a UNO method to throw exceptions. + + <p>A method can be an ordinary interface method, a get or set method from + an interface attribute, or the constructor methods of service creator + classes. If there are no exceptions specified for a method then this + attribute should not be applied. Because a + <type scope="com.sun.star.uno">RuntimeException</type> + can always be thrown it is not specified. Hence no + <code>ExceptionAttribute</code> + should be applied in that case.</p> + */ +[AttributeUsage(AttributeTargets.Method, Inherited=false)] +public sealed class ExceptionAttribute: System.Attribute +{ + /** initializes an instance with the specified values. + + @param raises + array of types of Exceptions which are declared in UNOIDL. + It must not be null. + */ + public ExceptionAttribute(Type[] raises) + { + m_raises = raises; + } + + public Type[] Raises + { + get + { + return m_raises; + } + } + + private Type[] m_raises; +} + +} + diff --git a/cli_ure/source/basetypes/uno/OnewayAttribute.cs b/cli_ure/source/basetypes/uno/OnewayAttribute.cs new file mode 100644 index 000000000..ed8ad85ff --- /dev/null +++ b/cli_ure/source/basetypes/uno/OnewayAttribute.cs @@ -0,0 +1,34 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +using System; + + +namespace uno +{ +/** is used to mark UNO interface methods as being one - way according to the + UNOIDL <code>oneway<code> attribute. + <p> + */ +[AttributeUsage(AttributeTargets.Method, Inherited=false)] +public sealed class OnewayAttribute: System.Attribute +{ + +} +} + diff --git a/cli_ure/source/basetypes/uno/ParameterizedTypeAttribute.cs b/cli_ure/source/basetypes/uno/ParameterizedTypeAttribute.cs new file mode 100644 index 000000000..1ff37b280 --- /dev/null +++ b/cli_ure/source/basetypes/uno/ParameterizedTypeAttribute.cs @@ -0,0 +1,59 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +using System; + + +namespace uno +{ +/** is used to mark a UNO entity to have a parameterized type. + + <p>Currently it is only applied to members of polymorphic structs. That is structs, + which have a type parameter list. + </p> + + @see TypeParametersAttribute + */ +[AttributeUsage(AttributeTargets.Field, Inherited=false)] +public sealed class ParameterizedTypeAttribute: System.Attribute +{ + /** initializes an instance with the specified value. + + @param parameter + the name of parameter from the parameter list from + <type scope="uno.idl">TypeParametersAttribute</type> + It must not be null. + */ + public ParameterizedTypeAttribute(string parameter) + { + m_parameter = parameter; + } + + public string Type + { + get + { + return m_parameter; + } + } + + private string m_parameter; +} + +} + diff --git a/cli_ure/source/basetypes/uno/PolymorphicType.cs b/cli_ure/source/basetypes/uno/PolymorphicType.cs new file mode 100644 index 000000000..e996131fc --- /dev/null +++ b/cli_ure/source/basetypes/uno/PolymorphicType.cs @@ -0,0 +1,434 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +using System; +using System.Collections; +using System.Reflection; +using System.Globalization; + +namespace uno { + + + +/** represents a polymorphic type. + + This class is used to carry type information for polymorphic struct types + and arrays of polymorphic struct types. These types would be easiest represented + with type templates, which are not available in .NET 1.1. Therefore + the System.Type cannot contain information about template parameters. To + retain this information we use PolymorphicType which directly inherits from + System.Type. The additional information about type parameters are passed + as simple string when creating an instance of PolymorphicType. Usually one + only needs a PolymorphicType when a polymporphic type is put into an + uno.Any. For example, let's assume there is a idl type PolyStruct: + + module test { + struct PolyStruct< T > + { + T member; + }; + }; + + Then one would use it in C# in this way: + + uno.Any myAny = new uno.Any( PolymorphicType.GetType( + typeof(PolyStruct), "unoidl.test.PolyStruct<System.Boolean>"), + new PolyStruct(true)); + + or if one has a sequence of polymorphic structs: + + uno.Any myAny = new uno.Any( PolymorphicType.GetType( + typeof(PolyStruct), "unoidl.test.PolyStruct<System.Boolean>[]"), + new PolyStruct[] {new PolyStruct(true)} ); + + + To get a new instance of PolymorphicType one uses the static method + PolymorphicType.GetType. The method ensures that there is only one instance + for each distinct name. For example, if GetType is called multiple times with + the name "unoidl.test.PolyStruct<System.Boolean>" then the same instance of + PolymorphicType is returned. This makes it possible to compare the instances + by reference, thas is using the operator "==". + + The polymorphic name, which is passed as second argument to PolymorphicType.GetType, + contains a list of type names. Only type names common + to all CLI languages can be used. That is, instead of using names, such as + char, int, float, the names System.Char, System.Int32 and + System.Single are to be used. Spaces are not allowed. + The name will always start with "unoidl", when the type was generated by climaker. + Here are a couple of possible strings: + + unoidl.test.PolyStruct<System.Int32> + unoidl.test.PolyStruct<System.Char[]> + unoidl.test.PolyStruct<System.Int64>[] + unoidl.test.PolyStruct<unoidl.test.PolyStruct<System.Int64>> + unoidl.test.PolyStruct<unoidl.test.PolyStruct<System.Int64[]>[]>[] + + In the future, when the CLI supports templates, we will probably adapt the cli-uno + bridge accordingly to use real template types. Then this class will become obsolete. + */ +public class PolymorphicType: Type +{ + private Type m_base; + private string m_type_name; + + private static Hashtable m_ht_types = Hashtable.Synchronized(new Hashtable(256)); + + /** provides a unique instance of this class. + + This function returns null if the specified type is no polymorphic struct. + + @param type + the type of the polymorphic struct. For example, created by + <code>typeof(unoidl.com.sun.star.beans.Defaulted)</code> + @param name + the full name of the struct (including the type list). + @return + null - the argument type is no valid polymorphic struct or <br> + an instance of this class. + @exception System.ArgumentNullException + The argument was null. + */ + public static PolymorphicType GetType(Type type, string name) + { + if (name == null || type == null) + throw new ArgumentNullException( + "cli-uno: uno.PolymorphicType.GetType was called with a null argument"); + //check if the type is either an array of structs or a polymorphic struct. + if (type.IsArray) + { + Type elementType = type; + while ((elementType = elementType.GetElementType()).IsArray); + //unfortunately we cannot check if it is a real polymorphic struct here. + if ( ! elementType.IsClass) + return null; + + + } + else if (Attribute.GetCustomAttribute(type, typeof(uno.TypeParametersAttribute)) + == null) + { + return null; + } + + lock (m_ht_types.SyncRoot) + { + PolymorphicType t = (PolymorphicType) m_ht_types[name]; + if (t == null) + { + t = new PolymorphicType(type, name); + m_ht_types.Add(name, t); + } + return t; + } + } + + private PolymorphicType(Type type, string name) + { + m_type_name = name; + m_base = type; + } + + public string PolymorphicName + { + get + { + return m_type_name; + } + } + + public Type OriginalType + { + get + { + return m_base; + } + } + + + //implementations of abstract methods and properties from base class + public override string Name + { + get + { + return m_base.Name; + } + } + + public override Assembly Assembly + { + get + { + return m_base.Assembly; + } + } + + public override string AssemblyQualifiedName + { + get + { + return m_base.AssemblyQualifiedName; + } + } + + public override Type BaseType + { + get + { + return m_base.BaseType; + } + } + + public override string FullName + { + get + { + return m_base.FullName; + } + } + + public override Guid GUID + { + get + { + return m_base.GUID; + } + } + + public override Module Module + { + get + { + return m_base.Module; + } + } + + public override string Namespace + { + get + { + return m_base.Namespace; + } + } + + public override RuntimeTypeHandle TypeHandle + { + get + { + return m_base.TypeHandle; + } + } + + public override Type UnderlyingSystemType + { + get + { + return m_base.UnderlyingSystemType; + } + } + + public override Type DeclaringType + { + get + { + return m_base.DeclaringType; + } + } + + public override object[] GetCustomAttributes( + bool inherit) + { + return m_base.GetCustomAttributes(inherit); + } + + public override object[] GetCustomAttributes( + Type attributeType, + bool inherit) + { + return m_base.GetCustomAttributes(attributeType, inherit); + } + + public override bool IsDefined( + Type attributeType, + bool inherit) + { + return IsDefined(attributeType, inherit); + } + + protected override TypeAttributes GetAttributeFlagsImpl() + { + return m_base.Attributes; + } + + protected override ConstructorInfo GetConstructorImpl( + BindingFlags bindingAttr, + Binder binder, + CallingConventions callConvention, + Type[] types, + ParameterModifier[] modifiers) + { + return m_base.GetConstructor( + bindingAttr, binder, callConvention, types, modifiers); + } + + public override ConstructorInfo[] GetConstructors( + BindingFlags bindingAttr) + { + return m_base.GetConstructors(bindingAttr); + } + + public override Type GetElementType() + { + return m_base.GetElementType(); + } + + public override EventInfo GetEvent( + string name, + BindingFlags bindingAttr) + { + return m_base.GetEvent(name, bindingAttr); + } + + public override EventInfo[] GetEvents( + BindingFlags bindingAttr) + { + return m_base.GetEvents(bindingAttr); + } + + public override FieldInfo GetField( + string name, + BindingFlags bindingAttr) + { + return m_base.GetField(name, bindingAttr); + } + + public override FieldInfo[] GetFields( + BindingFlags bindingAttr) + { + return m_base.GetFields(bindingAttr); + } + + public override Type GetInterface( + string name, bool ignoreCase) + { + return m_base.GetInterface(name, ignoreCase); + } + + public override Type[] GetInterfaces() + { + return m_base.GetInterfaces(); + } + + public override MemberInfo[] GetMembers( + BindingFlags bindingAttr) + { + return m_base.GetMembers(bindingAttr); + } + + protected override MethodInfo GetMethodImpl( + string name, + BindingFlags bindingAttr, + Binder binder, + CallingConventions callConvention, + Type[] types, + ParameterModifier[] modifiers) + { + return m_base.GetMethod( + name, bindingAttr, binder, callConvention, types, modifiers); + } + + public override MethodInfo[] GetMethods( + BindingFlags bindingAttr) + { + return m_base.GetMethods(bindingAttr); + } + + public override Type GetNestedType( + string name, BindingFlags bindingAttr) + { + return m_base.GetNestedType(name, bindingAttr); + } + + public override Type[] GetNestedTypes( + BindingFlags bindingAttr) + { + return m_base.GetNestedTypes(bindingAttr); + } + + public override PropertyInfo[] GetProperties( + BindingFlags bindingAttr) + { + return m_base.GetProperties(bindingAttr); + } + + protected override PropertyInfo GetPropertyImpl( + string name, + BindingFlags bindingAttr, + Binder binder, + Type returnType, + Type[] types, + ParameterModifier[] modifiers) + { + return m_base.GetProperty( + name, bindingAttr, binder, returnType, types, modifiers); + } + + protected override bool HasElementTypeImpl() + { + return m_base.HasElementType; + } + + public override object InvokeMember( + string name, + BindingFlags invokeAttr, + Binder binder, + object target, + object[] args, + ParameterModifier[] modifiers, + CultureInfo culture, + string[] namedParameters) + { + return m_base.InvokeMember( + name, invokeAttr, binder, target, args, modifiers, culture, namedParameters); + } + + protected override bool IsArrayImpl() + { + return m_base.IsArray; + } + + protected override bool IsByRefImpl() + { + return m_base.IsByRef; + } + + protected override bool IsCOMObjectImpl() + { + return m_base.IsCOMObject; + } + + protected override bool IsPointerImpl() + { + return m_base.IsPointer; + } + + protected override bool IsPrimitiveImpl() + { + return m_base.IsPrimitive; + } +} +} diff --git a/cli_ure/source/basetypes/uno/TypeArgumentsAttribute.cs b/cli_ure/source/basetypes/uno/TypeArgumentsAttribute.cs new file mode 100644 index 000000000..f2e8c8e39 --- /dev/null +++ b/cli_ure/source/basetypes/uno/TypeArgumentsAttribute.cs @@ -0,0 +1,75 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +using System; + + +namespace uno +{ +/** is used to mark a parameterized UNO entity(i.e. struct) + to be an instantiation of a + template with the specified type arguments. + + <p>Currently only UNO structs can have type parameters.</p> + + <pre> + + [TypeParameters(new string[]{"T"})] + struct Foo { + [ParameterizedType("T")] + Object member; + } + + public interface XFoo { + [return:TypeArguments(new string[]{typeof(char)})] + Foo func( [TypeArguments(new string[]{typeof(char)})] Foo f); + } + </pre> + + @see TypeParametersAttribute + @see ParameterizedTypeAttribute + */ +[AttributeUsage(AttributeTargets.ReturnValue + | AttributeTargets.Parameter + | AttributeTargets.Field, Inherited=false)] +public sealed class TypeArgumentsAttribute: System.Attribute +{ + /** initializes an instance with the specified value. + + @param parameters + arrayay of names representing the types. + It must not be null. + */ + public TypeArgumentsAttribute(Type[] arguments) + { + m_arguments = arguments; + } + + public Type[] Arguments + { + get + { + return m_arguments; + } + } + + private Type[] m_arguments; +} + +} + diff --git a/cli_ure/source/basetypes/uno/TypeParametersAttribute.cs b/cli_ure/source/basetypes/uno/TypeParametersAttribute.cs new file mode 100644 index 000000000..f6adebaa0 --- /dev/null +++ b/cli_ure/source/basetypes/uno/TypeParametersAttribute.cs @@ -0,0 +1,56 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +using System; + + +namespace uno +{ +/** is used to mark a UNO entity to have type parameters. + + <p>Currently it is only applied with polymorphic structs. That is structs, + which have a type parameter list. + </p> + */ +[AttributeUsage(AttributeTargets.Class, Inherited=false)] +public sealed class TypeParametersAttribute: System.Attribute +{ + /** initializes an instance with the specified value. + + @param parameters + array of names representing the types. + It must not be null. + */ + public TypeParametersAttribute(string[] parameters) + { + m_parameters = parameters; + } + + public string[] Parameters + { + get + { + return m_parameters; + } + } + + private string[] m_parameters; +} + +} + diff --git a/cli_ure/source/climaker/climaker_app.cxx b/cli_ure/source/climaker/climaker_app.cxx new file mode 100644 index 000000000..7b9bc001a --- /dev/null +++ b/cli_ure/source/climaker/climaker_app.cxx @@ -0,0 +1,675 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "sal/config.h" + +#include <cstdlib> +#include <iostream> +#include <stdio.h> +#include <vector> + +#include "climaker_share.h" + +#include "sal/main.h" +#include "osl/process.h" +#include "osl/file.hxx" +#include "osl/thread.h" +#include "rtl/ustrbuf.hxx" +#include "cppuhelper/bootstrap.hxx" +#include "com/sun/star/lang/XComponent.hpp" +#include "com/sun/star/container/XHierarchicalNameAccess.hpp" +#include "com/sun/star/container/XSet.hpp" +#include "com/sun/star/reflection/XTypeDescriptionEnumerationAccess.hpp" +#include "com/sun/star/uno/XComponentContext.hpp" +#include "unoidl/unoidl.hxx" + +using namespace ::std; +using namespace ::System::Reflection; + + +using namespace ::osl; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace climaker +{ + + +static char const s_usingText [] = +"\n" +"using: climaker <switches> [registry-file-1 registry-file-2 ...]\n" +"\n" +"switches:\n" +" -O, --out <output-file> output assembly file;\n" +" defaults to cli_unotypes.dll if more than one\n" +" registry-file is given, else <registry-file>.dll\n" +" -T, --types types to be generated (if none is given,\n" +" <type1[;type2;...]> then all types of given registries are emitted\n" +" -X, --extra <rdb-file> additional rdb to saturate referenced types in\n" +" given registry file(s); these types will not be\n" +" emitted into the output assembly file\n" +" -r, --reference reference metadata from assembly file\n" +" <assembly-file>\n" +" -k, --keyfile keyfile needed for strong name\n" +" --assembly-version <version> sets assembly version\n" +" --assembly-description <text> sets assembly description text\n" +" --assembly-product <text> sets assembly product name\n" +" --assembly-company <text> sets assembly company\n" +" --assembly-copyright <text> sets assembly copyright\n" +" --assembly-trademark <text> sets assembly trademark\n" +" -v, --verbose verbose output to stdout\n" +" -h, --help this message\n" +"\n" +"example: climaker --out cli_mytypes.dll \\\n" +" --reference cli_uretypes.dll \\\n" +" --extra types.rdb \\\n" +" mytypes.rdb\n" +"\n"; + +struct OptionInfo +{ + char const * m_name; + sal_uInt32 m_name_length; + sal_Unicode m_short_option; + bool m_has_argument; +}; + +bool g_bVerbose = false; + + +static const OptionInfo s_option_infos [] = { + { RTL_CONSTASCII_STRINGPARAM("out"), 'O', true }, + { RTL_CONSTASCII_STRINGPARAM("types"), 'T', true }, + { RTL_CONSTASCII_STRINGPARAM("extra"), 'X', true }, + { RTL_CONSTASCII_STRINGPARAM("reference"), 'r', true }, + { RTL_CONSTASCII_STRINGPARAM("keyfile"), 'k', true }, + { RTL_CONSTASCII_STRINGPARAM("delaySign"), 'd', true }, + { RTL_CONSTASCII_STRINGPARAM("assembly-version"), '\0', true }, + { RTL_CONSTASCII_STRINGPARAM("assembly-description"), '\0', true }, + { RTL_CONSTASCII_STRINGPARAM("assembly-product"), '\0', true }, + { RTL_CONSTASCII_STRINGPARAM("assembly-company"), '\0', true }, + { RTL_CONSTASCII_STRINGPARAM("assembly-copyright"), '\0', true }, + { RTL_CONSTASCII_STRINGPARAM("assembly-trademark"), '\0', true }, + { RTL_CONSTASCII_STRINGPARAM("verbose"), 'v', false }, + { RTL_CONSTASCII_STRINGPARAM("help"), 'h', false } +}; + + +static OptionInfo const * get_option_info( + OUString const & opt, sal_Unicode copt = '\0' ) +{ + for ( sal_Int32 pos = 0; + pos < (sizeof (s_option_infos) / sizeof (OptionInfo)); + ++pos ) + { + OptionInfo const & option_info = s_option_infos[ pos ]; + + if (opt.getLength() > 0) + { + if (opt.equalsAsciiL( + option_info.m_name, option_info.m_name_length ) && + (copt == '\0' || copt == option_info.m_short_option)) + { + return &option_info; + } + } + else + { + OSL_ASSERT( copt != '\0' ); + if (copt == option_info.m_short_option) + { + return &option_info; + } + } + } + OSL_FAIL( + OUStringToOString( opt, osl_getThreadTextEncoding() ).getStr() ); + return 0; +} + + +static bool is_option( + OptionInfo const * option_info, sal_uInt32 * pIndex ) +{ + OSL_ASSERT( option_info != 0 ); + if (osl_getCommandArgCount() <= *pIndex) + return false; + + OUString arg; + osl_getCommandArg( *pIndex, &arg.pData ); + sal_Int32 len = arg.getLength(); + + if (len < 2 || arg[ 0 ] != '-') + return false; + + if (len == 2 && arg[ 1 ] == option_info->m_short_option) + { + ++(*pIndex); + return true; + } + if (arg[ 1 ] == '-' && rtl_ustr_ascii_compare( + arg.pData->buffer + 2, option_info->m_name ) == 0) + { + ++(*pIndex); + return true; + } + return false; +} + + +static inline bool read_option( + bool * flag, OptionInfo const * option_info, sal_uInt32 * pIndex ) +{ + bool ret = is_option( option_info, pIndex ); + if (ret) + *flag = true; + return ret; +} + + +static bool read_argument( + OUString * pValue, OptionInfo const * option_info, sal_uInt32 * pIndex ) +{ + if (is_option( option_info, pIndex )) + { + if (*pIndex < osl_getCommandArgCount()) + { + osl_getCommandArg( *pIndex, &pValue->pData ); + ++(*pIndex); + return true; + } + --(*pIndex); + } + return false; +} + + +static OUString const & path_get_working_dir() +{ + static OUString s_workingDir; + if (! s_workingDir.getLength()) + osl_getProcessWorkingDir( &s_workingDir.pData ); + return s_workingDir; +} + + +static OUString path_make_absolute_file_url( OUString const & path ) +{ + OUString file_url; + oslFileError rc = osl_getFileURLFromSystemPath( + path.pData, &file_url.pData ); + if (osl_File_E_None == rc) + { + OUString abs; + rc = osl_getAbsoluteFileURL( + path_get_working_dir().pData, file_url.pData, &abs.pData ); + if (osl_File_E_None == rc) + { + return abs; + } + else + { + throw RuntimeException( + "cannot make absolute: " + file_url ); + } + } + else + { + throw RuntimeException( + "cannot get file url from system path: " + path ); + } +} + +} + +using namespace ::climaker; + + +SAL_IMPLEMENT_MAIN() +{ + sal_uInt32 nCount = osl_getCommandArgCount(); + if (0 == nCount) + { + puts( s_usingText ); + return 0; + } + + int ret = 0; + css::uno::Reference< XComponentContext > xContext; + + try + { + OptionInfo const * info_help = + get_option_info( "help" ); + OptionInfo const * info_verbose = + get_option_info( "verbose" ); + OptionInfo const * info_out = + get_option_info( "out" ); + OptionInfo const * info_types = + get_option_info( "types" ); + OptionInfo const * info_reference = + get_option_info( "reference" ); + OptionInfo const * info_extra = + get_option_info( "extra" ); + OptionInfo const * info_keyfile = + get_option_info( "keyfile" ); + OptionInfo const * info_delaySign = + get_option_info( "delaySign" ); + OptionInfo const * info_version = + get_option_info( "assembly-version" ); + OptionInfo const * info_product = + get_option_info( "assembly-product" ); + OptionInfo const * info_description = + get_option_info( "assembly-description" ); + OptionInfo const * info_company = + get_option_info( "assembly-company" ); + OptionInfo const * info_copyright = + get_option_info( "assembly-copyright" ); + OptionInfo const * info_trademark = + get_option_info( "assembly-trademark" ); + + OUString output; + vector< OUString > mandatory_registries; + vector< OUString > extra_registries; + vector< OUString > extra_assemblies; + vector< OUString > explicit_types; + OUString version, product, description, company, copyright, trademark, + keyfile, delaySign; + + OUString cmd_arg; + for ( sal_uInt32 nPos = 0; nPos < nCount; ) + { + // options + if (is_option( info_help, &nPos )) + { + puts( s_usingText ); + return 0; + } + else if (read_argument( &cmd_arg, info_types, &nPos )) + { + sal_Int32 index = 0; + do + { + explicit_types.push_back( + cmd_arg.getToken( 0, ';', index ) ); + } + while (index >= 0); + } + else if (read_argument( &cmd_arg, info_extra, &nPos )) + { + extra_registries.push_back( + path_make_absolute_file_url( cmd_arg ) ); + } + else if (read_argument( &cmd_arg, info_reference, &nPos )) + { + extra_assemblies.push_back( + path_make_absolute_file_url( cmd_arg ) ); + } + else if (!read_option( &g_bVerbose, info_verbose, &nPos ) && + !read_argument( &output, info_out, &nPos ) && + !read_argument( &version, info_version, &nPos ) && + !read_argument( &description, info_description, &nPos ) && + !read_argument( &product, info_product, &nPos ) && + !read_argument( &company, info_company, &nPos ) && + !read_argument( ©right, info_copyright, &nPos ) && + !read_argument( &trademark, info_trademark, &nPos ) && + !read_argument( &keyfile, info_keyfile, &nPos ) && + !read_argument( &delaySign, info_delaySign, &nPos )) + { + osl_getCommandArg( nPos, &cmd_arg.pData ); + ++nPos; + cmd_arg = cmd_arg.trim(); + if (cmd_arg.getLength() > 0) + { + if (cmd_arg[ 0 ] == '-') // is option + { + OptionInfo const * option_info = 0; + if (cmd_arg.getLength() > 2 && + cmd_arg[ 1 ] == '-') + { + // long option + option_info = get_option_info( + cmd_arg.copy( 2 ), '\0' ); + } + else if (cmd_arg.getLength() == 2 && + cmd_arg[ 1 ] != '-') + { + // short option + option_info = get_option_info( + OUString(), cmd_arg[ 1 ] ); + } + if (option_info == 0) + { + throw RuntimeException("unknown option " + cmd_arg + "! Use climaker --help to print all options."); + } + else + { + OSL_FAIL( "unhandled valid option?!" ); + if (option_info->m_has_argument) + ++nPos; + } + } + else + { + mandatory_registries.push_back( + path_make_absolute_file_url( cmd_arg ) ); + } + } + } + } + + // bootstrap uno + xContext = ::cppu::defaultBootstrap_InitialComponentContext(); + css::uno::Reference< container::XHierarchicalNameAccess > xTDmgr( + xContext->getValueByName( + "/singletons/com.sun.star.reflection." + "theTypeDescriptionManager" ), + UNO_QUERY_THROW ); + + // The registries are consumed twice, once to insert them into the + // TypeDescriptionManager so that TypeEmitter can work on + // css.star.reflection.XTypeDescription representation, and once + // directly as unoidl::Provider instances to keep track which types are + // coming from the mandatory registries for the "no explicit types + // given" case (which iterates over the full TypeDescriptionManager + // now); a welcome clean-up would be to make TypeEmitter work on + // unoidl::Entity directly like the other codemakers: + css::uno::Reference< container::XSet > xSet( xTDmgr, UNO_QUERY_THROW ); + rtl::Reference unoidlMgr(new unoidl::Manager); + std::vector< rtl::Reference< unoidl::Provider > > unoidlMandatoryProvs; + for (auto& rRegistry : extra_registries) + { + xSet->insert(Any(rRegistry)); + unoidlMgr->addProvider(rRegistry); + } + for (auto& rRegistry : mandatory_registries) + { + xSet->insert(Any(rRegistry)); + rtl::Reference< unoidl::Provider > prov(unoidlMgr->addProvider(rRegistry)); + unoidlMandatoryProvs.push_back(prov); + } + + if (0 == output.getLength()) // no output file specified + { + // if only one rdb has been given, then take rdb name + if (1 == mandatory_registries.size()) + { + output = mandatory_registries[ 0 ]; + output = output.copy( output.lastIndexOf( '/' ) +1 ); + sal_Int32 dot = output.lastIndexOf( '.' ); + if (dot > 0) + output = output.copy( 0, dot ); + } + else + { + output = "cli_unotypes"; + } + } + output = path_make_absolute_file_url( output ); + sal_Int32 slash = output.lastIndexOf( '/' ); + OUString sys_output_dir; + if (FileBase::E_None != FileBase::getSystemPathFromFileURL( + output.copy( 0, slash ), sys_output_dir )) + { + throw RuntimeException( + "cannot get system path from file url " + + output.copy( 0, slash ) ); + } + OUString filename( output.copy( slash +1 ) ); + sal_Int32 dot = filename.lastIndexOf( '.' ); + OUString name( filename ); + if (dot < 0) // has no extension + filename += ".dll"; + else + name = name.copy( 0, dot ); + ::System::String ^ output_dir = ustring_to_String( sys_output_dir ); + ::System::String ^ output_file = ustring_to_String( filename ); + + //Get the key pair for making a strong name + StrongNameKeyPair^ kp = nullptr; + if (keyfile.getLength() > 0) + { + ::System::String ^ sKeyFile = ustring_to_String(keyfile); + try { + System::IO::FileStream^ fs = gcnew System::IO::FileStream( + sKeyFile, System::IO::FileMode::Open, + System::IO::FileAccess::Read, System::IO::FileShare::Read); + kp = gcnew StrongNameKeyPair(fs); + fs->Close(); + } + catch (System::IO::FileNotFoundException ^ ) + { + throw Exception("Could not find the keyfile. Verify the --keyfile argument!", 0); + } + } + else + { + if (g_bVerbose) + { + ::System::Console::Write( + "> no key file specified. Cannot create strong name!\n"); + } + } + // setup assembly info: xxx todo set more? e.g. avoid strong versioning + AssemblyName ^ assembly_name = gcnew AssemblyName(); + assembly_name->CodeBase = output_dir; + assembly_name->Name = gcnew ::System::String( + reinterpret_cast<wchar_t const *>(name.getStr())); + if (kp != nullptr) + assembly_name->KeyPair= kp; + + if (version.getLength() != 0) + { + assembly_name->Version= + gcnew ::System::Version( ustring_to_String( version ) ); + } + + // app domain + ::System::AppDomain ^ current_appdomain = + ::System::AppDomain::CurrentDomain; + +// Weird warning from this statement +// warning C4538: 'cli::array<Type> ^' : const/volatile qualifiers on this type are not supported +// Could be a compiler bug, says http://stackoverflow.com/questions/12151060/seemingly-inappropriate-compilation-warning-with-c-cli +#pragma warning (push) +#pragma warning (disable: 4538) + // target assembly + Emit::AssemblyBuilder ^ assembly_builder = + current_appdomain->DefineDynamicAssembly( + assembly_name, Emit::AssemblyBuilderAccess::Save, output_dir ); +#pragma warning (pop) + + if (product.getLength() != 0) + { + cli::array< ::System::Type^>^ params = gcnew cli::array< ::System::Type^> (1); + cli::array< ::System::Object^>^args = gcnew cli::array< ::System::Object^>(1); + params[ 0 ] = ::System::String::typeid; + args[ 0 ] = ustring_to_String( product ); + assembly_builder->SetCustomAttribute( + gcnew Emit::CustomAttributeBuilder( + (AssemblyProductAttribute::typeid)->GetConstructor( + params ), args ) ); + } + if (description.getLength() != 0) + { + cli::array< ::System::Type^>^ params = gcnew cli::array< ::System::Type^>(1); + cli::array< ::System::Object^>^ args = gcnew cli::array< ::System::Object^>(1); + params[ 0 ] = ::System::String::typeid; + args[ 0 ] = ustring_to_String( description ); + assembly_builder->SetCustomAttribute( + gcnew Emit::CustomAttributeBuilder( + (AssemblyDescriptionAttribute::typeid)->GetConstructor( + params ), args ) ); + } + if (company.getLength() != 0) + { + cli::array< ::System::Type^>^ params = gcnew cli::array< ::System::Type^>(1); + cli::array< ::System::Object^>^ args = gcnew cli::array< ::System::Object^>(1); + params[ 0 ] = ::System::String::typeid; + args[ 0 ] = ustring_to_String( company ); + assembly_builder->SetCustomAttribute( + gcnew Emit::CustomAttributeBuilder( + (AssemblyCompanyAttribute::typeid)->GetConstructor( + params ), args ) ); + } + if (copyright.getLength() != 0) + { + cli::array< ::System::Type^>^ params = gcnew cli::array< ::System::Type^>(1); + cli::array< ::System::Object^>^ args = gcnew cli::array< ::System::Object^>(1); + params[ 0 ] = ::System::String::typeid; + args[ 0 ] = ustring_to_String( copyright ); + assembly_builder->SetCustomAttribute( + gcnew Emit::CustomAttributeBuilder( + (AssemblyCopyrightAttribute::typeid)->GetConstructor( + params ), args ) ); + } + if (trademark.getLength() != 0) + { + cli::array< ::System::Type^>^ params = gcnew cli::array< ::System::Type^>(1); + cli::array< ::System::Object^>^ args = gcnew cli::array< ::System::Object^>(1); + params[ 0 ] = ::System::String::typeid; + args[ 0 ] = ustring_to_String( trademark ); + assembly_builder->SetCustomAttribute( + gcnew Emit::CustomAttributeBuilder( + (AssemblyTrademarkAttribute::typeid)->GetConstructor( + params ), args ) ); + } + + // load extra assemblies + cli::array<Assembly^>^ assemblies = + gcnew cli::array<Assembly^>(extra_assemblies.size()); + for ( size_t pos = 0; pos < extra_assemblies.size(); ++pos ) + { + assemblies[ pos ] = Assembly::LoadFrom( + ustring_to_String( extra_assemblies[ pos ] ) ); + } + + // type emitter + TypeEmitter ^ type_emitter = gcnew TypeEmitter( + assembly_builder->DefineDynamicModule( output_file ), assemblies ); + // add handler resolving assembly's types + ::System::ResolveEventHandler ^ type_resolver = + gcnew ::System::ResolveEventHandler( + type_emitter, &TypeEmitter::type_resolve ); + current_appdomain->TypeResolve += type_resolver; + + // and emit types to it + if (explicit_types.empty()) + { + css::uno::Reference< reflection::XTypeDescriptionEnumeration > xTD_enum( + css::uno::Reference< reflection::XTypeDescriptionEnumerationAccess >( + xTDmgr, UNO_QUERY_THROW ) + ->createTypeDescriptionEnumeration( + OUString() /* all IDL modules */, + Sequence< TypeClass >() /* all classes of types */, + reflection::TypeDescriptionSearchDepth_INFINITE ) ); + while (xTD_enum->hasMoreElements()) + { + css::uno::Reference< reflection::XTypeDescription > td( + xTD_enum->nextTypeDescription()); + OUString name(td->getName()); + bool bEmit = std::any_of(unoidlMandatoryProvs.begin(), unoidlMandatoryProvs.end(), + [&name](rtl::Reference<unoidl::Provider>& rProv) { return rProv->findEntity(name).is(); }); + if (bEmit) { + type_emitter->get_type(td); + } + } + } + else + { + for ( size_t nPos = explicit_types.size(); nPos--; ) + { + type_emitter->get_type( + css::uno::Reference< reflection::XTypeDescription >( + xTDmgr->getByHierarchicalName( explicit_types[ nPos ] ), + UNO_QUERY_THROW ) ); + } + } + type_emitter->~TypeEmitter(); + + if (g_bVerbose) + { + ::System::Console::Write( + "> saving assembly {0}{1}{2}...", + output_dir, + gcnew ::System::String( + ::System::IO::Path::DirectorySeparatorChar, 1 ), + output_file ); + } + assembly_builder->Save( output_file ); + if (g_bVerbose) + { + ::System::Console::WriteLine( "ok." ); + } + current_appdomain->TypeResolve -= type_resolver; + } + catch (unoidl::NoSuchFileException & e) + { + std::cerr << "ERROR: No such file <" << e.getUri() << ">\n"; + return EXIT_FAILURE; + } + catch (unoidl::FileFormatException & e) + { + std::cerr + << "ERROR: Bad format of <" << e.getUri() << ">, \"" + << e.getDetail() << "\"\n"; + return EXIT_FAILURE; + } + catch (Exception & exc) + { + OString msg( + OUStringToOString( exc.Message, osl_getThreadTextEncoding() ) ); + fprintf( + stderr, "\n> error: %s\n> dying abnormally...\n", msg.getStr() ); + ret = 1; + } + catch (::System::Exception ^ exc) + { + OString msg( OUStringToOString( + String_to_ustring( exc->ToString() ), + osl_getThreadTextEncoding() ) ); + fprintf( + stderr, + "\n> error: .NET exception occurred: %s\n> dying abnormally...", + msg.getStr() ); + ret = 1; + } + + try + { + css::uno::Reference< lang::XComponent > xComp( xContext, UNO_QUERY ); + if (xComp.is()) + xComp->dispose(); + } + catch (Exception & exc) + { + OString msg( + OUStringToOString( exc.Message, osl_getThreadTextEncoding() ) ); + fprintf( + stderr, + "\n> error disposing component context: %s\n" + "> dying abnormally...\n", + msg.getStr() ); + ret = 1; + } + + return ret; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cli_ure/source/climaker/climaker_emit.cxx b/cli_ure/source/climaker/climaker_emit.cxx new file mode 100644 index 000000000..e34a9b088 --- /dev/null +++ b/cli_ure/source/climaker/climaker_emit.cxx @@ -0,0 +1,2279 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "climaker_share.h" + +#include "rtl/string.hxx" +#include "rtl/ustrbuf.hxx" +#include "com/sun/star/reflection/XIndirectTypeDescription.hpp" +#include "com/sun/star/reflection/XStructTypeDescription.hpp" +#include "com/sun/star/reflection/XInterfaceTypeDescription2.hpp" +#include "com/sun/star/reflection/XInterfaceMethodTypeDescription.hpp" +#include "com/sun/star/reflection/XInterfaceAttributeTypeDescription.hpp" +#include "com/sun/star/reflection/XInterfaceAttributeTypeDescription2.hpp" +#include <vector> + +using namespace ::System::Reflection; + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace climaker +{ +System::String^ mapUnoPolymorphicName(System::String^ unoName); + +static inline ::System::String ^ to_cts_name( + OUString const & uno_name ) +{ + return ustring_to_String("unoidl." + uno_name); +} + + +static inline ::System::Object ^ to_cli_constant( Any const & value ) +{ + switch (value.getValueTypeClass()) + { + case TypeClass_CHAR: + return ((::System::Char) *reinterpret_cast< sal_Unicode const * >( + value.getValue() )); + case TypeClass_BOOLEAN: + return ((::System::Boolean) + sal_False != *reinterpret_cast< sal_Bool const * >( + value.getValue() )); + case TypeClass_BYTE: + return ((::System::Byte) *reinterpret_cast< sal_Int8 const * >( + value.getValue() )); + case TypeClass_SHORT: + return ((::System::Int16) *reinterpret_cast< sal_Int16 const * >( + value.getValue() )); + case TypeClass_UNSIGNED_SHORT: + return ((::System::UInt16) *reinterpret_cast< sal_uInt16 const * >( + value.getValue() )); + case TypeClass_LONG: + return ((::System::Int32) *reinterpret_cast< sal_Int32 const * >( + value.getValue() )); + case TypeClass_UNSIGNED_LONG: + return ((::System::UInt32) *reinterpret_cast< sal_uInt32 const * >( + value.getValue() )); + case TypeClass_HYPER: + return ((::System::Int64) *reinterpret_cast< sal_Int64 const * >( + value.getValue() )); + case TypeClass_UNSIGNED_HYPER: + return ((::System::UInt64) *reinterpret_cast< sal_uInt64 const * >( + value.getValue() )); + case TypeClass_FLOAT: + return ((::System::Single) *reinterpret_cast< float const * >( + value.getValue() )); + case TypeClass_DOUBLE: + return ((::System::Double) *reinterpret_cast< double const * >( + value.getValue() )); + default: + throw RuntimeException( + "unexpected constant type " + + value.getValueType().getTypeName() ); + } +} + + +static inline void emit_ldarg( Emit::ILGenerator ^ code, ::System::Int32 index ) +{ + switch (index) + { + case 0: +#pragma warning (push) +#pragma warning (disable: 4538) // const/volatile qualifiers on this type are not supported + code->Emit( Emit::OpCodes::Ldarg_0 ); +#pragma warning (pop) + break; + case 1: + code->Emit( Emit::OpCodes::Ldarg_1 ); + break; + case 2: + code->Emit( Emit::OpCodes::Ldarg_2 ); + break; + case 3: + code->Emit( Emit::OpCodes::Ldarg_3 ); + break; + default: + if (index < 0x100) + code->Emit( Emit::OpCodes::Ldarg_S, (::System::Byte) index ); + else if (index < 0x8000) + code->Emit( Emit::OpCodes::Ldarg_S, (::System::Int16) index ); + else + code->Emit( Emit::OpCodes::Ldarg, index ); + break; + } +} + +void polymorphicStructNameToStructName(::System::String ^* sPolyName) +{ + if ((*sPolyName)->EndsWith(">") == false) + return; + + int index = (*sPolyName)->IndexOf('<'); + OSL_ASSERT(index != -1); + *sPolyName = (*sPolyName)->Substring(0, index); +} + + +System::String^ mapUnoTypeName(System::String ^ typeName) +{ + ::System::Text::StringBuilder^ buf= gcnew System::Text::StringBuilder(); + ::System::String ^ sUnoName = ::System::String::Copy(typeName); + //determine if the type is a sequence and its dimensions + int dims= 0; + if (typeName->StartsWith("["))//if (usUnoName[0] == '[') + { + int index= 1; + while (true) + { + if (typeName[index++] == ']')//if (usUnoName[index++] == ']') + dims++; + if (typeName[index++] != '[')//usUnoName[index++] != '[') + break; + } + sUnoName = sUnoName->Substring(index - 1);//usUnoName = usUnoName.copy(index - 1); + } + if (sUnoName->Equals(const_cast<System::String^>(Constants::sUnoBool))) + buf->Append(const_cast<System::String^>(Constants::sBoolean)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::sUnoChar))) + buf->Append(const_cast<System::String^>(Constants::sChar)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::sUnoByte))) + buf->Append(const_cast<System::String^>(Constants::sByte)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::sUnoShort))) + buf->Append(const_cast<System::String^>(Constants::sInt16)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::sUnoUShort))) + buf->Append(const_cast<System::String^>(Constants::sUInt16)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::sUnoLong))) + buf->Append(const_cast<System::String^>(Constants::sInt32)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::sUnoULong))) + buf->Append(const_cast<System::String^>(Constants::sUInt32)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::sUnoHyper))) + buf->Append(const_cast<System::String^>(Constants::sInt64)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::sUnoUHyper))) + buf->Append(const_cast<System::String^>(Constants::sUInt64)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::sUnoFloat))) + buf->Append(const_cast<System::String^>(Constants::sSingle)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::sUnoDouble))) + buf->Append(const_cast<System::String^>(Constants::sDouble)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::sUnoString))) + buf->Append(const_cast<System::String^>(Constants::sString)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::sUnoVoid))) + buf->Append(const_cast<System::String^>(Constants::sVoid)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::sUnoType))) + buf->Append(const_cast<System::String^>(Constants::sType)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::sUnoXInterface))) + buf->Append(const_cast<System::String^>(Constants::sObject)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::sUnoAny))) + { + buf->Append(const_cast<System::String^>(Constants::sAny)); + } + else + { + //put "unoidl." at the beginning + buf->Append(const_cast<System::String^>(Constants::sUnoidl)); + buf->Append(mapUnoPolymorphicName(sUnoName)); + } + // append [] + for (;dims--;) + buf->Append(const_cast<System::String^>(Constants::sBrackets)); + + return buf->ToString(); +} + + +/** For example, there is a uno type + com.sun.star.Foo<char, long>. + The values in the type list + are uno types and are replaced by cli types, such as System.Char, + System.Int32, etc. + + Strings can be as complicated as this + test.MyStruct<char,test.MyStruct<long, []string>> + */ +System::String^ mapUnoPolymorphicName(System::String^ unoName) +{ + int index = unoName->IndexOf('<'); + if (index == -1) + return unoName; + + System::Text::StringBuilder ^ builder = + gcnew System::Text::StringBuilder(unoName->Substring(0, index +1 )); + + //Find the first occurrence of ',' + //If the parameter is a polymorphic struct then we need to ignore everything + //between the brackets because it can also contain commas + //get the type list within < and > + int endIndex = unoName->Length - 1; + index++; + int cur = index; + int countParams = 0; + while (cur <= endIndex) + { + System::Char c = unoName[cur]; + if (c == ',' || c == '>') + { + //insert a comma if needed + if (countParams != 0) + builder->Append(","); + countParams++; + System::String ^ sParam = unoName->Substring(index, cur - index); + //skip the comma + cur++; + //the index to the beginning of the next param + index = cur; + builder->Append(mapUnoTypeName(sParam)); + } + else if (c == '<') + { + cur++; + //continue until the matching '>' + int numNested = 0; + for (;;cur++) + { + System::Char curChar = unoName[cur]; + if (curChar == '<') + { + numNested ++; + } + else if (curChar == '>') + { + if (numNested > 0) + numNested--; + else + break; + } + } + } + cur++; + } + + builder->Append((System::Char) '>'); + return builder->ToString(); +} + + +Assembly ^ TypeEmitter::type_resolve( + ::System::Object ^, ::System::ResolveEventArgs ^ args ) +{ + ::System::String ^ cts_name = args->Name; + ::System::Type ^ ret_type = nullptr; + + iface_entry ^ entry = dynamic_cast< iface_entry ^ >(m_incomplete_ifaces[cts_name] ); + if (nullptr != entry) + ret_type = entry->m_type_builder; + + if (nullptr == ret_type) + { + sal_Int32 len = m_extra_assemblies->Length; + for ( sal_Int32 pos = 0; pos < len; ++pos ) + { + ret_type = m_extra_assemblies[ pos ]->GetType( + cts_name, false /* no exc */ ); + if (nullptr != ret_type) + { + if (g_bVerbose) + { + ::System::Console::WriteLine( + "> resolving type {0} from {1}.", + cts_name, ret_type->Assembly->FullName ); + } + break; + } + } + } + if (nullptr != ret_type) + return ret_type->Assembly; + return nullptr; +} + + +::System::Type ^ TypeEmitter::get_type( + ::System::String ^ cts_name, bool throw_exc ) +{ + ::System::Type ^ ret_type = m_module_builder->GetType( cts_name, false ); + //We get the type from the ModuleBuilder even if the type is not complete + //but have been defined. + //if (ret_type == 0) + //{ + // iface_entry * entry = dynamic_cast< iface_entry * >( + // m_incomplete_ifaces->get_Item( cts_name ) ); + // if (0 != entry) + // ret_type = entry->m_type_builder; + //} + //try the cli_basetypes assembly + if (ret_type == nullptr) + { + ::System::Text::StringBuilder ^ builder = gcnew ::System::Text::StringBuilder(cts_name); + builder->Append(",cli_basetypes"); + ret_type = ::System::Type::GetType(builder->ToString()); + } + + if (ret_type == nullptr) + { + try + { + // may call on type_resolve() + return ::System::Type::GetType( cts_name, throw_exc ); + } + catch (::System::Exception^ exc) + { + //If the type is not found one may have forgotten to specify assemblies with + //additional types + ::System::Text::StringBuilder ^ sb = gcnew ::System::Text::StringBuilder(); + sb->Append(gcnew ::System::String("\nThe type ")); + sb->Append(cts_name); + sb->Append(gcnew ::System::String(" \n could not be found. Did you forget to " + "specify an additional assembly with the --reference option?\n")); + if (throw_exc) + throw gcnew ::System::Exception(sb->ToString(), exc); + } + } + else + { + return ret_type; + } +} + + +::System::Type ^ TypeEmitter::get_type_Exception() +{ + if (nullptr == m_type_Exception) + { + m_type_Exception = get_type( + "unoidl.com.sun.star.uno.Exception", false /* no exc */ ); + if (nullptr == m_type_Exception) + { + // define hardcoded type unoidl.com.sun.star.uno.Exception + Emit::TypeBuilder ^ type_builder = + m_module_builder->DefineType( + "unoidl.com.sun.star.uno.Exception", + (TypeAttributes) (TypeAttributes::Public | + TypeAttributes::BeforeFieldInit | + TypeAttributes::AnsiClass), + (::System::Exception::typeid) ); + Emit::FieldBuilder ^ field_Context = type_builder->DefineField( + "Context", (::System::Object::typeid), + FieldAttributes::Public ); + // default .ctor + type_builder->DefineDefaultConstructor( c_ctor_method_attr ); + // .ctor + array< ::System::Type^>^ param_types = + gcnew array< ::System::Type^>(2); + param_types[ 0 ] = ::System::String::typeid; + param_types[ 1 ] = ::System::Object::typeid; + Emit::ConstructorBuilder ^ ctor_builder = + type_builder->DefineConstructor( + c_ctor_method_attr, CallingConventions::Standard, + param_types ); + ctor_builder->DefineParameter( + 1, ParameterAttributes::In, "Message" ); + ctor_builder->DefineParameter( + 2, ParameterAttributes::In, "Context" ); + Emit::ILGenerator ^ code = ctor_builder->GetILGenerator(); + code->Emit( Emit::OpCodes::Ldarg_0 ); + code->Emit( Emit::OpCodes::Ldarg_1 ); + param_types = gcnew array< ::System::Type^>(1); + param_types[ 0 ] = ::System::String::typeid; + code->Emit( + Emit::OpCodes::Call, + (::System::Exception::typeid) + ->GetConstructor( param_types ) ); + code->Emit( Emit::OpCodes::Ldarg_0 ); + code->Emit( Emit::OpCodes::Ldarg_2 ); + code->Emit( Emit::OpCodes::Stfld, field_Context ); + code->Emit( Emit::OpCodes::Ret ); + + if (g_bVerbose) + { + ::System::Console::WriteLine( + "> emitting exception type " + "unoidl.com.sun.star.uno.Exception" ); + } + m_type_Exception = type_builder->CreateType(); + } + } + return m_type_Exception; +} + + +::System::Type ^ TypeEmitter::get_type_RuntimeException() +{ + if (nullptr == m_type_RuntimeException) + { + m_type_RuntimeException = get_type( + "unoidl.com.sun.star.uno.RuntimeException", false /* no exc */ ); + if (nullptr == m_type_RuntimeException) + { + // define hardcoded type unoidl.com.sun.star.uno.RuntimeException + ::System::Type ^ type_Exception = get_type_Exception(); + Emit::TypeBuilder ^ type_builder = + m_module_builder->DefineType( + "unoidl.com.sun.star.uno.RuntimeException", + (TypeAttributes) (TypeAttributes::Public | + TypeAttributes::BeforeFieldInit | + TypeAttributes::AnsiClass), + type_Exception ); + // default .ctor + type_builder->DefineDefaultConstructor( c_ctor_method_attr ); + // .ctor + array< ::System::Type^>^ param_types = + gcnew array< ::System::Type^>(2); + param_types[ 0 ] = ::System::String::typeid; + param_types[ 1 ] = ::System::Object::typeid; + Emit::ConstructorBuilder ^ ctor_builder = + type_builder->DefineConstructor( + c_ctor_method_attr, CallingConventions::Standard, + param_types ); + ctor_builder->DefineParameter( + 1, ParameterAttributes::In, "Message" ); + ctor_builder->DefineParameter( + 2, ParameterAttributes::In, "Context" ); + Emit::ILGenerator ^ code = ctor_builder->GetILGenerator(); + code->Emit( Emit::OpCodes::Ldarg_0 ); + code->Emit( Emit::OpCodes::Ldarg_1 ); + code->Emit( Emit::OpCodes::Ldarg_2 ); + code->Emit( + Emit::OpCodes::Call, + type_Exception->GetConstructor( param_types ) ); + code->Emit( Emit::OpCodes::Ret ); + + if (g_bVerbose) + { + ::System::Console::WriteLine( + "> emitting exception type " + "unoidl.com.sun.star.uno.RuntimeException" ); + } + m_type_RuntimeException = type_builder->CreateType(); + } + } + return m_type_RuntimeException; +} + + +::System::Type ^ TypeEmitter::get_type( + Reference< reflection::XConstantTypeDescription > const & xType ) +{ + ::System::String ^ cts_name = to_cts_name( xType->getName() ); + ::System::Type ^ ret_type = get_type( cts_name, false /* no exc */ ); + if (nullptr == ret_type) + { + Reference< reflection::XConstantTypeDescription > xConstant( + xType, UNO_SET_THROW ); + ::System::Object ^ constant = + to_cli_constant( xConstant->getConstantValue() ); + Emit::TypeBuilder ^ type_builder = + m_module_builder->DefineType( + cts_name, + (TypeAttributes) (TypeAttributes::Public | + TypeAttributes::Sealed | + TypeAttributes::BeforeFieldInit | + TypeAttributes::AnsiClass) ); + + Emit::FieldBuilder ^ field_builder = type_builder->DefineField( + cts_name->Substring( cts_name->LastIndexOf( '.' ) +1 ), + constant->GetType(), + (FieldAttributes) (FieldAttributes::Public | + FieldAttributes::Static | + FieldAttributes::Literal) ); + field_builder->SetConstant( constant ); + + if (g_bVerbose) + { + ::System::Console::WriteLine( + "> emitting constant type {0}", cts_name ); + } + ret_type = type_builder->CreateType(); + } + return ret_type; +} + + +::System::Type ^ TypeEmitter::get_type( + Reference< reflection::XConstantsTypeDescription > const & xType ) +{ + ::System::String ^ cts_name = to_cts_name( xType->getName() ); + ::System::Type ^ ret_type = get_type( cts_name, false /* no exc */ ); + if (nullptr == ret_type) + { + Emit::TypeBuilder ^ type_builder = + m_module_builder->DefineType( + cts_name, + (TypeAttributes) (TypeAttributes::Public | + TypeAttributes::Sealed | + TypeAttributes::BeforeFieldInit | + TypeAttributes::AnsiClass) ); + + Sequence< Reference< + reflection::XConstantTypeDescription > > seq_constants( + xType->getConstants() ); + Reference< reflection::XConstantTypeDescription > const * constants = + seq_constants.getConstArray(); + sal_Int32 constants_length = seq_constants.getLength(); + for ( sal_Int32 constants_pos = 0; + constants_pos < constants_length; ++constants_pos ) + { + Reference< + reflection::XConstantTypeDescription > const & xConstant = + constants[ constants_pos ]; + ::System::Object ^ constant = + to_cli_constant( xConstant->getConstantValue() ); + ::System::String ^ uno_name = + ustring_to_String( xConstant->getName() ); + Emit::FieldBuilder ^ field_builder = type_builder->DefineField( + uno_name->Substring( uno_name->LastIndexOf( '.' ) +1 ), + constant->GetType(), + (FieldAttributes) (FieldAttributes::Public | + FieldAttributes::Static | + FieldAttributes::Literal) ); + field_builder->SetConstant( constant ); + } + + if (g_bVerbose) + { + ::System::Console::WriteLine( + "> emitting constants group type {0}", cts_name ); + } + ret_type = type_builder->CreateType(); + } + return ret_type; +} + + +::System::Type ^ TypeEmitter::get_type( + Reference< reflection::XEnumTypeDescription > const & xType ) +{ + ::System::String ^ cts_name = to_cts_name( xType->getName() ); + ::System::Type ^ ret_type = get_type( cts_name, false /* no exc */ ); + if (nullptr == ret_type) + { + // workaround enum builder bug + Emit::TypeBuilder ^ enum_builder = + m_module_builder->DefineType( + cts_name, + (TypeAttributes) (TypeAttributes::Public | + TypeAttributes::Sealed), + ::System::Enum::typeid ); + enum_builder->DefineField( + "value__", ::System::Int32::typeid, + (FieldAttributes) (FieldAttributes::Private | + FieldAttributes::SpecialName | + FieldAttributes::RTSpecialName) ); + Sequence< OUString > seq_enum_names( xType->getEnumNames() ); + Sequence< sal_Int32 > seq_enum_values( xType->getEnumValues() ); + sal_Int32 enum_length = seq_enum_names.getLength(); + OSL_ASSERT( enum_length == seq_enum_values.getLength() ); + OUString const * enum_names = seq_enum_names.getConstArray(); + sal_Int32 const * enum_values = seq_enum_values.getConstArray(); + for ( sal_Int32 enum_pos = 0; enum_pos < enum_length; ++enum_pos ) + { +// enum_builder->DefineLiteral( +// ustring_to_String( enum_names[ enum_pos ] ), +// __box ((::System::Int32) enum_values[ enum_pos ]) ); + Emit::FieldBuilder ^ field_builder = + enum_builder->DefineField( + ustring_to_String( enum_names[ enum_pos ] ), + enum_builder, + (FieldAttributes) (FieldAttributes::Public | + FieldAttributes::Static | + FieldAttributes::Literal) ); + field_builder->SetConstant( + ((::System::Int32) enum_values[ enum_pos ]) ); + } + + if (g_bVerbose) + { + ::System::Console::WriteLine( + "> emitting enum type {0}", cts_name ); + } + ret_type = enum_builder->CreateType(); + } + return ret_type; +} + + +::System::Type ^ TypeEmitter::get_type( + Reference< reflection::XCompoundTypeDescription > const & xType ) +{ + OUString uno_name( xType->getName() ); + if (TypeClass_EXCEPTION == xType->getTypeClass()) + { + if ( uno_name == "com.sun.star.uno.Exception" ) + { + return get_type_Exception(); + } + if ( uno_name == "com.sun.star.uno.RuntimeException" ) + { + return get_type_RuntimeException(); + } + } + ::System::String ^ cts_name = to_cts_name( uno_name ); + // if the struct is an instantiated polymorphic struct then we create the simple struct name + // For example: + // void func ([in] PolyStruct<boolean> arg); + //PolyStruct<boolean> will be converted to PolyStruct + polymorphicStructNameToStructName( & cts_name); + + ::System::Type ^ ret_type = get_type( cts_name, false /* no exc */ ); + if (nullptr == ret_type) + { + Reference< reflection::XCompoundTypeDescription > xBaseType( + xType->getBaseType(), UNO_QUERY ); + ::System::Type ^ base_type = (xBaseType.is() + ? get_type( xBaseType ) + : ::System::Object::typeid); + Emit::TypeBuilder ^ type_builder = + m_module_builder->DefineType( + cts_name, + (TypeAttributes) (TypeAttributes::Public | + TypeAttributes::BeforeFieldInit | + TypeAttributes::AnsiClass), + base_type ); + + + // insert to be completed + struct_entry ^ entry = gcnew struct_entry(); + xType->acquire(); + entry->m_xType = xType.get(); + entry->m_type_builder = type_builder; + entry->m_base_type = base_type; + m_incomplete_structs->Add( cts_name, entry ); + + // type is incomplete + ret_type = type_builder; + } + + //In case of an instantiated polymorphic struct we want to return a + //uno.PolymorphicType (inherits Type) rather than Type. This is needed for constructing + //the service code. We can only do that if the struct is completed. + if (m_generated_structs[cts_name]) + { + Reference< reflection::XStructTypeDescription> xStructTypeDesc( + xType, UNO_QUERY); + + if (xStructTypeDesc.is()) + { + Sequence< Reference< reflection::XTypeDescription > > seqTypeArgs = xStructTypeDesc->getTypeArguments(); + sal_Int32 numTypes = seqTypeArgs.getLength(); + if (numTypes > 0) + { + //it is an instantiated polymorphic struct + ::System::String ^ sCliName = mapUnoTypeName(ustring_to_String(xType->getName())); + ret_type = ::uno::PolymorphicType::GetType(ret_type, sCliName); + } + } + } + return ret_type; +} + + +::System::Type ^ TypeEmitter::get_type( + Reference< reflection::XInterfaceTypeDescription2 > const & xType ) +{ + OUString uno_name( xType->getName() ); + if ( uno_name == "com.sun.star.uno.XInterface" ) + { + return ::System::Object::typeid; + } + + ::System::String ^ cts_name = to_cts_name( xType->getName() ); + ::System::Type ^ ret_type = get_type( cts_name, false /* no exc */ ); + if (nullptr == ret_type) + { + Emit::TypeBuilder ^ type_builder; + + TypeAttributes attr = (TypeAttributes) (TypeAttributes::Public | + TypeAttributes::Interface | + TypeAttributes::Abstract | + TypeAttributes::AnsiClass); + + std::vector<Reference<reflection::XInterfaceTypeDescription2> > vecBaseTypes; + Sequence<Reference< reflection::XTypeDescription > > seqBaseTypes = + xType->getBaseTypes(); + if (seqBaseTypes.getLength() > 0) + { + for (int i = 0; i < seqBaseTypes.getLength(); i++) + { + Reference<reflection::XInterfaceTypeDescription2> xIfaceTd = + resolveInterfaceTypedef(seqBaseTypes[i]); + + if ( xIfaceTd->getName() != "com.sun.star.uno.XInterface" ) + { + vecBaseTypes.push_back(xIfaceTd); + } + } + + array< ::System::Type^>^ base_interfaces = + gcnew array< ::System::Type^>( vecBaseTypes.size() ); + + int index = 0; + for (auto const & vecBaseType : vecBaseTypes) + { + base_interfaces[ index ] = get_type( vecBaseType ); + ++index; + } + type_builder = m_module_builder->DefineType( + cts_name, attr, nullptr, base_interfaces ); + } + else + { + ::System::Console::WriteLine( + "warning: IDL interface {0} is not derived from " + "com.sun.star.uno.XInterface!", + ustring_to_String( uno_name ) ); + + type_builder = m_module_builder->DefineType( cts_name, attr ); + } + + // insert to be completed + iface_entry ^ entry = gcnew iface_entry(); + xType->acquire(); + entry->m_xType = xType.get(); + entry->m_type_builder = type_builder; + m_incomplete_ifaces->Add( cts_name, entry ); + + // type is incomplete + ret_type = type_builder; + } + return ret_type; +} + + +::System::Type ^ TypeEmitter::get_type( + Reference< reflection::XServiceTypeDescription2 > const & xType ) +{ + if (xType->isSingleInterfaceBased() == sal_False) + return nullptr; + + System::String ^ cts_name = to_cts_name( xType->getName() ); + System::Type ^ ret_type = get_type( cts_name, false /* no exc */ ); + if (ret_type != nullptr) + return ret_type; + + TypeAttributes attr = (TypeAttributes) (TypeAttributes::Public | + TypeAttributes::Sealed | + TypeAttributes::BeforeFieldInit | + TypeAttributes::AnsiClass); + + Emit::TypeBuilder ^ type_builder = m_module_builder->DefineType( + cts_name, attr); + + // insert to be completed + service_entry ^ entry = gcnew service_entry(); + xType->acquire(); + entry->m_xType = xType.get(); + entry->m_type_builder = type_builder; + m_incomplete_services->Add(cts_name,entry ); + + return type_builder; +} + +::System::Type ^ TypeEmitter::get_type( + Reference<reflection::XSingletonTypeDescription2 > const & xType ) +{ + if (xType->isInterfaceBased() == sal_False) + return nullptr; + + ::System::String^ cts_name = to_cts_name( xType->getName() ); + ::System::Type ^ ret_type = get_type( cts_name, false /* no exc */ ); + if (ret_type != nullptr) + return ret_type; + + TypeAttributes attr = static_cast<TypeAttributes>( + TypeAttributes::Public | + TypeAttributes::Sealed | + TypeAttributes::BeforeFieldInit | + TypeAttributes::AnsiClass); + + Emit::TypeBuilder ^ type_builder = m_module_builder->DefineType( + cts_name, attr); + + // insert to be completed + singleton_entry ^ entry = gcnew singleton_entry(); + xType->acquire(); + entry->m_xType = xType.get(); + entry->m_type_builder = type_builder; + m_incomplete_singletons->Add(cts_name,entry ); + + return type_builder; + +} + + +::System::Type ^ TypeEmitter::complete_iface_type( iface_entry ^ entry ) +{ + Emit::TypeBuilder ^ type_builder = entry->m_type_builder; + reflection::XInterfaceTypeDescription2 * xType = entry->m_xType; + + Sequence<Reference< reflection::XTypeDescription > > seqBaseTypes( xType->getBaseTypes() ); + if (seqBaseTypes.getLength() > 0) + { + for (int i = 0; i < seqBaseTypes.getLength(); i++) + { + //make sure we get the interface rather than a typedef + Reference<reflection::XInterfaceTypeDescription2> aBaseType = + resolveInterfaceTypedef( seqBaseTypes[i]); + + if ( aBaseType->getName() != "com.sun.star.uno.XInterface" ) + { + ::System::String ^ basetype_name = to_cts_name( aBaseType->getName() ); + iface_entry ^ base_entry = dynamic_cast< iface_entry ^ >( + m_incomplete_ifaces[basetype_name] ); + if (nullptr != base_entry) + { + // complete uncompleted base type first + complete_iface_type( base_entry ); + } + } + } + } + + Sequence< + Reference< reflection::XInterfaceMemberTypeDescription > > seq_members( + xType->getMembers() ); + Reference< reflection::XInterfaceMemberTypeDescription > const * members = + seq_members.getConstArray(); + sal_Int32 members_length = seq_members.getLength(); + for ( sal_Int32 members_pos = 0; + members_pos < members_length; ++members_pos ) + { + Reference< + reflection::XInterfaceMemberTypeDescription > const & xMember = + members[ members_pos ]; + Sequence< Reference< reflection::XTypeDescription > > seq_exceptions; + Emit::MethodBuilder ^ method_builder; + + MethodAttributes c_method_attr = (MethodAttributes) + (MethodAttributes::Public | + MethodAttributes::Abstract | + MethodAttributes::Virtual | + MethodAttributes::NewSlot | + MethodAttributes::HideBySig); + + if (TypeClass_INTERFACE_METHOD == xMember->getTypeClass()) + { + Reference< reflection::XInterfaceMethodTypeDescription > xMethod( + xMember, UNO_QUERY_THROW ); + + Sequence< + Reference< reflection::XMethodParameter > > seq_parameters( + xMethod->getParameters() ); + sal_Int32 params_length = seq_parameters.getLength(); + array< ::System::Type^>^ param_types = + gcnew array< ::System::Type^>( params_length ); + Reference< reflection::XMethodParameter > const * parameters = + seq_parameters.getConstArray(); + // first determine all types + //Make the first param type as return type + sal_Int32 params_pos = 0; + for ( ; params_pos < params_length; ++params_pos ) + { + Reference< reflection::XMethodParameter > const & xParam = + parameters[ params_pos ]; + ::System::Type ^ param_type = get_type( xParam->getType() ); + ::System::String ^ param_type_name = param_type->FullName; + if (xParam->isOut()) + { + param_type = get_type( + ::System::String::Concat( + param_type_name, "&" ), true ); + } + param_types[ xParam->getPosition() ] = param_type; + } + + + // create method +// if (tb) +// method_builder = type_builder->DefineMethod( +// ustring_to_String( xMethod->getMemberName() ), +// c_method_attr, tb, +// param_types ); +// else + method_builder = type_builder->DefineMethod( + ustring_to_String( xMethod->getMemberName() ), + c_method_attr, get_type( xMethod->getReturnType() ), + param_types ); + // then define parameter infos + params_pos = 0; + for ( ; params_pos < params_length; ++params_pos ) + { + Reference< reflection::XMethodParameter > const & xParam = + parameters[ params_pos ]; + long param_flags = 0; + if (xParam->isIn()) + param_flags |= (long)ParameterAttributes::In; + if (xParam->isOut()) + param_flags |= (long)ParameterAttributes::Out; + OSL_ASSERT( 0 != param_flags ); + method_builder->DefineParameter( + xParam->getPosition() +1 /* starts with 1 */, + (ParameterAttributes) param_flags, + ustring_to_String( xParam->getName() ) ); + } + //Apply attribute TypeParametersAttribute to return value if it + //is a parameterized Type. Currently only structs can have parameters. + Reference<reflection::XStructTypeDescription> xReturnStruct( + xMethod->getReturnType(), UNO_QUERY); + + if (xReturnStruct.is()) + { + Sequence<Reference<reflection::XTypeDescription> > seq_type_args = + xReturnStruct->getTypeArguments(); + if (seq_type_args.getLength() != 0) + { + //get th ctor of the attribute + array< ::System::Type^>^ arCtor = {::System::Type::GetType("System.Type[]")}; + //Get the arguments for the attribute's ctor + Reference<reflection::XTypeDescription> const * arXTypeArgs = + seq_type_args.getConstArray(); + int numTypes = seq_type_args.getLength(); + array< ::System::Type^>^ arCtsTypes = gcnew array< ::System::Type^>(numTypes); + for (int i = 0; i < numTypes; i++) + arCtsTypes[i] = get_type(arXTypeArgs[i]); + array< ::System::Object^>^ arArgs = {arCtsTypes}; + + Emit::CustomAttributeBuilder ^ attrBuilder = + gcnew Emit::CustomAttributeBuilder( + ::uno::TypeArgumentsAttribute::typeid + ->GetConstructor( arCtor), + arArgs); + + method_builder->SetCustomAttribute(attrBuilder); + } + } + + //define UNO exception attribute (exceptions)-------------------------------------- + Emit::CustomAttributeBuilder^ attrBuilder = + get_iface_method_exception_attribute(xMethod); + if (attrBuilder != nullptr) + method_builder->SetCustomAttribute(attrBuilder); + + // oneway attribute + if (xMethod->isOneway()) + { + array< ::System::Type^>^ arCtorOneway = gcnew array< ::System::Type^>(0); + array< ::System::Object^>^ arArgs = gcnew array< ::System::Object^>(0); + Emit::CustomAttributeBuilder ^ attrBuilder = + gcnew Emit::CustomAttributeBuilder( + ::uno::OnewayAttribute::typeid->GetConstructor( arCtorOneway), + arArgs); + method_builder->SetCustomAttribute(attrBuilder); + } + } + else // attribute + { + OSL_ASSERT( + TypeClass_INTERFACE_ATTRIBUTE == xMember->getTypeClass() ); + Reference< + reflection::XInterfaceAttributeTypeDescription2 > xAttribute( + xMember, UNO_QUERY_THROW ); + + MethodAttributes c_property_method_attr = (MethodAttributes) + (c_method_attr | MethodAttributes::SpecialName); + + ::System::Type ^ attribute_type = get_type( xAttribute->getType() ); + array< ::System::Type^>^ parameters = + gcnew array< ::System::Type^> ( 0 ); + + Emit::PropertyBuilder ^ property_builder = + type_builder->DefineProperty( + ustring_to_String( xAttribute->getMemberName() ), + PropertyAttributes::None, + attribute_type, parameters ); + + //set BoundAttribute, if necessary + if (xAttribute->isBound()) + { + ConstructorInfo ^ ctorBoundAttr = + ::uno::BoundAttribute::typeid->GetConstructor( + gcnew array<System::Type^>(0)); + Emit::CustomAttributeBuilder ^ attrBuilderBound = + gcnew Emit::CustomAttributeBuilder( + ctorBoundAttr, gcnew array< ::System::Object^>(0)); + property_builder->SetCustomAttribute(attrBuilderBound); + } + + // getter + Emit::MethodBuilder ^ method_builder = + type_builder->DefineMethod( + ustring_to_String( "get_" + + xAttribute->getMemberName() ), + c_property_method_attr, attribute_type, parameters ); + + //define UNO exception attribute (exceptions)-------------------------------------- + Emit::CustomAttributeBuilder^ attrBuilder = + get_exception_attribute(xAttribute->getGetExceptions()); + if (attrBuilder != nullptr) + method_builder->SetCustomAttribute(attrBuilder); + + property_builder->SetGetMethod( method_builder ); + + if (! xAttribute->isReadOnly()) + { + // setter + parameters = gcnew array< ::System::Type^> ( 1 ); + parameters[ 0 ] = attribute_type; + method_builder = + type_builder->DefineMethod( + ustring_to_String( "set_" + + xAttribute->getMemberName() ), + c_property_method_attr, nullptr, parameters ); + // define parameter info + method_builder->DefineParameter( + 1 /* starts with 1 */, ParameterAttributes::In, "value" ); + //define UNO exception attribute (exceptions)-------------------------------------- + Emit::CustomAttributeBuilder^ attrBuilder = + get_exception_attribute(xAttribute->getSetExceptions()); + if (attrBuilder != nullptr) + method_builder->SetCustomAttribute(attrBuilder); + + property_builder->SetSetMethod( method_builder ); + } + } + } + + // remove from incomplete types map + ::System::String ^ cts_name = type_builder->FullName; + m_incomplete_ifaces->Remove( cts_name ); + xType->release(); + + if (g_bVerbose) + { + ::System::Console::WriteLine( + "> emitting interface type {0}", cts_name ); + } + return type_builder->CreateType(); +} + +::System::Type ^ TypeEmitter::complete_struct_type( struct_entry ^ entry ) +{ + OSL_ASSERT(entry); + ::System::String ^ cts_name = entry->m_type_builder->FullName; + + //Polymorphic struct, define uno.TypeParametersAttribute + //A polymorphic struct cannot have a basetype. + //When we create the template of the struct then we have no exact types + //and the name does not contain a parameter list + Sequence< OUString > seq_type_parameters; + Reference< reflection::XStructTypeDescription> xStructTypeDesc( + entry->m_xType, UNO_QUERY); + if (xStructTypeDesc.is()) + { + seq_type_parameters = xStructTypeDesc->getTypeParameters(); + int numTypes = 0; + if ((numTypes = seq_type_parameters.getLength()) > 0) + { + array< ::System::Object^>^ aArg = gcnew array< ::System::Object^>(numTypes); + for (int i = 0; i < numTypes; i++) + aArg[i] = ustring_to_String(seq_type_parameters.getConstArray()[i]); + array< ::System::Object^>^ args = {aArg}; + + array< ::System::Type^>^ arTypesCtor = + {::System::Type::GetType("System.String[]")}; + Emit::CustomAttributeBuilder ^ attrBuilder = + gcnew Emit::CustomAttributeBuilder( + ::uno::TypeParametersAttribute::typeid->GetConstructor(arTypesCtor), + args); + entry->m_type_builder->SetCustomAttribute(attrBuilder); + } + } + + // optional: lookup base type whether generated entry of this session + struct_entry ^ base_type_entry = nullptr; + if (nullptr != entry->m_base_type) + { + //ToDo maybe get from incomplete structs + base_type_entry = + dynamic_cast< struct_entry ^ >( + m_generated_structs[ + entry->m_base_type->FullName ] ); + } + + // members + Sequence< Reference< reflection::XTypeDescription > > seq_members( + entry->m_xType->getMemberTypes() ); + Sequence< OUString > seq_member_names( entry->m_xType->getMemberNames() ); + sal_Int32 members_length = seq_members.getLength(); + OSL_ASSERT( seq_member_names.getLength() == members_length ); + //check if we have a XTypeDescription for every member. If not then the user may + //have forgotten to specify additional rdbs with the --extra option. + Reference< reflection::XTypeDescription > const * pseq_members = + seq_members.getConstArray(); + OUString const * pseq_member_names = + seq_member_names.getConstArray(); + for (int i = 0; i < members_length; i++) + { + const OUString sType(entry->m_xType->getName()); + const OUString sMemberName(pseq_member_names[i]); + if ( ! pseq_members[i].is()) + throw RuntimeException("Missing type description . Check if you need to " + "specify additional RDBs with the --extra option. Type missing for: " + sType + + "::" + sMemberName,0); + } + + sal_Int32 all_members_length = 0; + sal_Int32 member_pos; + sal_Int32 type_param_pos = 0; + + // collect base types; wrong order + ::System::Collections::ArrayList ^ base_types_list = + gcnew ::System::Collections::ArrayList( 3 /* initial capacity */ ); + for (::System::Type ^ base_type_pos = entry->m_base_type; + ! base_type_pos->Equals( ::System::Object::typeid ); + base_type_pos = base_type_pos->BaseType ) + { + base_types_list->Add( base_type_pos ); + if (base_type_pos->Equals( ::System::Exception::typeid )) + { + // special Message member + all_members_length += 1; + break; // don't include System.Exception base classes + } + else + { + //ensure the base type is complete. Otherwise GetFields won't work + get_complete_struct(base_type_pos->FullName); + all_members_length += + base_type_pos->GetFields( + (BindingFlags) (BindingFlags::Instance | + BindingFlags::Public | + BindingFlags::DeclaredOnly) ) + ->Length; + } + } + + // create all_members arrays; right order + array< ::System::String^>^ all_member_names = + gcnew array< ::System::String^> (all_members_length + members_length ); + array< ::System::Type^>^ all_param_types = + gcnew array< ::System::Type^> (all_members_length + members_length ); + member_pos = 0; + for ( sal_Int32 pos = base_types_list->Count; pos--; ) + { + ::System::Type ^ base_type = safe_cast< ::System::Type ^ >( + base_types_list[pos] ); + if (base_type->Equals( ::System::Exception::typeid )) + { + all_member_names[ member_pos ] = "Message"; + all_param_types[ member_pos ] = ::System::String::typeid; + ++member_pos; + } + else + { + ::System::String ^ base_type_name = base_type->FullName; + + //ToDo m_generated_structs? + struct_entry ^ entry = + dynamic_cast< struct_entry ^ >( + m_generated_structs[base_type_name] ); + if (nullptr == entry) + { + // complete type + array<FieldInfo^>^ fields = + base_type->GetFields( + (BindingFlags) (BindingFlags::Instance | + BindingFlags::Public | + BindingFlags::DeclaredOnly) ); + sal_Int32 len = fields->Length; + for ( sal_Int32 pos = 0; pos < len; ++pos ) + { + FieldInfo ^ field = fields[ pos ]; + all_member_names[ member_pos ] = field->Name; + all_param_types[ member_pos ] = field->FieldType; + ++member_pos; + } + } + else // generated during this session: + // members may be incomplete ifaces + { + sal_Int32 len = entry->m_member_names->Length; + for ( sal_Int32 pos = 0; pos < len; ++pos ) + { + all_member_names[ member_pos ] = + entry->m_member_names[ pos ]; + all_param_types[ member_pos ] = + entry->m_param_types[ pos ]; + ++member_pos; + } + } + } + } + OSL_ASSERT( all_members_length == member_pos ); + + // build up entry +// struct_entry * entry = new struct_entry(); + entry->m_member_names = gcnew array< ::System::String^> ( members_length ); + entry->m_param_types = gcnew array< ::System::Type^> ( members_length ); + + // add members + array<Emit::FieldBuilder^>^ members = gcnew array<Emit::FieldBuilder^> ( members_length ); + //Reference< reflection::XTypeDescription > const * pseq_members = + // seq_members.getConstArray(); + //OUString const * pseq_member_names = + // seq_member_names.getConstArray(); + + int curParamIndex = 0; //count the fields which have parameterized types + for ( member_pos = 0; member_pos < members_length; ++member_pos ) + { + ::System::String ^ field_name = + ustring_to_String( pseq_member_names[ member_pos ] ); + ::System::Type ^ field_type; + //Special handling of struct parameter types + bool bParameterizedType = false; + if (pseq_members[ member_pos ]->getTypeClass() == TypeClass_UNKNOWN) + { + bParameterizedType = true; + if (type_param_pos < seq_type_parameters.getLength()) + { + field_type = ::System::Object::typeid; + type_param_pos++; + } + else + { + throw RuntimeException( + "unexpected member type in " + entry->m_xType->getName() ); + } + } + else + { + field_type = get_type( pseq_members[ member_pos ] ); + + if (field_type->IsArray + && m_incomplete_structs[cts_name] + && !field_type->Namespace->Equals("System")) + { + //Find the value type. In case of sequence<sequence< ... > > find the actual value type + ::System::Type ^ value = field_type; + while ((value = value->GetElementType())->IsArray); + //If the value type is a struct then make sure it is fully created. + get_complete_struct(value->FullName); + field_type = get_type(pseq_members[member_pos]); + } + } + members[ member_pos ] = + entry->m_type_builder->DefineField( + field_name, field_type, FieldAttributes::Public ); + + //parameterized type (polymorphic struct) ? + if (bParameterizedType && xStructTypeDesc.is()) + { + //get the name + OSL_ASSERT(seq_type_parameters.getLength() > curParamIndex); + ::System::String^ sTypeName = ustring_to_String( + seq_type_parameters.getConstArray()[curParamIndex++]); + array< ::System::Object^>^ args = {sTypeName}; + //set ParameterizedTypeAttribute + array< ::System::Type^>^ arCtorTypes = {::System::String::typeid}; + + Emit::CustomAttributeBuilder ^ attrBuilder = + gcnew Emit::CustomAttributeBuilder( + ::uno::ParameterizedTypeAttribute::typeid + ->GetConstructor(arCtorTypes), + args); + + members[member_pos]->SetCustomAttribute(attrBuilder); + } + // add to all_members + all_member_names[ all_members_length + member_pos ] = field_name; + all_param_types[ all_members_length + member_pos ] = field_type; + // add to entry + entry->m_member_names[ member_pos ] = field_name; + entry->m_param_types[ member_pos ] = field_type; + } + all_members_length += members_length; + + // default .ctor + Emit::ConstructorBuilder ^ ctor_builder = + entry->m_type_builder->DefineConstructor( + c_ctor_method_attr, CallingConventions::Standard, + gcnew array< ::System::Type^> ( 0 ) ); + Emit::ILGenerator ^ code = ctor_builder->GetILGenerator(); + code->Emit( Emit::OpCodes::Ldarg_0 ); + code->Emit( + Emit::OpCodes::Call, + nullptr == base_type_entry + ? entry->m_base_type->GetConstructor( gcnew array< ::System::Type^> ( 0 ) ) + : base_type_entry->m_default_ctor ); + // default initialize members + for ( member_pos = 0; member_pos < members_length; ++member_pos ) + { + FieldInfo ^ field = members[ member_pos ]; + ::System::Type ^ field_type = field->FieldType; + // ::System::Type * new_field_type = m_module_builder->GetType(field_type->FullName, false); + // default initialize: + // string, type, enum, sequence, struct, exception, any + if (field_type->Equals( ::System::String::typeid )) + { + code->Emit( Emit::OpCodes::Ldarg_0 ); + code->Emit( Emit::OpCodes::Ldstr, "" ); + code->Emit( Emit::OpCodes::Stfld, field ); + } + else if (field_type->Equals( ::System::Type::typeid )) + { + code->Emit( Emit::OpCodes::Ldarg_0 ); + code->Emit( + Emit::OpCodes::Ldtoken, ::System::Void::typeid ); + code->Emit( + Emit::OpCodes::Call, m_method_info_Type_GetTypeFromHandle ); + code->Emit( Emit::OpCodes::Stfld, field ); + } + else if (field_type->IsArray) + { + code->Emit( Emit::OpCodes::Ldarg_0 ); + code->Emit( Emit::OpCodes::Ldc_I4_0 ); + code->Emit( + Emit::OpCodes::Newarr, field_type->GetElementType() ); + code->Emit( Emit::OpCodes::Stfld, field ); + } + else if (field_type->IsValueType) + { + if (field_type->FullName->Equals( "uno.Any" )) + { + code->Emit( Emit::OpCodes::Ldarg_0 ); + code->Emit( Emit::OpCodes::Ldsfld, ::uno::Any::typeid->GetField("VOID")); + code->Emit( Emit::OpCodes::Stfld, field ); + } + } + else if (field_type->IsClass) + { + /* may be XInterface */ + if (! field_type->Equals( ::System::Object::typeid )) + { + // struct, exception + //make sure the struct is already complete. + get_complete_struct(field_type->FullName); + code->Emit( Emit::OpCodes::Ldarg_0 ); + code->Emit( + Emit::OpCodes::Newobj, + //GetConstructor requires that the member types of the object which is to be constructed are already known. + field_type->GetConstructor( + gcnew array< ::System::Type^> ( 0 ) ) ); + code->Emit( Emit::OpCodes::Stfld, field ); + } + } + } + code->Emit( Emit::OpCodes::Ret ); + entry->m_default_ctor = ctor_builder; + + // parameterized .ctor including all base members + ctor_builder = entry->m_type_builder->DefineConstructor( + c_ctor_method_attr, CallingConventions::Standard, all_param_types ); + for ( member_pos = 0; member_pos < all_members_length; ++member_pos ) + { + ctor_builder->DefineParameter( + member_pos +1 /* starts with 1 */, ParameterAttributes::In, + all_member_names[ member_pos ] ); + } + code = ctor_builder->GetILGenerator(); + // call base .ctor + code->Emit( Emit::OpCodes::Ldarg_0 ); // push this + sal_Int32 base_members_length = all_members_length - members_length; + array< ::System::Type^>^ param_types = + gcnew array< ::System::Type^> ( base_members_length ); + for ( member_pos = 0; member_pos < base_members_length; ++member_pos ) + { + emit_ldarg( code, member_pos +1 ); + param_types[ member_pos ] = all_param_types[ member_pos ]; + } + code->Emit( + Emit::OpCodes::Call, + nullptr == base_type_entry + ? entry->m_base_type->GetConstructor( param_types ) + : base_type_entry->m_ctor ); + // initialize members + for ( member_pos = 0; member_pos < members_length; ++member_pos ) + { + code->Emit( Emit::OpCodes::Ldarg_0 ); // push this + emit_ldarg( code, member_pos + base_members_length +1 ); + code->Emit( Emit::OpCodes::Stfld, members[ member_pos ] ); + } + code->Emit( Emit::OpCodes::Ret ); + entry->m_ctor = ctor_builder; + + if (g_bVerbose) + { + ::System::Console::WriteLine( + "> emitting {0} type {1}", + TypeClass_STRUCT == entry->m_xType->getTypeClass() + ? "struct" + : "exception", + cts_name); + } + // new entry + m_generated_structs->Add(cts_name, entry ); + ::System::Type ^ ret_type = entry->m_type_builder->CreateType(); + + // remove from incomplete types map + m_incomplete_structs->Remove( cts_name ); + entry->m_xType->release(); + + if (g_bVerbose) + { + ::System::Console::WriteLine( + "> emitting struct type {0}", cts_name); + } + return ret_type; +} + +//Examples of generated code +// public static XWeak constructor1(XComponentContext ctx) +// { +// XMultiComponentFactory factory = ctx.getServiceManager(); +// if (factory == null) +// throw new com.sun.star.uno.DeploymentException("bla", null); +// return (XWeak) factory.createInstanceWithContext("service_specifier", ctx); +// } +// public static XWeak constructor2(XComponentContext ctx, int a, int b, Any c) +// { +// XMultiComponentFactory factory = ctx.getServiceManager(); +// if (factory == null) +// throw new com.sun.star.uno.DeploymentException("bla", null); +// Any[] arAny = new Any[3]; +// arAny[0] = new Any(typeof(int), a); +// arAny[1] = new Any(typeof(int), b); +// arAny[2] = new Any(c.Type, c.Value); +// return (XWeak) factory.createInstanceWithArgumentsAndContext("service_specifier", arAny, ctx); +// } +// Notice that an any parameter is NOT wrapped by another any. Instead the new any is created with the type and value +// of the parameter. + +// public static XWeak constructor3(XComponentContext ctx, params Any[] c) +// { +// XMultiComponentFactory factory = ctx.getServiceManager(); +// if (factory == null) +// throw new com.sun.star.uno.DeploymentException("bla", null); +// return (XWeak) factory.createInstanceWithArgumentsAndContext("service_specifier", c, ctx); +// } +::System::Type ^ TypeEmitter::complete_service_type(service_entry ^ entry) +{ + Emit::TypeBuilder ^ type_builder = entry->m_type_builder; + reflection::XServiceTypeDescription2 * xServiceType = entry->m_xType; + + //Create the private default constructor + Emit::ConstructorBuilder^ ctor_builder = + type_builder->DefineConstructor( + (MethodAttributes) (MethodAttributes::Private | + MethodAttributes::HideBySig | + MethodAttributes::SpecialName | + MethodAttributes::RTSpecialName), + CallingConventions::Standard, nullptr); + + Emit::ILGenerator^ ilGen = ctor_builder->GetILGenerator(); + ilGen->Emit( Emit::OpCodes::Ldarg_0 ); // push this + ilGen->Emit( + Emit::OpCodes::Call, + type_builder->BaseType->GetConstructor(gcnew array< ::System::Type^>(0))); + ilGen->Emit( Emit::OpCodes::Ret ); + + + //Create the service constructors. + //obtain the interface which makes up this service, it is the return + //type of the constructor functions + Reference<reflection::XInterfaceTypeDescription2> xIfaceType( + xServiceType->getInterface(), UNO_QUERY); + if (xIfaceType.is () == sal_False) + xIfaceType = resolveInterfaceTypedef(xServiceType->getInterface()); + System::Type ^ retType = get_type(xIfaceType); + + //Create the ConstructorInfo for a DeploymentException + ::System::Type ^ typeDeploymentExc = + get_type("unoidl.com.sun.star.uno.DeploymentException", true); + + array< ::System::Type^>^ arTypeCtor = {::System::String::typeid, + ::System::Object::typeid}; + ::System::Reflection::ConstructorInfo ^ ctorDeploymentException = + typeDeploymentExc->GetConstructor(arTypeCtor); + + Sequence<Reference<reflection::XServiceConstructorDescription> > seqCtors = + xServiceType->getConstructors(); + + ::System::Type ^ type_uno_exception = get_type("unoidl.com.sun.star.uno.Exception", true); + + for (int i = seqCtors.getLength() - 1; i >= 0; i--) + { + bool bParameterArray = false; + ::System::Type ^ typeAny = ::uno::Any::typeid; + const Reference<reflection::XServiceConstructorDescription> & ctorDes = + seqCtors[i]; + //obtain the parameter types + Sequence<Reference<reflection::XParameter> > seqParams = + ctorDes->getParameters(); + Reference<reflection::XParameter> const * arXParams = seqParams.getConstArray(); + sal_Int32 cParams = seqParams.getLength(); + array< ::System::Type^>^ arTypeParameters = gcnew array< ::System::Type^> (cParams + 1); + arTypeParameters[0] = get_type("unoidl.com.sun.star.uno.XComponentContext", true); + for (int iparam = 0; iparam != cParams; iparam++) + { + if (arXParams[iparam]->isRestParameter()) + arTypeParameters[iparam + 1] = array< ::uno::Any>::typeid; + else + arTypeParameters[iparam + 1] = get_type(arXParams[iparam]->getType()); + } + //The array arTypeParameters can contain: + //System.Type and uno.PolymorphicType. + //Passing PolymorphicType to MethodBuilder.DefineMethod will cause a problem. + //The exception will read something like no on information for parameter # d + //Maybe we need no override another Type method in PolymorphicType ... + //Until we have figured this out, we will create another array of System.Type which + //we pass on to DefineMethod. + array< ::System::Type^>^ arParamTypes = gcnew array< ::System::Type^> (cParams + 1); +// arParamTypes[0] = get_type("unoidl.com.sun.star.uno.XComponentContext", true); + for (int i = 0; i < cParams + 1; i++) + { + ::uno::PolymorphicType ^ pT = dynamic_cast< ::uno::PolymorphicType ^ >(arTypeParameters[i]); + if (pT) + arParamTypes[i] = pT->OriginalType; + else + arParamTypes[i] = arTypeParameters[i]; + } + //define method + System::String ^ ctorName; + if (ctorDes->isDefaultConstructor()) + ctorName = gcnew ::System::String("create"); + else + ctorName = ustring_to_String(ctorDes->getName()); + Emit::MethodBuilder^ method_builder = type_builder->DefineMethod( + ctorName, + static_cast<MethodAttributes>(MethodAttributes::Public | MethodAttributes::HideBySig | + MethodAttributes::Static), + retType, +// arTypeParameters); + arParamTypes); + + //define UNO exception attribute (exceptions)-------------------------------------- + Emit::CustomAttributeBuilder^ attrBuilder = get_service_exception_attribute(ctorDes); + if (attrBuilder != nullptr) + method_builder->SetCustomAttribute(attrBuilder); + + + //define parameter attributes (paramarray), names etc. + //The first parameter is the XComponentContext, which cannot be obtained + //from reflection. + //The context is not part of the idl description + method_builder->DefineParameter( + 1, ParameterAttributes::In, "the_context"); + + array<Emit::ParameterBuilder^>^ arParameterBuilder = + gcnew array<Emit::ParameterBuilder^> (cParams); + for (int iparam = 0; iparam != cParams; iparam++) + { + Reference<reflection::XParameter> const & aParam = arXParams[iparam]; + ::System::String ^ sParamName = ustring_to_String(aParam->getName()); + + arParameterBuilder[iparam] = method_builder->DefineParameter( + iparam + 2, ParameterAttributes::In, sParamName); + + if (aParam->isRestParameter()) + { + bParameterArray = true; + //set the ParameterArrayAttribute + ::System::Reflection::ConstructorInfo^ ctor_info = + System::ParamArrayAttribute::typeid->GetConstructor( + gcnew array< ::System::Type^>(0)); + Emit::CustomAttributeBuilder ^ attr_builder = + gcnew Emit::CustomAttributeBuilder(ctor_info, gcnew array< ::System::Object^>(0)); + arParameterBuilder[iparam]->SetCustomAttribute(attr_builder); + break; + } + } + + Emit::ILGenerator ^ ilGen = method_builder->GetILGenerator(); + + //Define locals --------------------------------- + //XMultiComponentFactory + Emit::LocalBuilder^ local_factory = + ilGen->DeclareLocal( + get_type("unoidl.com.sun.star.lang.XMultiComponentFactory", true)); + + //The return type + Emit::LocalBuilder^ local_return_val = + ilGen->DeclareLocal(retType); + + //Obtain the XMultiComponentFactory and throw an exception if we do not get one + ilGen->Emit(Emit::OpCodes::Ldarg_0); + + ::System::Reflection::MethodInfo ^ methodGetServiceManager = get_type( + "unoidl.com.sun.star.uno.XComponentContext", true) + ->GetMethod("getServiceManager"); + ilGen->Emit(Emit::OpCodes::Callvirt, methodGetServiceManager); + ilGen->Emit(Emit::OpCodes::Stloc, local_factory); + ilGen->Emit(Emit::OpCodes::Ldloc, local_factory); + Emit::Label label1 = ilGen->DefineLabel(); + ilGen->Emit(Emit::OpCodes::Brtrue, label1); + //The string for the exception + ::System::Text::StringBuilder ^ strbuilder = gcnew ::System::Text::StringBuilder(256); + strbuilder->Append("The service "); + strbuilder->Append(to_cts_name(xServiceType->getName())); + strbuilder->Append(" could not be created. The context failed to supply the service manager."); + + ilGen->Emit(Emit::OpCodes::Ldstr, strbuilder->ToString()); + ilGen->Emit(Emit::OpCodes::Ldarg_0); + ilGen->Emit(Emit::OpCodes::Newobj, ctorDeploymentException); + ilGen->Emit(Emit::OpCodes::Throw); + ilGen->MarkLabel(label1); + + //We create a try/ catch around the createInstanceWithContext, etc. functions + //There are 3 cases + //1. function do not specify exceptions. Then RuntimeExceptions are re-thrown and other + // exceptions produce a DeploymentException. + //2. function specify Exception. Then all exceptions fly through + //3. function specifies exceptions but no Exception. Then these are rethrown + // and other exceptions, except RuntimeException, produce a deployment exception. + //In case there are no parameters we call + //XMultiComponentFactory.createInstanceWithContext + + ::System::Collections::ArrayList ^ arExceptionTypes = + get_service_ctor_method_exceptions_reduced(ctorDes->getExceptions()); + if (arExceptionTypes->Contains( + type_uno_exception) == false) + { + ilGen->BeginExceptionBlock(); + } + if (cParams == 0) + { + ilGen->Emit(Emit::OpCodes::Ldloc, local_factory); + ilGen->Emit(Emit::OpCodes::Ldstr, ustring_to_String(xServiceType->getName())); + ilGen->Emit(Emit::OpCodes::Ldarg_0); + + ::System::Reflection::MethodInfo ^ methodCreate = + local_factory->LocalType->GetMethod("createInstanceWithContext"); + ilGen->Emit(Emit::OpCodes::Callvirt, methodCreate); + } + else if(bParameterArray) + { + //Service constructor with parameter array + ilGen->Emit(Emit::OpCodes::Ldloc, local_factory); + ilGen->Emit(Emit::OpCodes::Ldstr, ustring_to_String(xServiceType->getName())); + ilGen->Emit(Emit::OpCodes::Ldarg_1); + ilGen->Emit(Emit::OpCodes::Ldarg_0); + ::System::Reflection::MethodInfo ^ methodCreate = + local_factory->LocalType->GetMethod("createInstanceWithArgumentsAndContext"); + ilGen->Emit(Emit::OpCodes::Callvirt, methodCreate); + } + else + { + // Any param1, Any param2, etc. + // For each parameter,except the component context, and parameter array + // and Any is created. + array<Emit::LocalBuilder^>^ arLocalAny = gcnew array<Emit::LocalBuilder^> (cParams); + + for (int iParam = 0; iParam < cParams; iParam ++) + { + arLocalAny[iParam] = ilGen->DeclareLocal(typeAny); + } + + //Any[]. This array is filled with the created Anys which contain the parameters + //and the values contained in the parameter array + Emit::LocalBuilder ^ local_anyParams = + ilGen->DeclareLocal(array< ::uno::Any>::typeid); + + //Create the Any for every argument, except for the parameter array + //arLocalAny contains the LocalBuilder for all these parameters. + //we call the ctor Any(Type, Object) + //If the parameter is an Any then the Any is created with Any(param.Type, param.Value); + array< ::System::Type^>^ arTypesCtorAny = {::System::Type::typeid, + ::System::Object::typeid}; + ::System::Reflection::ConstructorInfo ^ ctorAny = + typeAny->GetConstructor( arTypesCtorAny); + ::System::Reflection::MethodInfo ^ methodAnyGetType = + typeAny->GetProperty("Type")->GetGetMethod(); + ::System::Reflection::MethodInfo ^ methodAnyGetValue = + typeAny->GetProperty("Value")->GetGetMethod(); + for (int i = 0; i < arLocalAny->Length; i ++) + { + //check if the parameter is a polymorphic struct + ::uno::PolymorphicType ^polyType = dynamic_cast< ::uno::PolymorphicType^ >(arTypeParameters[i+1]); + //arTypeParameters[i+1] = polyType->OriginalType; + if (polyType) + { + //It is a polymorphic struct + //Load the uninitialized local Any on which we will call the ctor + ilGen->Emit(Emit::OpCodes::Ldloca, arLocalAny[i]); + // Call PolymorphicType PolymorphicType::GetType(Type t, String polyName) + // Prepare the first parameter + ilGen->Emit(Emit::OpCodes::Ldtoken, polyType->OriginalType); + array< ::System::Type^>^ arTypeParams = {::System::RuntimeTypeHandle::typeid}; + ilGen->Emit(Emit::OpCodes::Call, + ::System::Type::typeid->GetMethod( + "GetTypeFromHandle", arTypeParams)); + // Prepare the second parameter + ilGen->Emit(Emit::OpCodes::Ldstr, polyType->PolymorphicName); + // Make the actual call + array< ::System::Type^>^ arTypeParam_GetType = { + ::System::Type::typeid, ::System::String::typeid }; + ilGen->Emit(Emit::OpCodes::Call, + ::uno::PolymorphicType::typeid->GetMethod(gcnew System::String("GetType"), + arTypeParam_GetType)); + + //Stack is: localAny, PolymorphicType + //Call Any::Any(Type, Object) + //Prepare the second parameter for the any ctor + ilGen->Emit(Emit::OpCodes::Ldarg, i + 1); + // if the parameter is a value type then we need to box it, because + // the Any ctor takes an Object + if (arTypeParameters[i+1]->IsValueType) + ilGen->Emit(Emit::OpCodes::Box, arTypeParameters[i+1]); + ilGen->Emit(Emit::OpCodes::Call, ctorAny); + } + else if (arTypeParameters[i+1] == typeAny) + { + //Create the call new Any(param.Type,param,Value) + //Stack must be Any,Type,Value + //First load the Any which is to be constructed + ilGen->Emit(Emit::OpCodes::Ldloca, arLocalAny[i]); + //Load the Type, which is obtained by calling param.Type + ilGen->Emit(Emit::OpCodes::Ldarga, i + 1); + ilGen->Emit(Emit::OpCodes::Call, methodAnyGetType); + //Load the Value, which is obtained by calling param.Value + ilGen->Emit(Emit::OpCodes::Ldarga, i + 1); + ilGen->Emit(Emit::OpCodes::Call, methodAnyGetValue); + //Call the Any ctor. + ilGen->Emit(Emit::OpCodes::Call, ctorAny); + } + else + { + ilGen->Emit(Emit::OpCodes::Ldloca, arLocalAny[i]); + ilGen->Emit(Emit::OpCodes::Ldtoken, arTypeParameters[i+1]); + + array< ::System::Type^>^ arTypeParams = {::System::RuntimeTypeHandle::typeid}; + ilGen->Emit(Emit::OpCodes::Call, + ::System::Type::typeid->GetMethod( + "GetTypeFromHandle", arTypeParams)); + ilGen->Emit(Emit::OpCodes::Ldarg, i + 1); + // if the parameter is a value type then we need to box it, because + // the Any ctor takes an Object + if (arTypeParameters[i+1]->IsValueType) + ilGen->Emit(Emit::OpCodes::Box, arTypeParameters[i+1]); + ilGen->Emit(Emit::OpCodes::Call, ctorAny); + } + } + + //Create the Any[] that is passed to the + //createInstanceWithContext[AndArguments] function + ilGen->Emit(Emit::OpCodes::Ldc_I4, arLocalAny->Length); + ilGen->Emit(Emit::OpCodes::Newarr, typeAny); + ilGen->Emit(Emit::OpCodes::Stloc, local_anyParams); + + //Assign all anys created from the parameters + //array to the Any[] + for (int i = 0; i < arLocalAny->Length; i++) + { + ilGen->Emit(Emit::OpCodes::Ldloc, local_anyParams); + ilGen->Emit(Emit::OpCodes::Ldc_I4, i); + ilGen->Emit(Emit::OpCodes::Ldelema, typeAny); + ilGen->Emit(Emit::OpCodes::Ldloc, arLocalAny[i]); + ilGen->Emit(Emit::OpCodes::Stobj, typeAny); + } + // call createInstanceWithArgumentsAndContext + ilGen->Emit(Emit::OpCodes::Ldloc, local_factory); + ilGen->Emit(Emit::OpCodes::Ldstr, ustring_to_String(xServiceType->getName())); + ilGen->Emit(Emit::OpCodes::Ldloc, local_anyParams); + ilGen->Emit(Emit::OpCodes::Ldarg_0); + ::System::Reflection::MethodInfo ^ methodCreate = + local_factory->LocalType->GetMethod("createInstanceWithArgumentsAndContext"); + ilGen->Emit(Emit::OpCodes::Callvirt, methodCreate); + } + //cast the object returned by the functions createInstanceWithContext or + //createInstanceWithArgumentsAndContext to the interface type + ilGen->Emit(Emit::OpCodes::Castclass, retType); + ilGen->Emit(Emit::OpCodes::Stloc, local_return_val); + + //catch exceptions thrown by createInstanceWithArgumentsAndContext and createInstanceWithContext + if (arExceptionTypes->Contains(type_uno_exception) == false) + { + // catch (unoidl.com.sun.star.uno.RuntimeException) {throw;} + ilGen->BeginCatchBlock(get_type("unoidl.com.sun.star.uno.RuntimeException", true)); + ilGen->Emit(Emit::OpCodes::Pop); + ilGen->Emit(Emit::OpCodes::Rethrow); + + //catch and rethrow all other defined Exceptions + for (int i = 0; i < arExceptionTypes->Count; i++) + { + ::System::Type ^ excType = safe_cast< ::System::Type^ >( + arExceptionTypes[i]); + if (excType->IsInstanceOfType( + get_type("unoidl.com.sun.star.uno.RuntimeException", true))) + {// we have a catch for RuntimeException already defined + continue; + } + + //catch Exception and rethrow + ilGen->BeginCatchBlock(excType); + ilGen->Emit(Emit::OpCodes::Pop); + ilGen->Emit(Emit::OpCodes::Rethrow); + } + //catch (unoidl.com.sun.star.uno.Exception) {throw DeploymentException...} + ilGen->BeginCatchBlock(type_uno_exception); + + //Define the local variable that keeps the exception + Emit::LocalBuilder ^ local_exception = ilGen->DeclareLocal( + type_uno_exception); + + //Store the exception + ilGen->Emit(Emit::OpCodes::Stloc, local_exception); + + //prepare the construction of the exception + strbuilder = gcnew ::System::Text::StringBuilder(256); + strbuilder->Append("The context (com.sun.star.uno.XComponentContext) failed to supply the service "); + strbuilder->Append(to_cts_name(xServiceType->getName())); + strbuilder->Append(": "); + + ilGen->Emit(Emit::OpCodes::Ldstr, strbuilder->ToString()); + + //add to the string the Exception.Message + ilGen->Emit(Emit::OpCodes::Ldloc, local_exception); + ilGen->Emit(Emit::OpCodes::Callvirt, + type_uno_exception->GetProperty("Message")->GetGetMethod()); + array< ::System::Type^>^ arConcatParams = {System::String::typeid, + System::String::typeid}; + ilGen->Emit(Emit::OpCodes::Call, + System::String::typeid->GetMethod("Concat", arConcatParams)); + //load context argument + ilGen->Emit(Emit::OpCodes::Ldarg_0); + ilGen->Emit(Emit::OpCodes::Newobj, ctorDeploymentException); + ilGen->Emit(Emit::OpCodes::Throw);//Exception(typeDeploymentExc); + + ilGen->EndExceptionBlock(); + } + + + //Check if the service instance was created and threw an exception if not + Emit::Label label_service_created = ilGen->DefineLabel(); + ilGen->Emit(Emit::OpCodes::Ldloc, local_return_val); + ilGen->Emit(Emit::OpCodes::Brtrue_S, label_service_created); + + strbuilder = gcnew ::System::Text::StringBuilder(256); + strbuilder->Append("The context (com.sun.star.uno.XComponentContext) failed to supply the service "); + strbuilder->Append(to_cts_name(xServiceType->getName())); + strbuilder->Append("."); + ilGen->Emit(Emit::OpCodes::Ldstr, strbuilder->ToString()); + ilGen->Emit(Emit::OpCodes::Ldarg_0); + ilGen->Emit(Emit::OpCodes::Newobj, ctorDeploymentException); + ilGen->Emit(Emit::OpCodes::Throw);//Exception(typeDeploymentExc); + + ilGen->MarkLabel(label_service_created); + ilGen->Emit(Emit::OpCodes::Ldloc, local_return_val); + ilGen->Emit(Emit::OpCodes::Ret); + + } + // remove from incomplete types map + ::System::String ^ cts_name = type_builder->FullName; + m_incomplete_services->Remove( cts_name ); + xServiceType->release(); + if (g_bVerbose) + { + ::System::Console::WriteLine( + "> emitting service type {0}", cts_name ); + } + return type_builder->CreateType(); +} + + +Emit::CustomAttributeBuilder^ TypeEmitter::get_service_exception_attribute( + const Reference<reflection::XServiceConstructorDescription> & ctorDes ) +{ + return get_exception_attribute(ctorDes->getExceptions()); +} + +Emit::CustomAttributeBuilder^ TypeEmitter::get_iface_method_exception_attribute( + const Reference< reflection::XInterfaceMethodTypeDescription >& xMethod ) +{ + + const Sequence<Reference<reflection::XTypeDescription> > seqTD = xMethod->getExceptions(); + int len = seqTD.getLength(); + Sequence<Reference<reflection::XCompoundTypeDescription> > seqCTD(len); + Reference<reflection::XCompoundTypeDescription> * arCTD = seqCTD.getArray(); + for (int i = 0; i < len; i++) + arCTD[i] = Reference<reflection::XCompoundTypeDescription>(seqTD[i], UNO_QUERY_THROW); + return get_exception_attribute(seqCTD); +} + +Emit::CustomAttributeBuilder^ TypeEmitter::get_exception_attribute( + + const Sequence<Reference< reflection::XCompoundTypeDescription > >& seq_exceptionsTd ) +{ + Emit::CustomAttributeBuilder ^ attr_builder = nullptr; + + Reference< reflection::XCompoundTypeDescription > const * exceptions = + seq_exceptionsTd.getConstArray(); + + array< ::System::Type^>^ arTypesCtor = {::System::Type::GetType("System.Type[]")}; + ConstructorInfo ^ ctor_ExceptionAttribute = + ::uno::ExceptionAttribute::typeid->GetConstructor(arTypesCtor); + + sal_Int32 exc_length = seq_exceptionsTd.getLength(); + if (exc_length != 0) // opt + { + array< ::System::Type^>^ exception_types = + gcnew array< ::System::Type^> ( exc_length ); + for ( sal_Int32 exc_pos = 0; exc_pos < exc_length; ++exc_pos ) + { + Reference< reflection::XCompoundTypeDescription > const & xExc = + exceptions[ exc_pos ]; + exception_types[ exc_pos ] = get_type( xExc ); + } + array< ::System::Object^>^ args = {exception_types}; + attr_builder = gcnew Emit::CustomAttributeBuilder( + ctor_ExceptionAttribute, args ); + } + return attr_builder; +} + + +::System::Type ^ TypeEmitter::complete_singleton_type(singleton_entry ^ entry) +{ + Emit::TypeBuilder ^ type_builder = entry->m_type_builder; + reflection::XSingletonTypeDescription2 * xSingletonType = entry->m_xType; + ::System::String^ sSingletonName = to_cts_name(xSingletonType->getName()); + + //Create the private default constructor + Emit::ConstructorBuilder^ ctor_builder = + type_builder->DefineConstructor( + static_cast<MethodAttributes>(MethodAttributes::Private | + MethodAttributes::HideBySig | + MethodAttributes::SpecialName | + MethodAttributes::RTSpecialName), + CallingConventions::Standard, nullptr); + + Emit::ILGenerator^ ilGen = ctor_builder->GetILGenerator(); + ilGen->Emit( Emit::OpCodes::Ldarg_0 ); // push this + ilGen->Emit( + Emit::OpCodes::Call, + type_builder->BaseType->GetConstructor(gcnew array< ::System::Type^>(0))); + ilGen->Emit( Emit::OpCodes::Ret ); + + + //obtain the interface which makes up this service, it is the return + //type of the constructor functions + Reference<reflection::XInterfaceTypeDescription2> xIfaceType( + xSingletonType->getInterface(), UNO_QUERY); + if (xIfaceType.is () == sal_False) + xIfaceType = resolveInterfaceTypedef(xSingletonType->getInterface()); + System::Type ^ retType = get_type(xIfaceType); + + //define method + array< ::System::Type^>^ arTypeParameters = {get_type("unoidl.com.sun.star.uno.XComponentContext", true)}; + Emit::MethodBuilder^ method_builder = type_builder->DefineMethod( + gcnew System::String("get"), + static_cast<MethodAttributes>(MethodAttributes::Public | MethodAttributes::HideBySig | + MethodAttributes::Static), + retType, + arTypeParameters); + + +// method_builder->SetCustomAttribute(get_service_ctor_method_attribute(ctorDes)); + + //The first parameter is the XComponentContext, which cannot be obtained + //from reflection. + //The context is not part of the idl description + method_builder->DefineParameter(1, ParameterAttributes::In, "the_context"); + + + ilGen = method_builder->GetILGenerator(); + //Define locals --------------------------------- + // Any, returned by XComponentContext.getValueByName + Emit::LocalBuilder^ local_any = + ilGen->DeclareLocal(::uno::Any::typeid); + + //Call XContext::getValueByName + ilGen->Emit(Emit::OpCodes::Ldarg_0); + // build the singleton name : /singleton/unoidl.com.sun.star.XXX + ::System::Text::StringBuilder^ sBuilder = + gcnew ::System::Text::StringBuilder("/singletons/"); + sBuilder->Append(sSingletonName); + ilGen->Emit(Emit::OpCodes::Ldstr, sBuilder->ToString()); + + ::System::Reflection::MethodInfo ^ methodGetValueByName = + get_type("unoidl.com.sun.star.uno.XComponentContext", true)->GetMethod("getValueByName"); + ilGen->Emit(Emit::OpCodes::Callvirt, methodGetValueByName); + ilGen->Emit(Emit::OpCodes::Stloc_0); + + //Contains the returned Any a value? + ilGen->Emit(Emit::OpCodes::Ldloca_S, local_any); + ::System::Reflection::MethodInfo ^ methodHasValue = + ::uno::Any::typeid->GetMethod("hasValue"); + ilGen->Emit(Emit::OpCodes::Call, methodHasValue); + + //If not, then throw a DeploymentException + Emit::Label label_singleton_exists = ilGen->DefineLabel(); + ilGen->Emit(Emit::OpCodes::Brtrue_S, label_singleton_exists); + sBuilder = gcnew ::System::Text::StringBuilder( + "Component context fails to supply singleton "); + sBuilder->Append(sSingletonName); + sBuilder->Append(" of type "); + sBuilder->Append(retType->FullName); + sBuilder->Append("."); + ilGen->Emit(Emit::OpCodes::Ldstr, sBuilder->ToString()); + ilGen->Emit(Emit::OpCodes::Ldarg_0); + array< ::System::Type^>^ arTypesCtorDeploymentException = { + ::System::String::typeid, ::System::Object::typeid}; + ilGen->Emit(Emit::OpCodes::Newobj, + get_type("unoidl.com.sun.star.uno.DeploymentException",true) + ->GetConstructor(arTypesCtorDeploymentException)); + ilGen->Emit(Emit::OpCodes::Throw); + ilGen->MarkLabel(label_singleton_exists); + + //Cast the singleton contained in the Any to the expected interface and return it. + ilGen->Emit(Emit::OpCodes::Ldloca_S, local_any); + ilGen->Emit(Emit::OpCodes::Call, ::uno::Any::typeid->GetProperty("Value")->GetGetMethod()); + ilGen->Emit(Emit::OpCodes::Castclass, retType); + ilGen->Emit(Emit::OpCodes::Ret); + + // remove from incomplete types map + ::System::String ^ cts_name = type_builder->FullName; + m_incomplete_singletons->Remove( cts_name ); + xSingletonType->release(); + if (g_bVerbose) + { + ::System::Console::WriteLine( + "> emitting singleton type {0}", cts_name ); + } + return type_builder->CreateType(); +} + + +::System::Type ^ TypeEmitter::get_type( + Reference< reflection::XTypeDescription > const & xType ) +{ + switch (xType->getTypeClass()) + { + case TypeClass_VOID: + return ::System::Void::typeid; + case TypeClass_CHAR: + return ::System::Char::typeid; + case TypeClass_BOOLEAN: + return ::System::Boolean::typeid; + case TypeClass_BYTE: + return ::System::Byte::typeid; + case TypeClass_SHORT: + return ::System::Int16::typeid; + case TypeClass_UNSIGNED_SHORT: + return ::System::UInt16::typeid; + case TypeClass_LONG: + return ::System::Int32::typeid; + case TypeClass_UNSIGNED_LONG: + return ::System::UInt32::typeid; + case TypeClass_HYPER: + return ::System::Int64::typeid; + case TypeClass_UNSIGNED_HYPER: + return ::System::UInt64::typeid; + case TypeClass_FLOAT: + return ::System::Single::typeid; + case TypeClass_DOUBLE: + return ::System::Double::typeid; + case TypeClass_STRING: + return ::System::String::typeid; + case TypeClass_TYPE: + return ::System::Type::typeid; + case TypeClass_ANY: + return ::uno::Any::typeid; + case TypeClass_ENUM: + return get_type( Reference< reflection::XEnumTypeDescription >( + xType, UNO_QUERY_THROW ) ); + case TypeClass_TYPEDEF: + // unwind typedefs + return get_type( + Reference< reflection::XIndirectTypeDescription >( + xType, UNO_QUERY_THROW )->getReferencedType() ); + case TypeClass_STRUCT: + case TypeClass_EXCEPTION: + return get_type( + Reference< reflection::XCompoundTypeDescription >( + xType, UNO_QUERY_THROW ) ); + case TypeClass_SEQUENCE: + { + ::System::Type ^ element_type = get_type( + Reference< reflection::XIndirectTypeDescription >( + xType, UNO_QUERY_THROW )->getReferencedType() ); + ::System::Type ^ retType = get_type( + ::System::String::Concat( + element_type->FullName, "[]" ), true ); + + ::uno::PolymorphicType ^ pt = dynamic_cast< ::uno::PolymorphicType ^ >(element_type); + if (pt) + { + ::System::String ^ sName = ::System::String::Concat(pt->PolymorphicName, "[]"); + retType = ::uno::PolymorphicType::GetType(retType, sName); + } + return retType; + } + case TypeClass_INTERFACE: + return get_type( + Reference< reflection::XInterfaceTypeDescription2 >( + xType, UNO_QUERY_THROW ) ); + case TypeClass_CONSTANT: + return get_type( + Reference< reflection::XConstantTypeDescription >( + xType, UNO_QUERY_THROW ) ); + case TypeClass_CONSTANTS: + return get_type( + Reference< reflection::XConstantsTypeDescription >( + xType, UNO_QUERY_THROW ) ); + case TypeClass_SERVICE: + return get_type( + Reference< reflection::XServiceTypeDescription2 >( + xType, UNO_QUERY_THROW) ); + case TypeClass_SINGLETON: + return get_type( + Reference< reflection::XSingletonTypeDescription2 >( + xType, UNO_QUERY_THROW) ); + case TypeClass_MODULE: + // ignore these + return nullptr; + default: + throw RuntimeException( + "unexpected type " + xType->getName() ); + } +} + + +::System::Type ^ TypeEmitter::get_complete_struct( ::System::String ^ sName) +{ + struct_entry ^ pStruct = safe_cast< struct_entry ^>( + m_incomplete_structs[sName]); + if (pStruct) + { + complete_struct_type(pStruct); + } + //get_type will asked the module builder for the type or otherwise all known assemblies. + return get_type(sName, true); +} +TypeEmitter::~TypeEmitter() +{ + while (true) + { + ::System::Collections::IDictionaryEnumerator ^ enumerator = + m_incomplete_ifaces->GetEnumerator(); + if (! enumerator->MoveNext()) + break; + complete_iface_type( + safe_cast< iface_entry ^ >( enumerator->Value ) ); + } + + while (true) + { + ::System::Collections::IDictionaryEnumerator ^ enumerator = + m_incomplete_structs->GetEnumerator(); + if (! enumerator->MoveNext()) + break; + complete_struct_type( + safe_cast< struct_entry ^ >( enumerator->Value ) ); + } + + + while (true) + { + ::System::Collections::IDictionaryEnumerator ^ enumerator = + m_incomplete_services->GetEnumerator(); + if (! enumerator->MoveNext()) + break; + complete_service_type( + safe_cast< service_entry ^ >( enumerator->Value ) ); + } + + while (true) + { + ::System::Collections::IDictionaryEnumerator ^ enumerator = + m_incomplete_singletons->GetEnumerator(); + if (! enumerator->MoveNext()) + break; + complete_singleton_type( + safe_cast< singleton_entry ^ >( enumerator->Value ) ); + } +} + +TypeEmitter::TypeEmitter( + ::System::Reflection::Emit::ModuleBuilder ^ module_builder, + array< ::System::Reflection::Assembly^>^ extra_assemblies ) + : m_module_builder( module_builder ), + m_extra_assemblies( extra_assemblies ), + m_method_info_Type_GetTypeFromHandle( nullptr ), + m_type_Exception( nullptr ), + m_type_RuntimeException( nullptr ), + m_incomplete_ifaces( gcnew ::System::Collections::Hashtable() ), + m_incomplete_structs( gcnew ::System::Collections::Hashtable() ), + m_incomplete_services(gcnew ::System::Collections::Hashtable() ), + m_incomplete_singletons(gcnew ::System::Collections::Hashtable() ), + m_generated_structs( gcnew ::System::Collections::Hashtable() ) +{ + array< ::System::Type^>^ param_types = gcnew array< ::System::Type^> ( 1 ); + param_types[ 0 ] = ::System::RuntimeTypeHandle::typeid; + m_method_info_Type_GetTypeFromHandle = + ::System::Type::typeid + ->GetMethod( "GetTypeFromHandle", param_types ); +} + +::System::Collections::ArrayList ^ TypeEmitter::get_service_ctor_method_exceptions_reduced( + const Sequence<Reference<reflection::XCompoundTypeDescription> > & seqExceptionsTd) +{ + if (seqExceptionsTd.getLength() == 0) + return gcnew ::System::Collections::ArrayList(); + + ::System::Collections::ArrayList ^ arTypes = gcnew ::System::Collections::ArrayList(); + for (int i = 0; i < seqExceptionsTd.getLength(); i++) + arTypes->Add(get_type(to_cts_name(seqExceptionsTd[i]->getName()), true)); + + int start = 0; + while (true) + { + bool bRemove = false; + for (int i = start; i < arTypes->Count; i++) + { + ::System::Type ^ t = safe_cast< ::System::Type^ >(arTypes[i]); + for (int j = 0; j < arTypes->Count; j++) + { + if (t->IsSubclassOf(safe_cast< ::System::Type^ >(arTypes[j]))) + { + arTypes->RemoveAt(i); + bRemove = true; + break; + } + } + if (bRemove) + break; + start++; + } + + if (bRemove == false) + break; + } + return arTypes; +} + + +css::uno::Reference< css::reflection::XInterfaceTypeDescription2 > +resolveInterfaceTypedef( + const css::uno::Reference<css::reflection::XTypeDescription>& type) +{ + Reference<reflection::XInterfaceTypeDescription2> + xIfaceTd(type, UNO_QUERY); + + if (xIfaceTd.is()) + return xIfaceTd; + + Reference<reflection::XIndirectTypeDescription> xIndTd( + type, UNO_QUERY_THROW); + + return resolveInterfaceTypedef(xIndTd->getReferencedType()); +} + + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cli_ure/source/climaker/climaker_share.h b/cli_ure/source/climaker/climaker_share.h new file mode 100644 index 000000000..ed7ab256a --- /dev/null +++ b/cli_ure/source/climaker/climaker_share.h @@ -0,0 +1,258 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#using <cli_basetypes.dll> + +#include <vcclr.h> + +#include "osl/diagnose.h" +#include "com/sun/star/reflection/XConstantTypeDescription.hpp" +#include "com/sun/star/reflection/XConstantsTypeDescription.hpp" +#include "com/sun/star/reflection/XEnumTypeDescription.hpp" +#include "com/sun/star/reflection/XInterfaceTypeDescription2.hpp" +#include "com/sun/star/reflection/XCompoundTypeDescription.hpp" +#include "com/sun/star/reflection/XServiceTypeDescription2.hpp" +#include "com/sun/star/reflection/XSingletonTypeDescription2.hpp" +#include "com/sun/star/reflection/XInterfaceMethodTypeDescription.hpp" + + +namespace climaker +{ + + +extern bool g_bVerbose; + +ref struct Constants +{ + static ::System::String ^ sUnoVoid = "void"; + static ::System::String ^ sUnoType = "type"; + static ::System::String ^ sUnoAny = "any"; + static ::System::String ^ sUnoBool = "boolean"; + static ::System::String ^ sUnoByte = "byte"; + static ::System::String ^ sUnoChar = "char"; + static ::System::String ^ sUnoShort = "short"; + static ::System::String ^ sUnoUShort = "unsigned short"; + static ::System::String ^ sUnoLong = "long"; + static ::System::String ^ sUnoULong = "unsigned long"; + static ::System::String ^ sUnoHyper = "hyper"; + static ::System::String ^ sUnoUHyper = "unsigned hyper"; + static ::System::String ^ sUnoString = "string"; + static ::System::String ^ sUnoFloat = "float"; + static ::System::String ^ sUnoDouble = "double"; + static ::System::String ^ sUnoXInterface = "com.sun.star.uno.XInterface"; + static ::System::String ^ sBrackets = "[]"; + + static System::String^ sObject = "System.Object"; + static System::String^ sType = "System.Type"; + static System::String^ sUnoidl = "unoidl."; + static System::String^ sVoid = "System.Void"; + static System::String^ sAny = "uno.Any"; + static System::String^ sBoolean = "System.Boolean"; + static System::String^ sChar = "System.Char"; + static System::String^ sByte = "System.Byte"; + static System::String^ sInt16 = "System.Int16"; + static System::String^ sUInt16 = "System.UInt16"; + static System::String^ sInt32 = "System.Int32"; + static System::String^ sUInt32 = "System.UInt32"; + static System::String^ sInt64 = "System.Int64"; + static System::String^ sUInt64 = "System.UInt64"; + static System::String^ sString = "System.String"; + static System::String^ sSingle = "System.Single"; + static System::String^ sDouble = "System.Double"; + static System::String^ sComma = gcnew System::String(","); + +}; + + +inline ::System::String ^ ustring_to_String( OUString const & ustr ) +{ + return gcnew ::System::String( + reinterpret_cast<wchar_t const *>(ustr.getStr()), 0, ustr.getLength()); +} + + +inline OUString String_to_ustring( ::System::String ^ str ) +{ + OSL_ASSERT( sizeof (wchar_t) == sizeof (sal_Unicode) ); + pin_ptr<const wchar_t> chars = PtrToStringChars( str ); + return OUString(reinterpret_cast<sal_Unicode const *>(chars), str->Length); +} + +/* If the argument type is a typedef for an interface then the interface + type description is returned, otherwise an exception is thrown. +*/ +css::uno::Reference< css::reflection::XInterfaceTypeDescription2 > +resolveInterfaceTypedef(const css::uno::Reference<css::reflection::XTypeDescription>& type); + +static ::System::Reflection::MethodAttributes c_ctor_method_attr = +(::System::Reflection::MethodAttributes) + (::System::Reflection::MethodAttributes::Public | + ::System::Reflection::MethodAttributes::HideBySig | + ::System::Reflection::MethodAttributes::SpecialName | + ::System::Reflection::MethodAttributes::RTSpecialName + /* | xxx todo: ??? compiler does not know Instance ??? + ::System::Reflection::MethodAttributes::Instance*/); + + +ref class TypeEmitter : public ::System::IDisposable +{ + ::System::Reflection::Emit::ModuleBuilder ^ m_module_builder; + array< ::System::Reflection::Assembly^>^ m_extra_assemblies; + + ::System::Reflection::MethodInfo ^ m_method_info_Type_GetTypeFromHandle; + + ::System::Type ^ m_type_Exception; + ::System::Type ^ get_type_Exception(); + ::System::Type ^ m_type_RuntimeException; + ::System::Type ^ get_type_RuntimeException(); + + ::System::Reflection::Emit::CustomAttributeBuilder^ get_service_exception_attribute( + const css::uno::Reference<css::reflection::XServiceConstructorDescription> & ctorDesc); + ::System::Reflection::Emit::CustomAttributeBuilder^ get_iface_method_exception_attribute( + const css::uno::Reference< css::reflection::XInterfaceMethodTypeDescription >& xMethod ); + ::System::Reflection::Emit::CustomAttributeBuilder^ get_exception_attribute( + const css::uno::Sequence<css::uno::Reference< + css::reflection::XCompoundTypeDescription > >& seq_exceptionsTd ); +/* Creates ::System::Type object for UNO exceptions. The UNO exceptions are + obtained by + css::reflection::XServiceConstructorDescription::getExceptions + In a first step the respective CLI types are created. Then it is examined + if a Type represents a super class of another class. If so the Type of the + derived class is discarded. For example there are a uno RuntimeException and + a DeploymentException which inherits RuntimeException. Then only the cli Type + of the RuntimeException is returned. + The purpose of this function is to provide exceptions for which catch blocks + are generated in the service constructor code. + + It is always an instance of an ArrayList returned, even if the sequence argument + does not contain elements. + */ + ::System::Collections::ArrayList ^ get_service_ctor_method_exceptions_reduced( + const css::uno::Sequence< + css::uno::Reference<css::reflection::XCompoundTypeDescription> > & seqExceptionsTd); + + + ref class iface_entry + { + public: + css::reflection::XInterfaceTypeDescription2 * m_xType; + ::System::Reflection::Emit::TypeBuilder ^ m_type_builder; + }; + ::System::Collections::Hashtable ^ m_incomplete_ifaces; + ::System::Type ^ complete_iface_type( iface_entry ^ entry ); + + ref class struct_entry + { + public: + css::reflection::XCompoundTypeDescription * m_xType; + ::System::Reflection::Emit::TypeBuilder ^ m_type_builder; + ::System::Type ^ m_base_type; + + array< ::System::String^>^ m_member_names; + array< ::System::Type^>^ m_param_types; + ::System::Reflection::ConstructorInfo ^ m_default_ctor; + ::System::Reflection::ConstructorInfo ^ m_ctor; + }; + ::System::Collections::Hashtable ^ m_incomplete_structs; + ::System::Type ^ complete_struct_type( struct_entry ^ entry ); + + /* returns the type for the name. If it is a struct then it may + complete the struct if not already done. This also refers to its + base types. + + @param sName + the full name of the type. + @return the type object for sName. Not necessarily a struct. + */ + ::System::Type ^ get_complete_struct( ::System::String ^ sName); + + ref class service_entry + { + public: + ::System::Reflection::Emit::TypeBuilder ^ m_type_builder; + css::reflection::XServiceTypeDescription2 * m_xType; + }; + ::System::Collections::Hashtable ^ m_incomplete_services; + ::System::Type ^ complete_service_type(service_entry ^ entry); + + ref class singleton_entry + { + public: + ::System::Reflection::Emit::TypeBuilder ^ m_type_builder; + css::reflection::XSingletonTypeDescription2 * m_xType; + }; + + + ::System::Collections::Hashtable ^ m_incomplete_singletons; + ::System::Type ^ complete_singleton_type(singleton_entry ^ entry); + + + ::System::Collections::Hashtable ^ m_generated_structs; + + ::System::Type ^ get_type( + ::System::String ^ cli_name, bool throw_exc ); + ::System::Type ^ get_type( + css::uno::Reference< + css::reflection::XConstantTypeDescription > const & xType ); + ::System::Type ^ get_type( + css::uno::Reference< + css::reflection::XConstantsTypeDescription > const & xType ); + ::System::Type ^ get_type( + css::uno::Reference< + css::reflection::XEnumTypeDescription > const & xType ); + /* returns the type for a struct or exception. In case of a polymorphic struct it may + return a ::uno::PolymorphicType (cli_basetypes.dll) only if the struct is already + complete. + */ + ::System::Type ^ get_type( + css::uno::Reference< + css::reflection::XCompoundTypeDescription > const & xType ); + ::System::Type ^ get_type( + css::uno::Reference< + css::reflection::XInterfaceTypeDescription2 > const & xType ); + ::System::Type ^ get_type( + css::uno::Reference< + css::reflection::XSingletonTypeDescription2 > const & xType ); + + /* + May return NULL if the service description is an obsolete. See + description of + com.sun.star.reflection.XServiceTypeDescription2.isSingleInterfaceBased + */ + ::System::Type ^ get_type( + css::uno::Reference< + css::reflection::XServiceTypeDescription2 > const & xType ); +public: + TypeEmitter( + ::System::Reflection::Emit::ModuleBuilder ^ module_builder, + array< ::System::Reflection::Assembly^>^ assemblies ); + // must be called to finish up uncompleted types + ~TypeEmitter(); + + ::System::Reflection::Assembly ^ type_resolve( + ::System::Object ^ sender, ::System::ResolveEventArgs ^ args ); + + ::System::Type ^ get_type( + css::uno::Reference< + css::reflection::XTypeDescription > const & xType ); +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cli_ure/source/cliuno.snk b/cli_ure/source/cliuno.snk Binary files differnew file mode 100644 index 000000000..938f16939 --- /dev/null +++ b/cli_ure/source/cliuno.snk diff --git a/cli_ure/source/native/assembly.cxx b/cli_ure/source/native/assembly.cxx new file mode 100644 index 000000000..75e33e8c7 --- /dev/null +++ b/cli_ure/source/native/assembly.cxx @@ -0,0 +1,26 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +[assembly:System::Reflection::AssemblyProduct("CLI-UNO Language Binding")]; +[assembly:System::Reflection::AssemblyDescription("CLI-UNO Helper Library")]; +[assembly:System::Reflection::AssemblyDelaySign(true)]; +[assembly:System::Reflection::AssemblyCompany("OpenOffice.org")]; +[assembly:System::Reflection::AssemblyVersion("@CLI_CPPUHELPER_NEW_VERSION@")]; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cli_ure/source/native/cli_cppuhelper_config b/cli_ure/source/native/cli_cppuhelper_config new file mode 100644 index 000000000..627a3a564 --- /dev/null +++ b/cli_ure/source/native/cli_cppuhelper_config @@ -0,0 +1,11 @@ +<?xml version="1.0"?> +<configuration> + <runtime> + <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> + <dependentAssembly> + <assemblyIdentity name="cli_cppuhelper" publicKeyToken="ce2cb7e279207b9e"/> + <bindingRedirect oldVersion="CLI_CPPUHELPER_OLD_VERSION" newVersion="CLI_CPPUHELPER_NEW_VERSION" /> + </dependentAssembly> + </assemblyBinding> + </runtime> +</configuration>
\ No newline at end of file diff --git a/cli_ure/source/native/native_bootstrap.cxx b/cli_ure/source/native/native_bootstrap.cxx new file mode 100644 index 000000000..f26a634c2 --- /dev/null +++ b/cli_ure/source/native/native_bootstrap.cxx @@ -0,0 +1,297 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "native_share.h" + +#include "rtl/bootstrap.hxx" +#include "com/sun/star/uno/XComponentContext.hpp" +#include "cppuhelper/bootstrap.hxx" +#include <memory> +#include <stdio.h> +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <delayimp.h> + +#define INSTALL_PATH L"Software\\LibreOffice\\UNO\\InstallPath" +#define UNO_PATH L"UNO_PATH" + +namespace { + +/* Gets the installation path from the Windows Registry for the specified + registry key. + + @param hroot open handle to predefined root registry key + @param subKeyName name of the subkey to open + + @return the installation path or nullptr, if no installation was found or + if an error occurred +*/ +WCHAR* getPathFromRegistryKey( HKEY hroot, LPCWSTR subKeyName ) +{ + // open the specified registry key + HKEY hkey; + if ( RegOpenKeyExW( hroot, subKeyName, 0, KEY_READ, &hkey ) != ERROR_SUCCESS ) + return nullptr; + + struct CloseKeyGuard { + HKEY m_hkey; + ~CloseKeyGuard() { RegCloseKey( m_hkey ); } + } aCloseKeyGuard{hkey}; + + // find the type and size of the default value + DWORD type; + DWORD size; + if ( RegQueryValueExW( hkey, nullptr, nullptr, &type, nullptr, &size ) != ERROR_SUCCESS ) + return nullptr; + + // get memory to hold the default value + std::unique_ptr<WCHAR[]> data(new WCHAR[size]); + + // read the default value + if ( RegQueryValueExW( hkey, nullptr, nullptr, &type, reinterpret_cast<LPBYTE>(data.get()), &size ) != ERROR_SUCCESS ) + return nullptr; + + return data.release(); +} + +/* Returns the path to the program folder of the brand layer, + for example C:/Program Files/LibreOffice/program + This path is either obtained from the environment variable UNO_PATH + or the registry item "Software\\LibreOffice\\UNO\\InstallPath" + either in HKEY_CURRENT_USER or HKEY_LOCAL_MACHINE + The return value must be freed with delete[] +*/ +WCHAR* getInstallPath() +{ + std::unique_ptr<WCHAR[]> szInstallPath; + + DWORD cChars = GetEnvironmentVariableW(UNO_PATH, nullptr, 0); + if (cChars > 0) + { + szInstallPath.reset(new WCHAR[cChars]); + cChars = GetEnvironmentVariableW(UNO_PATH, szInstallPath.get(), cChars); + // If PATH is not set then it is no error + if (cChars == 0) + return nullptr; + } + + if (! szInstallPath) + { + szInstallPath.reset(getPathFromRegistryKey( HKEY_CURRENT_USER, INSTALL_PATH )); + if (! szInstallPath) + { + // read the key's default value from HKEY_LOCAL_MACHINE + szInstallPath.reset(getPathFromRegistryKey( HKEY_LOCAL_MACHINE, INSTALL_PATH )); + } + } + return szInstallPath.release(); +} + +/* We extend the path to contain the install folder, + so that components can use osl_loadModule with arguments, such as + "reg3.dll". That is, the arguments are only the library names. +*/ +void extendPath(LPCWSTR szPath) +{ + if (!szPath) + return; + + std::unique_ptr<WCHAR[]> sEnvPath; + DWORD cChars = GetEnvironmentVariableW(L"PATH", nullptr, 0); + if (cChars > 0) + { + sEnvPath.reset(new WCHAR[cChars]); + cChars = GetEnvironmentVariableW(L"PATH", sEnvPath.get(), cChars); + // If PATH is not set then it is no error + if (cChars == 0 && GetLastError() != ERROR_ENVVAR_NOT_FOUND) + return; + } + // Prepare the new PATH. Add the directory at the front. + // Note also adding ';' + std::unique_ptr<WCHAR[]> sNewPath(new WCHAR[lstrlenW(sEnvPath.get()) + lstrlenW(szPath) + 2]); + lstrcpyW(sNewPath.get(), szPath); + if (lstrlenW(sEnvPath.get())) + { + lstrcatW(sNewPath.get(), L";"); + lstrcatW(sNewPath.get(), sEnvPath.get()); + } + SetEnvironmentVariableW(L"PATH", sNewPath.get()); +} + +HMODULE loadFromPath(LPCSTR sLibName) +{ + if (!sLibName) + return nullptr; + + // Convert the ansi file name to wchar_t* + int size = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, sLibName, -1, nullptr, 0); + if (size == 0) + return nullptr; + std::unique_ptr<WCHAR[]> wsLibName(new WCHAR[size]); + if (!MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, sLibName, -1, wsLibName.get(), size)) + return nullptr; + + std::unique_ptr<WCHAR[]> szPath(getInstallPath()); + if (!szPath) + return nullptr; + + extendPath(szPath.get()); + + std::unique_ptr<WCHAR[]> szFullPath(new WCHAR[lstrlenW(wsLibName.get()) + lstrlenW(szPath.get()) + 2]); + lstrcpyW(szFullPath.get(), szPath.get()); + lstrcatW(szFullPath.get(), L"\\"); + lstrcatW(szFullPath.get(), wsLibName.get()); + HMODULE handle = LoadLibraryW(szFullPath.get()); + return handle; +} + +/* Hook for delayed loading of libraries which this library is linked with. + This is a failure hook. That is, it is only called when the loading of + a library failed. It will be called when loading of cppuhelper failed. + Because we extend the PATH to the install folder while this function is + executed (see extendPath), all other libraries are found. +*/ +extern "C" FARPROC WINAPI delayLoadHook( + unsigned int dliNotify, + PDelayLoadInfo pdli + ) +{ + if (dliNotify == dliFailLoadLib) + { + HANDLE h = loadFromPath(pdli->szDll); + return reinterpret_cast<FARPROC>(h); + } + return nullptr; +} +} + +ExternC +const PfnDliHook __pfnDliFailureHook2 = delayLoadHook; + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace uno +{ +namespace util +{ + +/** Bootstrapping native UNO. + + Bootstrapping requires the existence of many libraries which are contained + in an URE installation. To find and load these libraries the Windows + registry keys HKEY_CURRENT_USER\Software\LibreOffice\Layer\URE\1 + and HKEY_LOCAL_MACHINE\Software\LibreOffice\Layer\URE\1 are examined. + These contain a named value UREINSTALLLOCATION which holds a path to the URE + installation folder. +*/ +public ref class Bootstrap sealed +{ + inline Bootstrap() {} + +public: + + /** Bootstraps the initial component context from a native UNO installation. + + @see cppuhelper/bootstrap.hxx:defaultBootstrap_InitialComponentContext() + */ + static ::unoidl::com::sun::star::uno::XComponentContext ^ + defaultBootstrap_InitialComponentContext(); + + /** Bootstraps the initial component context from a native UNO installation. + + @param ini_file + a file URL of an ini file, e.g. uno.ini/unorc. (The ini file must + reside next to the cppuhelper library) + @param bootstrap_parameters + bootstrap parameters (maybe null) + + @see cppuhelper/bootstrap.hxx:defaultBootstrap_InitialComponentContext() + */ + static ::unoidl::com::sun::star::uno::XComponentContext ^ + defaultBootstrap_InitialComponentContext( + ::System::String ^ ini_file, + ::System::Collections::IDictionaryEnumerator ^ + bootstrap_parameters ); + + /** Bootstraps the initial component context from a native UNO installation. + + @see cppuhelper/bootstrap.hxx:bootstrap() + */ + static ::unoidl::com::sun::star::uno::XComponentContext ^ + bootstrap(); +}; + + +::unoidl::com::sun::star::uno::XComponentContext ^ +Bootstrap::defaultBootstrap_InitialComponentContext( + ::System::String ^ ini_file, + ::System::Collections::IDictionaryEnumerator ^ bootstrap_parameters ) +{ + if (nullptr != bootstrap_parameters) + { + bootstrap_parameters->Reset(); + while (bootstrap_parameters->MoveNext()) + { + OUString key( + String_to_ustring( safe_cast< ::System::String ^ >( + bootstrap_parameters->Key ) ) ); + OUString value( + String_to_ustring( safe_cast< ::System::String ^ >( + bootstrap_parameters->Value ) ) ); + + ::rtl::Bootstrap::set( key, value ); + } + } + + // bootstrap native uno + Reference< XComponentContext > xContext; + if (nullptr == ini_file) + { + xContext = ::cppu::defaultBootstrap_InitialComponentContext(); + } + else + { + xContext = ::cppu::defaultBootstrap_InitialComponentContext( + String_to_ustring( safe_cast< ::System::String ^ >( ini_file ) ) ); + } + + return safe_cast< ::unoidl::com::sun::star::uno::XComponentContext ^ >( + to_cli( xContext ) ); +} + + +::unoidl::com::sun::star::uno::XComponentContext ^ +Bootstrap::defaultBootstrap_InitialComponentContext() +{ + return defaultBootstrap_InitialComponentContext( nullptr, nullptr ); +} + +::unoidl::com::sun::star::uno::XComponentContext ^ Bootstrap::bootstrap() +{ + Reference<XComponentContext> xContext = ::cppu::bootstrap(); + return safe_cast< ::unoidl::com::sun::star::uno::XComponentContext ^ >( + to_cli( xContext ) ); + +} + +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cli_ure/source/native/native_share.h b/cli_ure/source/native/native_share.h new file mode 100644 index 000000000..f733d2558 --- /dev/null +++ b/cli_ure/source/native/native_share.h @@ -0,0 +1,112 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#using "cli_ure.dll" +#using "cli_uretypes.dll" + +#include "rtl/ustring.hxx" +#include <uno/lbnames.h> +#include "uno/mapping.hxx" + +#include <vcclr.h> + + +namespace uno +{ +namespace util +{ + + +inline ::System::String ^ ustring_to_String( OUString const & ustr ) +{ + return gcnew ::System::String( + reinterpret_cast<wchar_t const *>(ustr.getStr()), 0, ustr.getLength()); +} + +inline OUString String_to_ustring( ::System::String ^ str ) +{ + OSL_ASSERT( sizeof (wchar_t) == sizeof (sal_Unicode) ); + pin_ptr<wchar_t const> chars = PtrToStringChars( str ); + return OUString(reinterpret_cast<sal_Unicode const *>(chars), str->Length); +} + +template< typename T > +inline ::System::Object ^ to_cli( + css::uno::Reference< T > const & x ) +{ + css::uno::Mapping mapping( + CPPU_CURRENT_LANGUAGE_BINDING_NAME, UNO_LB_CLI ); + OSL_ASSERT( mapping.is() ); + if (! mapping.is()) + { + throw css::uno::RuntimeException( + "cannot get mapping from C++ to CLI!", + css::uno::Reference< + css::uno::XInterface >() ); + } + + intptr_t intptr = + reinterpret_cast< intptr_t >( + mapping.mapInterface( x.get(), cppu::UnoType<decltype(x)>::get() ) ); + ::System::Runtime::InteropServices::GCHandle ^ handle = ::System::Runtime::InteropServices::GCHandle::FromIntPtr(::System::IntPtr(intptr)); + ::System::Object ^ ret = handle->Target; + handle->Free(); + return ret; +} + +template< typename T > +inline void to_uno( + css::uno::Reference< T > * pRet, ::System::Object ^ x ) +{ + css::uno::Mapping mapping( + UNO_LB_CLI, CPPU_CURRENT_LANGUAGE_BINDING_NAME ); + OSL_ASSERT( mapping.is() ); + if (! mapping.is()) + { + throw css::uno::RuntimeException( + "cannot get mapping from CLI to C++!", + css::uno::Reference< + css::uno::XInterface >() ); + } + + ::System::Runtime::InteropServices::GCHandle handle( + ::System::Runtime::InteropServices::GCHandle::Alloc( x ) ); + T * ret = 0; + mapping.mapInterface( + reinterpret_cast< void ** >( &ret ), + reinterpret_cast< void * >( + ::System::Runtime::InteropServices::GCHandle::op_Explicit( handle ) +#if defined _WIN64 + .ToInt64() +#elif defined _WIN32 + .ToInt32() +#else +#error ERROR: either _WIN64 or _WIN32 must be defined + ERROR: either _WIN64 or _WIN32 must be defined +#endif + ), + cppu::UnoType<T>::get() ); + handle.Free(); + pRet->set( ret, SAL_NO_ACQUIRE /* takeover ownership */ ); +} + +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cli_ure/source/native/path.cxx b/cli_ure/source/native/path.cxx new file mode 100644 index 000000000..6fae51314 --- /dev/null +++ b/cli_ure/source/native/path.cxx @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "sal/config.h" + +#if defined(_WIN32) + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +#include "sal/types.h" + +namespace cli_ure { + +SAL_DLLPUBLIC_EXPORT WCHAR * filename(WCHAR * path) { + WCHAR * f = path; + for (WCHAR * p = path;;) { + switch (*p++) { + case L'\0': + return f; + case L'\\': + f = p; + break; + } + } +} + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cli_ure/source/scripts/increment_version.pl b/cli_ure/source/scripts/increment_version.pl new file mode 100644 index 000000000..df0c677a8 --- /dev/null +++ b/cli_ure/source/scripts/increment_version.pl @@ -0,0 +1,273 @@ +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# + +use warnings; +use strict; +use diagnostics; + +sub trim; +sub readIncVersions($); +sub processLine($$); +sub checkName($); +sub incrementNewVersion($); +sub incrementOldVersion($); +sub incrementPolicyVersion($); +my $usage = +"The tool increments the minor version of assemblies and the major version of ". +"the respective policy files. This is only done if new uno types have been added since". +"the last product update. This information is obtained from the file which is passed as ". +"argument changedTypes. The names in the version file must have a particular form. ". +"They must end on one of following terms: NEW_VERSION, OLD_VERSION, POLICY_VERSION\n". +"If no new published types have been added then no output, argument newVersions, is written". +"Usage is: \n increment_version.pl oldVersions incVersions newVersions changedTypes\n\n". +"oldVersion: Contains name value pairs, which are used for forming the config files of ". +"the policy assemblies, for building the assemblies. \n\n". +"incVersions: File containing the names of which the versions are to be incremented. ". +"Every line may only contain one name. The names must exactly match those from the ". +"oldVersion file.\n\n". +"newVersions: Contains all entries from oldVersions, but the values of the names,". +"which occur in selection, have been incremented.\n\n". +"changedTypes: File that contains the information if new published types have been added ". +"since the last product update.\n\n" ; + +my $sNameForm = +"The names must end on one of these names: NEW_VERSION, OLD_VERSION, POLICY_VERSION\n". +"For example, valid names are: \n". +"CLI_URETYPES_NEW_VERSION\nCLI_URETYPES_OLD_VERSION\nCLI_URETYPES_POLICY_VERSION\n"; + +if (scalar @ARGV < 3) { + print $usage; + exit -1; +} + +-e "$ARGV[0]" or die "Error: wrong arguments. \n".$usage; +-e "$ARGV[1]" or die "Error: wrong arguments. \n".$usage; +#-e "$ARGV[3]" or die "Error: wrong arguments. \n".$usage; + +# DISABLED: always increment +#check if new types have been added since last release. +#If not, then there is nothing to be done. +#read in oldVersions line by line and apply the increment operation +#open TYPES, "$ARGV[3]" or die "Cannot open to $ARGV[3] $!"; + +my $newTypes; + +#We look for the line that contains the number of new types +#while(<TYPES>) +#{ +# if (/New and published types/i) +# { +# $_ =~ /=\s*(\d+)/; +# if ( ! defined $1) +# { +# print "\n###$ARGV[3] contains an invalid entry for 'New and published types'. \n\n"; +# exit -1; +# } +# $newTypes = $1; +# } +#} + +#Check if changeTypes contained the line we are looking for +#if (! defined $newTypes) +#{ +# print "\n###$ARGV[3] does not contain entry about the new types ". +# "or we are looking for the wrong string! \n\n"; +# exit -1; +#} + +#if ( $newTypes == 0) +#{ +# print "\nNo new UNO types since las product update.\n"; +# exit 0; +#} +#else +#{ +# print "\nNew UNO types were added since last release. The version will be increased.\n\n"; +#} + +#read in incVersions in a list +my @incVersions = readIncVersions($ARGV[1]); +#print "@incVersions"; + +#read in oldVersions line by line and apply the increment operation +open OLDVERSION, "$ARGV[0]" or die "Cannot open to $ARGV[0] $!"; + +#open file we want to write to +open NEWVERSION, "> $ARGV[2]" or die "Cannot write to $ARGV[2] $!"; + +print NEWVERSION processLine($_, @incVersions) while(<OLDVERSION>); + +close NEWVERSION; +close OLDVERSION; + +exit 0; + +sub processLine($$) +{ + my $line = $_[0]; + #skip empty lines + my $trimmed; + return $line if (length($trimmed = trim($line)) == 0); + #Skip comment symbol: # + return $line if ($trimmed =~ /^#/); + + #Get the left part of '=' + my $i = index($line, "="); + if( $i == -1) + { + print "Error: No '=' found in line:,: \n $line \n"; + exit -1; + } + my $name = substr($line, 0, $i); + $name = trim($name); + #We do not check the names here because the file can contain + #other names, e.g. CLI_URETYPES_POLICY_ASSEMBLY + if (length($name) == 0) { + print "Wrong line in $ARGV[0]\n", $sNameForm; + exit -1; + } + my $value = substr($line, $i + 1); + $value = trim($value); + + #Check if the entry shall be incremented, this information is in the second + #argument + my $found; + for(@incVersions) { + if ($_ eq $name) { + $found = 1; + last; + } + } + if ( ! defined($found)) { + return $line; + } + + #Check if the name represents a version we need to change + if ($name =~ /NEW_VERSION$/) + { + $value = incrementNewVersion($value); + } + elsif ($name =~ /OLD_VERSION$/) + { + $value = incrementOldVersion($value); + } + elsif ($name =~ /POLICY_VERSION$/) + { + $value = incrementPolicyVersion($value); + } + else + { + #other name which we ignore + return $line; + } + return "${name}=${value}\n"; +} + +#The value of a new version has the form x.x.x.x +#We increment the third position from the left. +#Te argument must already be trimmed. +sub incrementNewVersion($) +{ + my @parts = split /\./,$_[0]; + if (scalar @parts != 4) + { + print "Error, no valid version given in $ARGV[0]\n. A 'new version' has four parts."; + exit -1; + } + $parts[2]++; + #build the version string and return + return "$parts[0].$parts[1].$parts[2].$parts[3]"; +} + +#The value of a new version has the form x.x.x.x-x.x.x.x +#We increment the third position of the second part. +#Te argument must already be trimmed. +sub incrementOldVersion($) +{ + my @parts = split /[\.-]/,$_[0]; + if (scalar @parts != 8) + { + print "Error, no valid version given in $ARGV[0]\n. A 'old version' has the form +x.x.x.x-x.x.x.x\n."; + exit -1; + } + $parts[6]++; + return "$parts[0].$parts[1].$parts[2].$parts[3]-$parts[4].$parts[5].$parts[6].$parts[7]"; + return $_[0]; +} + +sub incrementPolicyVersion($) +{ + my @parts = split /\./,$_[0]; + if (scalar @parts != 4) + { + print "Error, no valid version given in $ARGV[0]\n. A 'policy version' has four parts."; + exit -1; + } + $parts[0]++; + #build the version string and return + return "$parts[0].$parts[1].$parts[2].$parts[3]"; +} + + +sub readIncVersions($) +{ + open INC, $_[0] or die "Could not open $_[0] $!"; + my $arg = $_[0]; + my @names; + + while(<INC>) + { + chomp; + #Skip empty lines + my $line; + if (length($line = trim($_)) == 0) { + next; + } + #Skip comment symbol: # + if ($line =~ /^#/) { + next; + } + if (!checkName($line)) { + print "Wrong entry in file $_[0]\n", $sNameForm; + exit -1; + } + push @names, $line; + } + print "No entries found in $arg\n" if(scalar @names == 0); + return @names; +} + +#The argument must already be trimmed +#returns 1 if ok +sub checkName($) +{ + my $name = $_[0]; + if ( $name !~/NEW_VERSION$|OLD_VERSION$|POLICY_VERSION$/) { + return 0; + } + return 1; +} + +sub trim($) +{ + my $string = shift; + $string =~ s/^\s+//; + $string =~ s/\s+$//; + return $string; +} diff --git a/cli_ure/source/scripts/subst_template.pl b/cli_ure/source/scripts/subst_template.pl new file mode 100644 index 000000000..b3f90fef6 --- /dev/null +++ b/cli_ure/source/scripts/subst_template.pl @@ -0,0 +1,124 @@ +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This file incorporates work covered by the following license notice: +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed +# with this work for additional information regarding copyright +# ownership. The ASF licenses this file to you under the Apache +# License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 . +# + +use warnings; +use strict; +use diagnostics; + +sub trim; +sub readRedirectionValues($); + +my $usage = + "Usage is: \n subst_template.pl configTemplate redirections policyConfig + + configTemplate: The config file which is used for the policy assembly. It + contains place holders for the binding redirection. + + redirections: file containing the values for oldVersion and newVersion tags + which are used in the BindingRedirect element of the config files. + + policyConfig: Name of the file in which we want to write the config file. +"; + + +if (scalar @ARGV < 3) { + print $usage; + exit -1; +} + + +my %redirectionValue = readRedirectionValues($ARGV[1]); +#print "|$_| |$redirectionValue{$_}|\n", for keys %redirectionValue; + + +#Read config file in which we will replace the versions +$/ = undef; +open TEMPLATE, $ARGV[0] or die $!; +my $templ = <TEMPLATE>; + +#Open the config file we are goint to write to +open CONFIG, "> $ARGV[2]" or die "Cannot write to $ARGV[2] $!"; + +#No substitute the place holders for oldVersion and new Version in the config template with +#the values obtained from the redirections file +for (keys %redirectionValue) { + $templ=~ s/\b$_\b/$redirectionValue{$_}/; +} +#Write the config file +print CONFIG $templ; + +#Reads the key value pairs from the files, which name must be passed in +#the parameter. The file contains lines of the form name=value, for example +#CLI_TYPES_OLD_VERSION=1.1.0.0-1.1.1.0 +sub readRedirectionValues($) +{ + #Read in the values for the version redirection + open REDIR, $_[0] or die $!; + + my %redirectionValues; + + while (<REDIR>) + { + chomp; + my $trimmed; + #Skip empty lines + if (length($trimmed = trim($_)) == 0) { + next; + } + + #Skip comment symbol: # + if ($trimmed =~ /^#/) { + next; + } + + my @lineParts = split /=/,$_; + + #Check if we have valid name value pairs. + if (scalar @lineParts != 2) { + print "Error: Values in $ARGV[1] are not correct (Entries must have the form name=value). Invalid line: \n$_\n"; + exit -1; + } + + #Trim the strings and check if they still contain characters + my $name = trim($lineParts[0]); + my $value = trim($lineParts[1]); + if (length($name) == 0 || length($value) == 0) { + print "Error: Values in $ARGV[1] are not correct. Invalid line: \n$_\n"; + exit -1; + } + + #Check if we have duplicate key names + for (keys %redirectionValues) { + if ( $name eq $_) { + print "Error: Values in $ARGV[1] are not correct. The name $_ is not unique.\n"; + exit -1; + } + } + + $redirectionValues{$name} = $value; + } + return %redirectionValues; +} + +sub trim($) +{ + my $string = shift; + $string =~ s/^\s+//; + $string =~ s/\s+$//; + return $string; +} diff --git a/cli_ure/source/uno_bridge/README.txt b/cli_ure/source/uno_bridge/README.txt new file mode 100644 index 000000000..39b3ce364 --- /dev/null +++ b/cli_ure/source/uno_bridge/README.txt @@ -0,0 +1,20 @@ +Because of the LoaderLock bug in .NET Framework 1.0 and 1.1 the cli_uno.dll is linked +with the /NOENTRY switch, which prevent that the C-runtime is initialized when loading +the dll. + +Also I removed all static c++ objects which need construction by the CRT, +exception handling seems to need an initialised CRT. Therefore +I added CRT initialization code in uno_initEnvironment (cli_bridge.cxx) +However there is no deinitialization done because bridge libraries remain +in memory until the process dies. There is actually no good place where +this had to be called. If we would do that we would have to implement that +the bridge can be disposed. + + +Sell also: + +http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dv_vstechart/html/vcconmixeddllloadingproblem.asp +http://support.microsoft.com/?id=814472 +http://www.ddj.com/dept/windows/184416689 +http://blogs.msdn.com/cbrumme/archive/2003/08/20/51504.aspx +http://msdn2.microsoft.com/en-US/library/ms172219.aspx
\ No newline at end of file diff --git a/cli_ure/source/uno_bridge/cli_base.h b/cli_ure/source/uno_bridge/cli_base.h new file mode 100644 index 000000000..bca780f7c --- /dev/null +++ b/cli_ure/source/uno_bridge/cli_base.h @@ -0,0 +1,171 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CLI_URE_SOURCE_UNO_BRIDGE_CLI_BASE_H +#define INCLUDED_CLI_URE_SOURCE_UNO_BRIDGE_CLI_BASE_H + +#pragma unmanaged +// Workaround: osl/mutex.h contains only a forward declaration of _oslMutexImpls. +// When using the inline class in Mutex in osl/mutex.hxx, the loader needs to find +// a declaration for the struct. If not found a TypeLoadException is being thrown. +struct _oslMutexImpl +{ +}; +#pragma managed +#include <memory> +#include "rtl/ustring.hxx" +#include "typelib/typedescription.hxx" + +#using <system.dll> + +namespace cli_uno +{ +System::Type^ loadCliType(System::String ^ typeName); +System::Type^ mapUnoType(typelib_TypeDescription const * pTD); +System::Type^ mapUnoType(typelib_TypeDescriptionReference const * pTD); +typelib_TypeDescriptionReference* mapCliType(System::Type^ cliType); +OUString mapCliString(System::String ^ data); +System::String^ mapUnoString(rtl_uString const * data); +System::String^ mapUnoTypeName(rtl_uString const * typeName); + +ref struct Constants +{ + static System::String^ sXInterfaceName= gcnew System::String( + "unoidl.com.sun.star.uno.XInterface"); + static System::String^ sObject= gcnew System::String("System.Object"); + static System::String^ sType= gcnew System::String("System.Type"); + static System::String^ sUnoidl= gcnew System::String("unoidl."); + static System::String^ sVoid= gcnew System::String("System.Void"); + static System::String^ sAny= gcnew System::String("uno.Any"); + static System::String^ sArArray= gcnew System::String("System.Array[]"); + static System::String^ sBoolean= gcnew System::String("System.Boolean"); + static System::String^ sChar= gcnew System::String("System.Char"); + static System::String^ sByte= gcnew System::String("System.Byte"); + static System::String^ sInt16= gcnew System::String("System.Int16"); + static System::String^ sUInt16= gcnew System::String("System.UInt16"); + static System::String^ sInt32= gcnew System::String("System.Int32"); + static System::String^ sUInt32= gcnew System::String("System.UInt32"); + static System::String^ sInt64= gcnew System::String("System.Int64"); + static System::String^ sUInt64= gcnew System::String("System.UInt64"); + static System::String^ sString= gcnew System::String("System.String"); + static System::String^ sSingle= gcnew System::String("System.Single"); + static System::String^ sDouble= gcnew System::String("System.Double"); + static System::String^ sArBoolean= gcnew System::String("System.Boolean[]"); + static System::String^ sArChar= gcnew System::String("System.Char[]"); + static System::String^ sArByte= gcnew System::String("System.Byte[]"); + static System::String^ sArInt16= gcnew System::String("System.Int16[]"); + static System::String^ sArUInt16= gcnew System::String("System.UInt16[]"); + static System::String^ sArInt32= gcnew System::String("System.Int32[]"); + static System::String^ sArUInt32= gcnew System::String("System.UInt32[]"); + static System::String^ sArInt64= gcnew System::String("System.Int64[]"); + static System::String^ sArUInt64= gcnew System::String("System.UInt64[]"); + static System::String^ sArString= gcnew System::String("System.String[]"); + static System::String^ sArSingle= gcnew System::String("System.Single[]"); + static System::String^ sArDouble= gcnew System::String("System.Double[]"); + static System::String^ sArType= gcnew System::String("System.Type[]"); + static System::String^ sArObject= gcnew System::String("System.Object[]"); + static System::String^ sBrackets= gcnew System::String("[]"); + static System::String^ sAttributeSet= gcnew System::String("set_"); + static System::String^ sAttributeGet= gcnew System::String("get_"); + + static System::String^ usXInterface = "com.sun.star.uno.XInterface"; + static System::String^ usVoid = "void"; + static System::String^ usType = "type"; + static System::String^ usAny = "any"; + static System::String^ usBrackets = "[]"; + static System::String^ usBool = "boolean"; + static System::String^ usByte = "byte"; + static System::String^ usChar = "char"; + static System::String^ usShort = "short"; + static System::String^ usUShort = "unsigned short"; + static System::String^ usLong = "long"; + static System::String^ usULong = "unsigned long"; + static System::String^ usHyper = "hyper"; + static System::String^ usUHyper = "unsigned hyper"; + static System::String^ usString = "string"; + static System::String^ usFloat = "float"; + static System::String^ usDouble = "double"; +}; + +struct BridgeRuntimeError +{ + OUString m_message; + + inline BridgeRuntimeError( OUString const & message ) + : m_message( message ) + {} +}; + + +struct rtl_mem +{ + inline static void * operator new ( size_t nSize ) + { return std::malloc( nSize ); } + inline static void operator delete ( void * mem ) + { std::free( mem ); } + inline static void * operator new ( size_t, void * mem ) + { return mem; } + inline static void operator delete ( void *, void * ) + {} + + static inline std::unique_ptr< rtl_mem > allocate( std::size_t bytes ); +}; + +inline std::unique_ptr< rtl_mem > rtl_mem::allocate( std::size_t bytes ) +{ + void * p = std::malloc( bytes ); + if (0 == p) + throw BridgeRuntimeError("out of memory!" ); + return std::unique_ptr< rtl_mem >( (rtl_mem *)p ); +} + + +class TypeDescr +{ + typelib_TypeDescription * m_td; + + TypeDescr( TypeDescr const & ) = delete; + TypeDescr& operator = ( TypeDescr const & ) = delete; + +public: + inline explicit TypeDescr( typelib_TypeDescriptionReference * td_ref ); + inline ~TypeDescr() + { TYPELIB_DANGER_RELEASE( m_td ); } + + inline typelib_TypeDescription * get() const + { return m_td; } +}; + +inline TypeDescr::TypeDescr( typelib_TypeDescriptionReference * td_ref ) + : m_td( 0 ) +{ + TYPELIB_DANGER_GET( &m_td, td_ref ); + if (0 == m_td) + { + throw BridgeRuntimeError( + "cannot get comprehensive type description for " + + OUString::unacquired(&td_ref->pTypeName) ); + } +} + + +} //end namespace cli_uno + #endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cli_ure/source/uno_bridge/cli_bridge.cxx b/cli_ure/source/uno_bridge/cli_bridge.cxx new file mode 100644 index 000000000..d9b4cdd0b --- /dev/null +++ b/cli_ure/source/uno_bridge/cli_bridge.cxx @@ -0,0 +1,321 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <vcclr.h> +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#include "uno/environment.hxx" +#include "uno/lbnames.h" +#include "uno/mapping.hxx" +#include "typelib/typedescription.hxx" +#include "rtl/ustring.hxx" +#include <sal/log.hxx> + +#include "cli_bridge.h" +#include "cli_proxy.h" + +namespace sri= System::Runtime::InteropServices; + +namespace cli_uno +{ + +extern "C" +{ +void SAL_CALL Mapping_acquire( uno_Mapping * mapping ) + SAL_THROW_EXTERN_C() +{ + Mapping const * that = static_cast< Mapping const * >( mapping ); + that->m_bridge->acquire(); +} + +void SAL_CALL Mapping_release( uno_Mapping * mapping ) + SAL_THROW_EXTERN_C() +{ + Mapping const * that = static_cast< Mapping const * >( mapping ); + that->m_bridge->release(); +} + + +void SAL_CALL Mapping_cli2uno( + uno_Mapping * mapping, void ** ppOut, + void * pIn, typelib_InterfaceTypeDescription * td ) + SAL_THROW_EXTERN_C() +{ + uno_Interface ** ppUnoI = (uno_Interface **)ppOut; + intptr_t cliI = (intptr_t)pIn; + + OSL_ENSURE( ppUnoI && td, "### null ptr!" ); + + if (0 != *ppUnoI) + { + uno_Interface * pUnoI = *(uno_Interface **)ppUnoI; + (*pUnoI->release)( pUnoI ); + *ppUnoI = 0; + } + try + { + Mapping const * that = static_cast< Mapping const * >( mapping ); + Bridge * bridge = that->m_bridge; + + if (0 != cliI) + { + System::Object^ cliObj= sri::GCHandle::FromIntPtr(IntPtr(cliI)).Target; + (*ppOut)= bridge->map_cli2uno(cliObj, (typelib_TypeDescription*) td); + } + } + catch (BridgeRuntimeError & err) + { + (void) err; + SAL_WARN( "cli", "[cli_uno bridge error] " << err.m_message ); + } +} + +void SAL_CALL Mapping_uno2cli( + uno_Mapping * mapping, void ** ppOut, + void * pIn, typelib_InterfaceTypeDescription * td ) + SAL_THROW_EXTERN_C() +{ + try + { + OSL_ENSURE( td && ppOut, "### null ptr!" ); + OSL_ENSURE( (sizeof(System::Char) == sizeof(sal_Unicode)) + && (sizeof(System::Boolean) == sizeof(sal_Bool)) + && (sizeof(System::SByte) == sizeof(sal_Int8)) + && (sizeof(System::Int16) == sizeof(sal_Int16)) + && (sizeof(System::UInt16) == sizeof(sal_uInt16)) + && (sizeof(System::Int32) == sizeof(sal_Int32)) + && (sizeof(System::UInt32) == sizeof(sal_uInt32)) + && (sizeof(System::Int64) == sizeof(sal_Int64)) + && (sizeof(System::UInt64) == sizeof(sal_uInt64)) + && (sizeof(System::Single) == sizeof(float)) + && (sizeof(System::Double) == sizeof(double)), + "[cli_uno bridge] incompatible .NET data types"); + intptr_t * ppDNetI = (intptr_t *)ppOut; + uno_Interface * pUnoI = (uno_Interface *)pIn; + + Mapping const * that = static_cast< Mapping const * >( mapping ); + Bridge * bridge = that->m_bridge; + + if (0 != *ppDNetI) + { + sri::GCHandle::FromIntPtr(IntPtr(ppDNetI)).Free(); + } + + if (0 != pUnoI) + { + System::Object^ cliI= bridge->map_uno2cli(pUnoI, td); + intptr_t ptr= NULL; + if(cliI) + { + ptr= sri::GCHandle::ToIntPtr(sri::GCHandle::Alloc(cliI)) +#ifdef _WIN64 + .ToInt64(); +#else /* defined(_WIN32) */ + .ToInt32(); +#endif + } + (*ppOut)= reinterpret_cast<void*>(ptr); + } + } + catch (BridgeRuntimeError & err) + { + (void) err; + SAL_WARN( "cli", "[cli_uno bridge error] " << err.m_message ); + } +} + + +void SAL_CALL Bridge_free( uno_Mapping * mapping ) + SAL_THROW_EXTERN_C() +{ + Mapping * that = static_cast< Mapping * >( mapping ); + delete that->m_bridge; +} + +} //extern C +} //namespace + +namespace cli_uno +{ + + +/** ToDo + I doubt that the case that the ref count raises from 0 to 1 + can occur. uno_ext_getMapping returns an acquired mapping. Every time + that function is called then a new mapping is created. Following the + rules of ref counted objects, then if the ref count is null no one has + a reference to the object anymore. Hence no one can call acquire. If someone + calls acquire then they must have kept an unacquired pointer which is + illegal. + */ +void Bridge::acquire() const +{ + if (1 == osl_atomic_increment( &m_ref )) + { + if (m_registered_cli2uno) + { + uno_Mapping * mapping = const_cast<Mapping*>(&m_cli2uno); + uno_registerMapping( + const_cast<uno_Mapping**>(&mapping), Bridge_free, m_uno_cli_env, (uno_Environment *)m_uno_env, 0 ); + } + else + { + uno_Mapping * mapping = const_cast<Mapping*>(&m_uno2cli); + uno_registerMapping( + &mapping, Bridge_free, (uno_Environment *)m_uno_env, m_uno_cli_env, 0 ); + } + } +} + +void Bridge::release() const +{ + if (! osl_atomic_decrement( &m_ref )) + { + uno_revokeMapping( + m_registered_cli2uno + ? const_cast<Mapping*>(&m_cli2uno) + : const_cast<Mapping*>(&m_uno2cli) ); + } +} + +Bridge::Bridge( + uno_Environment * uno_cli_env, uno_ExtEnvironment * uno_env, + bool registered_cli2uno ) + : m_ref( 1 ), + m_uno_env( uno_env ), + m_uno_cli_env( uno_cli_env ), + m_registered_cli2uno( registered_cli2uno ) +{ + OSL_ASSERT( 0 != m_uno_cli_env && 0 != m_uno_env ); + (*((uno_Environment *)m_uno_env)->acquire)( (uno_Environment *)m_uno_env ); + (*m_uno_cli_env->acquire)( m_uno_cli_env ); + + // cli2uno + m_cli2uno.acquire = Mapping_acquire; + m_cli2uno.release = Mapping_release; + m_cli2uno.mapInterface = Mapping_cli2uno; + m_cli2uno.m_bridge = this; + // uno2cli + m_uno2cli.acquire = Mapping_acquire; + m_uno2cli.release = Mapping_release; + m_uno2cli.mapInterface = Mapping_uno2cli; + m_uno2cli.m_bridge = this; + +} + + +Bridge::~Bridge() +{ + //System::GC::Collect(); + (*m_uno_cli_env->release)( m_uno_cli_env ); + (*((uno_Environment *)m_uno_env)->release)( (uno_Environment *)m_uno_env ); +} + + +} //namespace cli_uno + +extern "C" +{ + +namespace cli_uno +{ + +void SAL_CALL cli_env_disposing( uno_Environment * uno_cli_env ) + SAL_THROW_EXTERN_C() +{ + uno_cli_env->pContext = 0; +} + + +SAL_DLLPUBLIC_EXPORT void SAL_CALL uno_initEnvironment( uno_Environment * uno_cli_env ) + SAL_THROW_EXTERN_C() +{ + //ToDo: remove when compiled with .NET 2 + + // Unclear whether the above comment refers to this whole function + // or a call to __crt_dll_initialize() that used to be here for + // _MSC_VER < 1400 + + uno_cli_env->environmentDisposing= cli_env_disposing; + uno_cli_env->pExtEnv = 0; + //Set the console to print Trace messages +#if OSL_DEBUG_LEVEL >= 1 + System::Diagnostics::Trace::Listeners-> + Add( gcnew System::Diagnostics::TextWriterTraceListener(System::Console::Out)); +#endif + OSL_ASSERT( 0 == uno_cli_env->pContext ); + + // We let the Cli_environment leak, since there is no good point where we could destruct it. + //dispose is not used because we would have then also synchronize the calls to proxies. If the + //Cli_environment is disposed, we must prevent all calls, otherwise we may crash at points + //where g_cli_env is accessed. + //When we compile the bridge with .NET 2 then we can again hold g_cli_env as a static gcroot + //member in a unmanaged class, such as Bridge. + CliEnvHolder::g_cli_env = gcnew Cli_environment(); +} + +SAL_DLLPUBLIC_EXPORT void SAL_CALL uno_ext_getMapping( + uno_Mapping ** ppMapping, uno_Environment * pFrom, uno_Environment * pTo ) + SAL_THROW_EXTERN_C() +{ + OSL_ASSERT( 0 != ppMapping && 0 != pFrom && 0 != pTo ); + if (*ppMapping) + { + (*(*ppMapping)->release)( *ppMapping ); + *ppMapping = 0; + } + + + OUString const & from_env_typename = OUString::unacquired( + &pFrom->pTypeName ); + OUString const & to_env_typename = OUString::unacquired( &pTo->pTypeName ); + + uno_Mapping * mapping = 0; + + try + { + if ( from_env_typename == UNO_LB_CLI && to_env_typename == UNO_LB_UNO ) + { + Bridge * bridge = new Bridge( pFrom, pTo->pExtEnv, true ); // ref count = 1 + mapping = &bridge->m_cli2uno; + uno_registerMapping( + &mapping, Bridge_free, pFrom, (uno_Environment *)pTo->pExtEnv, 0 ); + } + else if ( from_env_typename == UNO_LB_UNO && to_env_typename == UNO_LB_CLI ) + { + Bridge * bridge = new Bridge( pTo, pFrom->pExtEnv, false ); // ref count = 1 + mapping = &bridge->m_uno2cli; + uno_registerMapping( + &mapping, Bridge_free, (uno_Environment *)pFrom->pExtEnv, pTo, 0 ); + } + } + catch (BridgeRuntimeError & err) + { + (void) err; + SAL_WARN( "cli", "[cli_uno bridge error] " << err.m_message ); + } + *ppMapping = mapping; +} + +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cli_ure/source/uno_bridge/cli_bridge.h b/cli_ure/source/uno_bridge/cli_bridge.h new file mode 100644 index 000000000..95ddb0394 --- /dev/null +++ b/cli_ure/source/uno_bridge/cli_bridge.h @@ -0,0 +1,113 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CLI_URE_SOURCE_UNO_BRIDGE_CLI_BRIDGE_H +#define INCLUDED_CLI_URE_SOURCE_UNO_BRIDGE_CLI_BRIDGE_H +#include <vcclr.h> +#include "osl/interlck.h" +#include "uno/mapping.h" +#include "uno/environment.h" +#include "uno/dispatcher.h" +#include "cli_base.h" +#include "cli_environment.h" +//#using <cli_uretypes.dll> +#using <cli_basetypes.dll> +#using <system.dll> + +namespace sr = System::Reflection; + +namespace cli_uno +{ + + +// holds environments and mappings +struct Bridge; +struct Mapping : public uno_Mapping +{ + Bridge* m_bridge; +}; + +// The environment will be created in uno_initEnvironment. See also the remarks there +//Managed cli environment for cli objects a UNO proxies (which are cli +//objects. The uno_Environment is not used for cli objects. +ref struct CliEnvHolder { +static Cli_environment ^ g_cli_env = nullptr; +}; + + +/** An instance of Bridge represents exactly one mapping therefore either + m_cli2uno or m_uno2cli is valid. +*/ +struct Bridge +{ + mutable oslInterlockedCount m_ref; + uno_ExtEnvironment * m_uno_env; + uno_Environment * m_uno_cli_env; + + Mapping m_cli2uno; + Mapping m_uno2cli; + bool m_registered_cli2uno; + + ~Bridge(); + Bridge( uno_Environment * java_env, uno_ExtEnvironment * uno_env, bool registered_java2uno ); + + void acquire() const; + void release() const; + + void map_to_uno( + void * uno_data, System::Object^ cli_data, + typelib_TypeDescriptionReference * type, + bool assign) const; + + /** + @param info + the type of the converted data. It may be a byref type. + */ + void map_to_cli( + System::Object^ *cli_data, void const * uno_data, + typelib_TypeDescriptionReference * type, System::Type^ info /* maybe 0 */, + bool bDontCreateObj) const; + + System::Object^ map_uno2cli(uno_Interface * pUnoI, typelib_InterfaceTypeDescription* pTD) const; + + System::Object^ call_uno(uno_Interface * pUnoI, + typelib_TypeDescription* member_td, + typelib_TypeDescriptionReference * return_type, + sal_Int32 nParams, typelib_MethodParameter const * pParams, + array<System::Object^>^ args, array<System::Type^>^ argTypes, + System::Object^* pException) const; + + + void call_cli( + System::Object^ cliI, sr::MethodInfo^ method, + typelib_TypeDescriptionReference * return_type, + typelib_MethodParameter * params, int nParams, + void * uno_ret, void * uno_args [], uno_Any ** uno_exc ) const; + + uno_Interface * map_cli2uno( + System::Object^ cliI, typelib_TypeDescription* pTD) const; + +}; + +} //namespace cli_uno + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cli_ure/source/uno_bridge/cli_data.cxx b/cli_ure/source/uno_bridge/cli_data.cxx new file mode 100644 index 000000000..72b39df48 --- /dev/null +++ b/cli_ure/source/uno_bridge/cli_data.cxx @@ -0,0 +1,1929 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include "windows.h" +#include <ole2.h> + +#include <memory> + +#include "rtl/ustring.hxx" +#include "rtl/ustrbuf.hxx" +#include "uno/sequence2.h" +#include "typelib/typedescription.hxx" +#include "cli_proxy.h" +#include "cli_base.h" +#include "cli_bridge.h" + +#using <cli_uretypes.dll> + +#undef VOID + +namespace sri = System::Runtime::InteropServices; +namespace sr = System::Reflection; +namespace st = System::Text; +namespace ucss = unoidl::com::sun::star; + + +namespace cli_uno +{ +System::String^ mapUnoPolymorphicName(System::String^ unoName); +OUString mapCliTypeName(System::String^ typeName); +System::String^ mapCliPolymorphicName(System::String^ unoName); +System::String^ mapPolymorphicName(System::String^ unoName, bool bCliToUno); + +inline std::unique_ptr< rtl_mem > seq_allocate( sal_Int32 nElements, sal_Int32 nSize ) +{ + std::unique_ptr< rtl_mem > seq( + rtl_mem::allocate( SAL_SEQUENCE_HEADER_SIZE + (nElements * nSize) ) ); + uno_Sequence * p = (uno_Sequence *)seq.get(); + p->nRefCount = 1; + p->nElements = nElements; + return seq; +} + +System::Object^ Bridge::map_uno2cli(uno_Interface * pUnoI, typelib_InterfaceTypeDescription *pTD) const +{ + System::Object^ retVal= nullptr; + // get oid + rtl_uString * pOid = 0; + (*m_uno_env->getObjectIdentifier)( m_uno_env, &pOid, pUnoI ); + OSL_ASSERT( 0 != pOid ); + OUString oid(pOid, SAL_NO_ACQUIRE); + + // see if the interface was already mapped + System::Type^ ifaceType= mapUnoType(reinterpret_cast<typelib_TypeDescription*>(pTD)); + System::String^ sOid= mapUnoString(oid.pData); + + System::Threading::Monitor::Enter( CliEnvHolder::g_cli_env ); + try + { + retVal = CliEnvHolder::g_cli_env->getRegisteredInterface(sOid, ifaceType); + if (retVal) + { + // There is already a registered object. It can either be a proxy + // for the UNO object or a real cli object. In the first case we + // tell the proxy that it shall also represent the current UNO + // interface. If it already does that, then it does nothing + if (srr::RemotingServices::IsTransparentProxy(retVal)) + { + UnoInterfaceProxy^ p = static_cast<UnoInterfaceProxy^>( + srr::RemotingServices::GetRealProxy(retVal)); + p->addUnoInterface(pUnoI, pTD); + } + } + else + { + retVal = UnoInterfaceProxy::create( + (Bridge *) this, pUnoI, pTD, oid ); + } + } + __finally + { + System::Threading::Monitor::Exit( CliEnvHolder::g_cli_env ); + } + + return retVal; +} + +uno_Interface* Bridge::map_cli2uno(System::Object^ cliObj, typelib_TypeDescription *pTD) const +{ + uno_Interface* retIface = NULL; + // get oid from dot net environment + System::String^ ds_oid = CliEnvHolder::g_cli_env->getObjectIdentifier( cliObj); + OUString ousOid = mapCliString(ds_oid); + // look if interface is already mapped + m_uno_env->getRegisteredInterface(m_uno_env, (void**) &retIface, ousOid.pData, + (typelib_InterfaceTypeDescription*) pTD); + if ( ! retIface) + { + System::Threading::Monitor::Enter(Cli_environment::typeid); + try + { + m_uno_env->getRegisteredInterface(m_uno_env, (void**) &retIface, ousOid.pData, + (typelib_InterfaceTypeDescription*) pTD); + if ( ! retIface) + { + retIface = CliProxy::create((Bridge*)this, cliObj, pTD, ousOid); + } + } + __finally + { + System::Threading::Monitor::Exit(Cli_environment::typeid); + } + } + return retIface; +} + +inline System::Type^ loadCliType(rtl_uString * unoName) +{ + return loadCliType(mapUnoTypeName(unoName)); +} + +System::Type^ loadCliType(System::String ^ unoName) +{ + System::Type^ retVal= nullptr; + try + { + //If unoName denotes a polymorphic type, e.g com.sun.star.beans.Defaulted<System.Char> + //then we remove the type list, otherwise the type could not be loaded. + bool bIsPolymorphic = false; + + System::String ^ loadName = unoName; + int index = unoName->IndexOf('<'); + if (index != -1) + { + loadName = unoName->Substring(0, index); + bIsPolymorphic = true; + } + System::AppDomain^ currentDomain = System::AppDomain::CurrentDomain; + cli::array<sr::Assembly^>^ assems = currentDomain->GetAssemblies(); + for (int i = 0; i < assems->Length; i++) + { + retVal = assems[i]->GetType(loadName, false); + if (retVal) + break; + } + + if (retVal == nullptr) + { + System::String ^ msg = gcnew System::String("A type could not be loaded: "); + msg = System::String::Concat(msg, loadName); + throw BridgeRuntimeError(mapCliString(msg)); + } + + if (bIsPolymorphic) + { + retVal = uno::PolymorphicType::GetType(retVal, unoName); + } + } + catch( System::Exception ^ e) + { + OUString ouMessage(mapCliString(e->Message)); + throw BridgeRuntimeError(ouMessage); + } + return retVal; +} + +System::Type^ mapUnoType(typelib_TypeDescription const * pTD) +{ + return mapUnoType(pTD->pWeakRef); +} + +System::Type^ mapUnoType(typelib_TypeDescriptionReference const * pTD) +{ + System::Type ^ retVal = nullptr; + switch (pTD->eTypeClass) + { + case typelib_TypeClass_VOID: + retVal= void::typeid; break; + case typelib_TypeClass_CHAR: + retVal= System::Char::typeid; break; + case typelib_TypeClass_BOOLEAN: + retVal= System::Boolean::typeid; break; + case typelib_TypeClass_BYTE: + retVal= System::Byte::typeid; break; + case typelib_TypeClass_SHORT: + retVal= System::Int16::typeid; break; + case typelib_TypeClass_UNSIGNED_SHORT: + retVal= System::UInt16::typeid; break; + case typelib_TypeClass_LONG: + retVal= System::Int32::typeid; break; + case typelib_TypeClass_UNSIGNED_LONG: + retVal= System::UInt32::typeid; break; + case typelib_TypeClass_HYPER: + retVal= System::Int64::typeid; break; + case typelib_TypeClass_UNSIGNED_HYPER: + retVal= System::UInt64::typeid; break; + case typelib_TypeClass_FLOAT: + retVal= System::Single::typeid; break; + case typelib_TypeClass_DOUBLE: + retVal= System::Double::typeid; break; + case typelib_TypeClass_STRING: + retVal= System::String::typeid; break; + case typelib_TypeClass_TYPE: + retVal= System::Type::typeid; break; + case typelib_TypeClass_ANY: + retVal= uno::Any::typeid; break; + case typelib_TypeClass_ENUM: + case typelib_TypeClass_STRUCT: + case typelib_TypeClass_EXCEPTION: + retVal= loadCliType(pTD->pTypeName); break; + case typelib_TypeClass_INTERFACE: + { + //special handling for XInterface, since it does not exist in cli. + OUString usXInterface("com.sun.star.uno.XInterface"); + if (usXInterface.equals(pTD->pTypeName)) + retVal= System::Object::typeid; + else + retVal= loadCliType(pTD->pTypeName); + break; + } + case typelib_TypeClass_SEQUENCE: + { + css::uno::TypeDescription seqType( + const_cast<typelib_TypeDescriptionReference*>(pTD)); + typelib_TypeDescriptionReference* pElementTDRef= + reinterpret_cast<typelib_IndirectTypeDescription*>(seqType.get())->pType; + switch (pElementTDRef->eTypeClass) + { + case typelib_TypeClass_CHAR: + retVal= System::Type::GetType(const_cast<System::String^>(Constants::sArChar)); break; + case typelib_TypeClass_BOOLEAN: + retVal= System::Type::GetType(const_cast<System::String^>(Constants::sArBoolean)); + break; + case typelib_TypeClass_BYTE: + retVal= System::Type::GetType(const_cast<System::String^>(Constants::sArByte)); + break; + case typelib_TypeClass_SHORT: + retVal= System::Type::GetType(const_cast<System::String^>(Constants::sArInt16)); + break; + case typelib_TypeClass_UNSIGNED_SHORT: + retVal= System::Type::GetType(const_cast<System::String^>(Constants::sArUInt16)); + break; + case typelib_TypeClass_LONG: + retVal= System::Type::GetType(const_cast<System::String^>(Constants::sArInt32)); + break; + case typelib_TypeClass_UNSIGNED_LONG: + retVal= System::Type::GetType(const_cast<System::String^>(Constants::sArUInt32)); + break; + case typelib_TypeClass_HYPER: + retVal= System::Type::GetType(const_cast<System::String^>(Constants::sArInt64)); + break; + case typelib_TypeClass_UNSIGNED_HYPER: + retVal= System::Type::GetType(const_cast<System::String^>(Constants::sArUInt64)); + break; + case typelib_TypeClass_FLOAT: + retVal= System::Type::GetType(const_cast<System::String^>(Constants::sArSingle)); + break; + case typelib_TypeClass_DOUBLE: + retVal= System::Type::GetType(const_cast<System::String^>(Constants::sArDouble)); + break; + case typelib_TypeClass_STRING: + retVal= System::Type::GetType(const_cast<System::String^>(Constants::sArString)); + break; + case typelib_TypeClass_TYPE: + retVal= System::Type::GetType(const_cast<System::String^>(Constants::sArType)); + break; + case typelib_TypeClass_ANY: + case typelib_TypeClass_ENUM: + case typelib_TypeClass_EXCEPTION: + case typelib_TypeClass_STRUCT: + case typelib_TypeClass_INTERFACE: + case typelib_TypeClass_SEQUENCE: + { + retVal= loadCliType(pTD->pTypeName); + break; + } + default: + //All cases should be handled by the case statements above + OSL_ASSERT(0); + break; + } + break; + } + default: + OSL_ASSERT(false); + break; + } + return retVal; +} + +/** Returns an acquired td. + */ +typelib_TypeDescriptionReference* mapCliType(System::Type^ cliType) +{ + typelib_TypeDescriptionReference* retVal= NULL; + if (cliType == nullptr) + { + retVal = * typelib_static_type_getByTypeClass( + typelib_TypeClass_VOID ); + typelib_typedescriptionreference_acquire( retVal ); + return retVal; + } + //check for Enum first, + //because otherwise case System::TypeCode::Int32 applies + if (cliType->IsEnum) + { + OUString usTypeName= mapCliTypeName(cliType->FullName); + css::uno::Type unoType(css::uno::TypeClass_ENUM, usTypeName); + retVal= unoType.getTypeLibType(); + typelib_typedescriptionreference_acquire(retVal); + } + else + { + switch (System::Type::GetTypeCode(cliType)) + { + case System::TypeCode::Boolean: + retVal = * typelib_static_type_getByTypeClass( + typelib_TypeClass_BOOLEAN ); + typelib_typedescriptionreference_acquire( retVal ); + break; + case System::TypeCode::Char: + retVal = * typelib_static_type_getByTypeClass( + typelib_TypeClass_CHAR ); + typelib_typedescriptionreference_acquire( retVal ); + break; + case System::TypeCode::Byte: + retVal = * typelib_static_type_getByTypeClass( + typelib_TypeClass_BYTE ); + typelib_typedescriptionreference_acquire( retVal ); + break; + case System::TypeCode::Int16: + retVal = * typelib_static_type_getByTypeClass( + typelib_TypeClass_SHORT ); + typelib_typedescriptionreference_acquire( retVal ); + break; + case System::TypeCode::Int32: + retVal = * typelib_static_type_getByTypeClass( + typelib_TypeClass_LONG ); + typelib_typedescriptionreference_acquire( retVal ); + break; + case System::TypeCode::Int64: + retVal = * typelib_static_type_getByTypeClass( + typelib_TypeClass_HYPER ); + typelib_typedescriptionreference_acquire( retVal ); + break; + case System::TypeCode::UInt16: + retVal = * typelib_static_type_getByTypeClass( + typelib_TypeClass_UNSIGNED_SHORT ); + typelib_typedescriptionreference_acquire( retVal ); + break; + case System::TypeCode::UInt32: + retVal = * typelib_static_type_getByTypeClass( + typelib_TypeClass_UNSIGNED_LONG ); + typelib_typedescriptionreference_acquire( retVal ); + break; + case System::TypeCode::UInt64: + retVal = * typelib_static_type_getByTypeClass( + typelib_TypeClass_UNSIGNED_HYPER ); + typelib_typedescriptionreference_acquire( retVal ); + break; + case System::TypeCode::Single: + retVal = * typelib_static_type_getByTypeClass( + typelib_TypeClass_FLOAT ); + typelib_typedescriptionreference_acquire( retVal ); + break; + case System::TypeCode::Double: + retVal = * typelib_static_type_getByTypeClass( + typelib_TypeClass_DOUBLE ); + typelib_typedescriptionreference_acquire( retVal ); + break; + case System::TypeCode::String: + retVal = * typelib_static_type_getByTypeClass( + typelib_TypeClass_STRING ); + typelib_typedescriptionreference_acquire( retVal ); + break; + default: + break; + } + } + if (retVal == NULL) + { + System::String^ cliTypeName= cliType->FullName; + // Void + if (const_cast<System::String^>(Constants::sVoid)->Equals( + cliTypeName)) + { + retVal = * typelib_static_type_getByTypeClass( + typelib_TypeClass_VOID ); + typelib_typedescriptionreference_acquire( retVal ); + } + // Type + else if (const_cast<System::String^>(Constants::sType)->Equals( + cliTypeName)) + { + retVal = * typelib_static_type_getByTypeClass( + typelib_TypeClass_TYPE ); + typelib_typedescriptionreference_acquire( retVal ); + } + // Any + else if (const_cast<System::String^>(Constants::sAny)->Equals( + cliTypeName)) + { + retVal = * typelib_static_type_getByTypeClass( + typelib_TypeClass_ANY ); + typelib_typedescriptionreference_acquire( retVal ); + } + //struct, interfaces, sequences + else + { + OUString usTypeName; + uno::PolymorphicType ^ poly = dynamic_cast<uno::PolymorphicType^>(cliType); + if (poly != nullptr) + usTypeName = mapCliTypeName( poly->PolymorphicName); + else + usTypeName = mapCliTypeName(cliTypeName); + typelib_TypeDescription* td = NULL; + typelib_typedescription_getByName(&td, usTypeName.pData); + if (td) + { + retVal = td->pWeakRef; + typelib_typedescriptionreference_acquire(retVal); + typelib_typedescription_release(td); + } + } + } + if (retVal == NULL) + { + throw BridgeRuntimeError("[cli_uno bridge] mapCliType():could not map type: " + mapCliString(cliType->FullName)); + } + return retVal; +} + +/** + Otherwise a leading "unoidl." is removed. + */ +System::String^ mapUnoTypeName(rtl_uString const * typeName) +{ + OUString usUnoName( const_cast< rtl_uString * >( typeName ) ); + st::StringBuilder^ buf= gcnew st::StringBuilder(); + //determine if the type is a sequence and its dimensions + int dims= 0; + if (usUnoName[0] == '[') + { + sal_Int32 index= 1; + while (true) + { + if (usUnoName[index++] == ']') + dims++; + if (usUnoName[index++] != '[') + break; + } + usUnoName = usUnoName.copy(index - 1); + } + System::String ^ sUnoName = mapUnoString(usUnoName.pData); + if (sUnoName->Equals(const_cast<System::String^>(Constants::usBool))) + buf->Append(const_cast<System::String^>(Constants::sBoolean)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::usChar))) + buf->Append(const_cast<System::String^>(Constants::sChar)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::usByte))) + buf->Append(const_cast<System::String^>(Constants::sByte)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::usShort))) + buf->Append(const_cast<System::String^>(Constants::sInt16)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::usUShort))) + buf->Append(const_cast<System::String^>(Constants::sUInt16)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::usLong))) + buf->Append(const_cast<System::String^>(Constants::sInt32)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::usULong))) + buf->Append(const_cast<System::String^>(Constants::sUInt32)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::usHyper))) + buf->Append(const_cast<System::String^>(Constants::sInt64)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::usUHyper))) + buf->Append(const_cast<System::String^>(Constants::sUInt64)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::usFloat))) + buf->Append(const_cast<System::String^>(Constants::sSingle)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::usDouble))) + buf->Append(const_cast<System::String^>(Constants::sDouble)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::usString))) + buf->Append(const_cast<System::String^>(Constants::sString)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::usVoid))) + buf->Append(const_cast<System::String^>(Constants::sVoid)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::usType))) + buf->Append(const_cast<System::String^>(Constants::sType)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::usXInterface))) + buf->Append(const_cast<System::String^>(Constants::sObject)); + else if (sUnoName->Equals(const_cast<System::String^>(Constants::usAny))) + { + buf->Append(const_cast<System::String^>(Constants::sAny)); + } + else + { + //put "unoidl." at the beginning + buf->Append(const_cast<System::String^>(Constants::sUnoidl)); + //for polymorphic struct types remove the brackets, e.g mystruct<bool> -> mystruct + System::String ^ sName = mapUnoPolymorphicName(sUnoName); + buf->Append(sName); + } + // append [] + for (;dims--;) + buf->Append(const_cast<System::String^>(Constants::sBrackets)); + + return buf->ToString(); +} + +/** For example, there is a uno type + com.sun.star.Foo<char, long>. + The values in the type list + are uno types and are replaced by cli types, such as System.Char, + System.Int32, etc. + The prefix unoidl is not added. + */ +inline System::String^ mapUnoPolymorphicName(System::String^ unoName) +{ + return mapPolymorphicName(unoName, false); +} + +/** For example, there is a type name such as + com.sun.star.Foo<System.Char, System.Int32>. + The values in the type list + are CLI types and are replaced by uno types, such as char, + long, etc. + The prefix unoidl remains. + */ +inline System::String^ mapCliPolymorphicName(System::String^ unoName) +{ + return mapPolymorphicName(unoName, true); +} + +System::String^ mapPolymorphicName(System::String^ unoName, bool bCliToUno) +{ + int index = unoName->IndexOf('<'); + if (index == -1) + return unoName; + + System::Text::StringBuilder ^ builder = gcnew System::Text::StringBuilder(256); + builder->Append(unoName->Substring(0, index +1 )); + + //Find the first occurrence of ',' + //If the parameter is a polymorphic struct then we need to ignore everything + //between the brackets because it can also contain commas + //get the type list within < and > + int endIndex = unoName->Length - 1; + index++; + int cur = index; + int countParams = 0; + while (cur <= endIndex) + { + System::Char c = unoName[cur]; + if (c == ',' || c == '>') + { + //insert a comma if needed + if (countParams != 0) + builder->Append(","); + countParams++; + System::String ^ sParam = unoName->Substring(index, cur - index); + //skip the comma + cur++; + //the index to the beginning of the next param + index = cur; + if (bCliToUno) + { + builder->Append(mapCliTypeName(sParam).getStr()); + } + else + { + OUString s = mapCliString(sParam); + builder->Append(mapUnoTypeName(s.pData)); + } + } + else if (c == '<') + { + cur++; + //continue until the matching '>' + int numNested = 0; + for (;;cur++) + { + System::Char curChar = unoName[cur]; + if (curChar == '<') + { + numNested ++; + } + else if (curChar == '>') + { + if (numNested > 0) + numNested--; + else + break; + } + } + } + cur++; + } + + builder->Append((System::Char) '>'); + return builder->ToString(); +} + +OUString mapCliTypeName(System::String^ typeName) +{ + int dims= 0; + // Array? determine the "rank" (number of "[]") + // move from the rightmost end to the left, for example + // unoidl.PolymorphicStruct<System.Char[]>[] + // has only a "dimension" of 1 + int cur = typeName->Length - 1; + bool bRightBracket = false; + while (cur >= 0) + { + System::Char c = typeName[cur]; + if (c == ']') + { + bRightBracket = true; + } + else if (c == '[') + { + if (!bRightBracket) + throw BridgeRuntimeError( + "Typename is wrong. No matching brackets for sequence. Name is: " + + mapCliString(typeName)); + bRightBracket = false; + dims ++; + } + else + { + if (bRightBracket) + throw BridgeRuntimeError( + "Typename is wrong. No matching brackets for sequence. Name is: " + + mapCliString(typeName)); + break; + } + cur--; + } + + if (bRightBracket || cur < 0) + throw BridgeRuntimeError( + "Typename is wrong. " + + mapCliString(typeName)); + + typeName = typeName->Substring(0, cur + 1); + + System::Text::StringBuilder ^ buf = gcnew System::Text::StringBuilder(512); + + //Put the "[]" at the beginning of the uno type name + for (;dims--;) + buf->Append(const_cast<System::String^>(Constants::usBrackets)); + + if (typeName->Equals(const_cast<System::String^>(Constants::sBoolean))) + buf->Append(const_cast<System::String^>(Constants::usBool)); + else if (typeName->Equals(const_cast<System::String^>(Constants::sChar))) + buf->Append(const_cast<System::String^>(Constants::usChar)); + else if (typeName->Equals(const_cast<System::String^>(Constants::sByte))) + buf->Append(const_cast<System::String^>(Constants::usByte)); + else if (typeName->Equals(const_cast<System::String^>(Constants::sInt16))) + buf->Append(const_cast<System::String^>(Constants::usShort)); + else if (typeName->Equals(const_cast<System::String^>(Constants::sUInt16))) + buf->Append(const_cast<System::String^>(Constants::usUShort)); + else if (typeName->Equals(const_cast<System::String^>(Constants::sInt32))) + buf->Append(const_cast<System::String^>(Constants::usLong)); + else if (typeName->Equals(const_cast<System::String^>(Constants::sUInt32))) + buf->Append(const_cast<System::String^>(Constants::usULong)); + else if (typeName->Equals(const_cast<System::String^>(Constants::sInt64))) + buf->Append(const_cast<System::String^>(Constants::usHyper)); + else if (typeName->Equals(const_cast<System::String^>(Constants::sUInt64))) + buf->Append(const_cast<System::String^>(Constants::usUHyper)); + else if (typeName->Equals(const_cast<System::String^>(Constants::sSingle))) + buf->Append(const_cast<System::String^>(Constants::usFloat)); + else if (typeName->Equals(const_cast<System::String^>(Constants::sDouble))) + buf->Append(const_cast<System::String^>(Constants::usDouble)); + else if (typeName->Equals(const_cast<System::String^>(Constants::sString))) + buf->Append(const_cast<System::String^>(Constants::usString)); + else if (typeName->Equals(const_cast<System::String^>(Constants::sVoid))) + buf->Append(const_cast<System::String^>(Constants::usVoid)); + else if (typeName->Equals(const_cast<System::String^>(Constants::sType))) + buf->Append(const_cast<System::String^>(Constants::usType)); + else if (typeName->Equals(const_cast<System::String^>(Constants::sObject))) + buf->Append(const_cast<System::String^>(Constants::usXInterface)); + else if (typeName->Equals(const_cast<System::String^>(Constants::sAny))) + buf->Append(const_cast<System::String^>(Constants::usAny)); + else + { + System::String ^ sName = mapCliPolymorphicName(typeName); + int i= sName->IndexOf(L'.'); + buf->Append(sName->Substring(i + 1)); + } + return mapCliString(buf->ToString()); +} + +/** Maps uno types to dot net types. + * If uno_data is null then the type description is converted to System::Type + */ +inline System::String^ mapUnoString( rtl_uString const * data) +{ + OSL_ASSERT(data); + return gcnew System::String((__wchar_t*) data->buffer, 0, data->length); +} + +OUString mapCliString(System::String ^ data) +{ + + if (data != nullptr) + { + static_assert(sizeof(wchar_t) == sizeof(sal_Unicode), "char mismatch"); + pin_ptr<wchar_t const> pdata= PtrToStringChars(data); + return OUString( + reinterpret_cast<sal_Unicode const *>(pdata), + const_cast<System::String^>(data)->Length); + } + else + { + return OUString(); + } +} + +// ToDo convert cli types to expected types, e.g a long to a short where the uno type +// is a sal_Int16. This could be necessary if a scripting language (typeless) is used +// @param assign the uno_data has to be destructed (in/out args) +void Bridge::map_to_uno(void * uno_data, System::Object^ cli_data, + typelib_TypeDescriptionReference * type, + bool assign) const +{ + try{ + switch (type->eTypeClass) + { + case typelib_TypeClass_VOID: + break; + case typelib_TypeClass_CHAR: + { + System::Char aChar= *safe_cast<System::Char^>(cli_data); + *(sal_Unicode*) uno_data= aChar; + break; + } + case typelib_TypeClass_BOOLEAN: + { + System::Boolean aBool= *safe_cast<System::Boolean^>(cli_data); + *(sal_Bool*)uno_data= aBool == true ? sal_True : sal_False; + break; + } + case typelib_TypeClass_BYTE: + { + System::Byte aByte= *safe_cast<System::Byte^>(cli_data); + *(sal_Int8*) uno_data= aByte; + break; + } + case typelib_TypeClass_SHORT: + { + System::Int16 aShort= *safe_cast<System::Int16^>(cli_data); + *(sal_Int16*) uno_data= aShort; + break; + } + case typelib_TypeClass_UNSIGNED_SHORT: + { + System::UInt16 aUShort= *safe_cast<System::UInt16^>(cli_data); + *(sal_uInt16*) uno_data= aUShort; + break; + } + case typelib_TypeClass_LONG: + { + System::Int32 aLong= *safe_cast<System::Int32^>(cli_data); + *(sal_Int32*) uno_data= aLong; + break; + } + case typelib_TypeClass_UNSIGNED_LONG: + { + System::UInt32 aULong= *safe_cast<System::UInt32^>(cli_data); + *(sal_uInt32*) uno_data= aULong; + break; + } + case typelib_TypeClass_HYPER: + { + System::Int64 aHyper= *safe_cast<System::Int64^>(cli_data); + *(sal_Int64*) uno_data= aHyper; + break; + } + case typelib_TypeClass_UNSIGNED_HYPER: + { + System::UInt64 aLong= *safe_cast<System::UInt64^>(cli_data); + *(sal_uInt64*) uno_data= aLong; + break; + } + case typelib_TypeClass_FLOAT: + { + System::Single aFloat= *safe_cast<System::Single^>(cli_data); + *(float*) uno_data= aFloat; + break; + } + case typelib_TypeClass_DOUBLE: + { + System::Double aDouble= *safe_cast<System::Double^>(cli_data); + *(double*) uno_data= aDouble; + break; + } + case typelib_TypeClass_STRING: + { + if (assign && *(rtl_uString**) uno_data) + rtl_uString_release(*(rtl_uString**) uno_data); + + *(rtl_uString **)uno_data = 0; + if (cli_data == nullptr) + { + rtl_uString_new((rtl_uString**) uno_data); + } + else + { + System::String ^s= safe_cast<System::String^>(cli_data); + pin_ptr<const wchar_t> pdata= PtrToStringChars(s); + rtl_uString_newFromStr_WithLength( + reinterpret_cast<rtl_uString **>(uno_data), + reinterpret_cast<sal_Unicode const *>(pdata), s->Length); + } + break; + } + case typelib_TypeClass_TYPE: + { + typelib_TypeDescriptionReference* td= mapCliType(safe_cast<System::Type^>( + cli_data)); + if (assign) + { + typelib_typedescriptionreference_release( + *(typelib_TypeDescriptionReference **)uno_data ); + } + *(typelib_TypeDescriptionReference **)uno_data = td; + break; + } + case typelib_TypeClass_ANY: + { + uno_Any * pAny = (uno_Any *)uno_data; + if (cli_data == nullptr) // null-ref or uninitialized any maps to empty any + { + if (assign) + uno_any_destruct( pAny, 0 ); + uno_any_construct( pAny, 0, 0, 0 ); + break; + } + uno::Any aAny= *safe_cast<uno::Any^>(cli_data); + css::uno::Type value_td( mapCliType(aAny.Type), SAL_NO_ACQUIRE); + + if (assign) + uno_any_destruct( pAny, 0 ); + + try + { + switch (value_td.getTypeClass()) + { + case css::uno::TypeClass_VOID: + pAny->pData = &pAny->pReserved; + break; + case css::uno::TypeClass_CHAR: + pAny->pData = &pAny->pReserved; + *(sal_Unicode*) &pAny->pReserved = *safe_cast<System::Char^>(aAny.Value); + break; + case css::uno::TypeClass_BOOLEAN: + pAny->pData = &pAny->pReserved; + *(sal_Bool *) &pAny->pReserved = *safe_cast<System::Boolean^>(aAny.Value); + break; + case css::uno::TypeClass_BYTE: + pAny->pData = &pAny->pReserved; + *(sal_Int8*) &pAny->pReserved = *safe_cast<System::Byte^>(aAny.Value); + break; + case css::uno::TypeClass_SHORT: + pAny->pData = &pAny->pReserved; + *(sal_Int16*) &pAny->pReserved = *safe_cast<System::Int16^>(aAny.Value); + break; + case css::uno::TypeClass_UNSIGNED_SHORT: + pAny->pData = &pAny->pReserved; + *(sal_uInt16*) &pAny->pReserved = *safe_cast<System::UInt16^>(aAny.Value); + break; + case css::uno::TypeClass_LONG: + pAny->pData = &pAny->pReserved; + *(sal_Int32*) &pAny->pReserved = *safe_cast<System::Int32^>(aAny.Value); + break; + case css::uno::TypeClass_UNSIGNED_LONG: + pAny->pData = &pAny->pReserved; + *(sal_uInt32*) &pAny->pReserved = *safe_cast<System::UInt32^>(aAny.Value); + break; + case css::uno::TypeClass_HYPER: + if (sizeof (sal_Int64) <= sizeof (void *)) + { + pAny->pData = &pAny->pReserved; + *(sal_Int64*) &pAny->pReserved = *safe_cast<System::Int64^>(aAny.Value); + } + else + { + std::unique_ptr< rtl_mem > mem( rtl_mem::allocate( sizeof (sal_Int64) ) ); + *(sal_Int64 *) mem.get()= *safe_cast<System::Int64^>(aAny.Value); + pAny->pData = mem.release(); + } + break; + case css::uno::TypeClass_UNSIGNED_HYPER: + if (sizeof (sal_uInt64) <= sizeof (void *)) + { + pAny->pData = &pAny->pReserved; + *(sal_uInt64*) &pAny->pReserved = *safe_cast<System::UInt64^>(aAny.Value); + } + else + { + std::unique_ptr< rtl_mem > mem( rtl_mem::allocate( sizeof (sal_uInt64) ) ); + *(sal_uInt64 *) mem.get()= *safe_cast<System::UInt64^>(aAny.Value); + pAny->pData = mem.release(); + } + break; + case css::uno::TypeClass_FLOAT: + if (sizeof (float) <= sizeof (void *)) + { + pAny->pData = &pAny->pReserved; + *(float*) &pAny->pReserved = *safe_cast<System::Single^>(aAny.Value); + } + else + { + std::unique_ptr< rtl_mem > mem( rtl_mem::allocate( sizeof (float) ) ); + *(float*) mem.get() = *safe_cast<System::Single^>(aAny.Value); + pAny->pData = mem.release(); + } + break; + case css::uno::TypeClass_DOUBLE: + if (sizeof (double) <= sizeof (void *)) + { + pAny->pData = &pAny->pReserved; + *(double*) &pAny->pReserved= *safe_cast<System::Double^>(aAny.Value); + } + else + { + std::unique_ptr< rtl_mem > mem( rtl_mem::allocate( sizeof (double) ) ); + *(double*) mem.get()= *safe_cast<System::Double^>(aAny.Value); + pAny->pData= mem.release(); + } + break; + case css::uno::TypeClass_STRING: // anies often contain strings; copy string directly + { + pAny->pData= &pAny->pReserved; + OUString _s = mapCliString(static_cast<System::String^>(aAny.Value)); + pAny->pReserved= _s.pData; + rtl_uString_acquire(_s.pData); + break; + } + case css::uno::TypeClass_TYPE: + case css::uno::TypeClass_ENUM: //ToDo copy enum direct + case css::uno::TypeClass_SEQUENCE: + case css::uno::TypeClass_INTERFACE: + pAny->pData = &pAny->pReserved; + pAny->pReserved = 0; + map_to_uno( + &pAny->pReserved, aAny.Value, value_td.getTypeLibType(), + false /* no assign */); + break; + case css::uno::TypeClass_STRUCT: + case css::uno::TypeClass_EXCEPTION: + { + css::uno::Type anyType(value_td); + typelib_TypeDescription* td= NULL; + anyType.getDescription(&td); + std::unique_ptr< rtl_mem > mem(rtl_mem::allocate(td->nSize)); + typelib_typedescription_release(td); + map_to_uno( + mem.get(), aAny.Value, value_td.getTypeLibType(), + false /* no assign */); + pAny->pData = mem.release(); + break; + } + default: + { + throw BridgeRuntimeError("[map_to_uno():" + value_td.getTypeName() + "] unsupported value type of any!"); + } + } + } + catch(System::InvalidCastException^ ) + { +// ToDo check this + if (assign) + uno_any_construct( pAny, 0, 0, 0 ); // restore some valid any + OUString str = "[map_to_uno():Any" + value_td.getTypeName() + "]The Any type " + value_td.getTypeName() + " does not correspond to its value type: "; + if(aAny.Value != nullptr) + { + css::uno::Type td(mapCliType(aAny.Value->GetType()), SAL_NO_ACQUIRE); + str += td.getTypeName(); + } + if (assign) + uno_any_construct( pAny, 0, 0, 0 ); // restore some valid any + throw BridgeRuntimeError(str); + } + catch (BridgeRuntimeError& ) + { + if (assign) + uno_any_construct( pAny, 0, 0, 0 ); // restore some valid any + throw; + } + catch (...) + { + if (assign) + uno_any_construct( pAny, 0, 0, 0 ); // restore some valid any + throw; + } + + pAny->pType = value_td.getTypeLibType(); + typelib_typedescriptionreference_acquire(pAny->pType); + break; + } + case typelib_TypeClass_ENUM: + { + // InvalidCastException is caught at the end of this method + System::Int32 aEnum= System::Convert::ToInt32((cli_data)); + *(sal_Int32*) uno_data = aEnum; + break; + } + case typelib_TypeClass_STRUCT: + case typelib_TypeClass_EXCEPTION: + { + css::uno::TypeDescription td(type); + typelib_CompoundTypeDescription * comp_td = + (typelib_CompoundTypeDescription*) td.get(); + + typelib_StructTypeDescription * struct_td = NULL; + if (type->eTypeClass == typelib_TypeClass_STRUCT) + struct_td = (typelib_StructTypeDescription*) td.get(); + + if ( ! ((typelib_TypeDescription*) comp_td)->bComplete) + ::typelib_typedescription_complete( + (typelib_TypeDescription**) & comp_td ); + + sal_Int32 nMembers = comp_td->nMembers; + boolean bException= false; + System::Type^ cliType = nullptr; + if (cli_data) + cliType = cli_data->GetType(); + + if (0 != comp_td->pBaseTypeDescription) + { + map_to_uno( + uno_data, cli_data, + ((typelib_TypeDescription *)comp_td->pBaseTypeDescription)->pWeakRef, + assign); + } + sal_Int32 nPos = 0; + try + { + OUString usUnoException("com.sun.star.uno.Exception"); + for (; nPos < nMembers; ++nPos) + { + typelib_TypeDescriptionReference * member_type= comp_td->ppTypeRefs[nPos]; + System::Object^ val= nullptr; + if (cli_data != nullptr) + { + sr::FieldInfo^ aField= cliType->GetField( + mapUnoString(comp_td->ppMemberNames[nPos])); + // special case for Exception.Message property + // The com.sun.star.uno.Exception.Message field is mapped to the + // System.Exception property. Type.GetField("Message") returns null + if ( ! aField && usUnoException.equals(td.get()->pTypeName)) + {// get Exception.Message property + OUString usMessageMember("Message"); + if (usMessageMember.equals(comp_td->ppMemberNames[nPos])) + { + sr::PropertyInfo^ pi= cliType->GetProperty( + mapUnoString(comp_td->ppMemberNames[nPos])); + val= pi->GetValue(cli_data, nullptr); + } + else + { + throw BridgeRuntimeError("[map_to_uno(): Member: " + OUString::unacquired(&comp_td->ppMemberNames[nPos])); + } + } + else + { + val= aField->GetValue(cli_data); + } + } + void * p = (char *) uno_data + comp_td->pMemberOffsets[ nPos ]; + //When using polymorphic structs then the parameterized members can be null. + //Then we set a default value. + bool bDefault = (struct_td != NULL + && struct_td->pParameterizedTypes != NULL + && struct_td->pParameterizedTypes[nPos] == sal_True + && val == nullptr) + || cli_data == nullptr; + switch (member_type->eTypeClass) + { + case typelib_TypeClass_CHAR: + if (bDefault) + *(sal_Unicode*) p = 0; + else + *(sal_Unicode*) p = *safe_cast<System::Char^>(val); + break; + case typelib_TypeClass_BOOLEAN: + if (bDefault) + *(sal_Bool*) p = sal_False; + else + *(sal_Bool*) p = *safe_cast<System::Boolean^>(val); + break; + case typelib_TypeClass_BYTE: + if (bDefault) + *(sal_Int8*) p = 0; + else + *(sal_Int8*) p = *safe_cast<System::Byte^>(val); + break; + case typelib_TypeClass_SHORT: + if (bDefault) + *(sal_Int16*) p = 0; + else + *(sal_Int16*) p = *safe_cast<System::Int16^>(val); + break; + case typelib_TypeClass_UNSIGNED_SHORT: + if (bDefault) + *(sal_uInt16*) p = 0; + else + *(sal_uInt16*) p = *safe_cast<System::UInt16^>(val); + break; + case typelib_TypeClass_LONG: + if (bDefault) + *(sal_Int32*) p = 0; + else + *(sal_Int32*) p = *safe_cast<System::Int32^>(val); + break; + case typelib_TypeClass_UNSIGNED_LONG: + if (bDefault) + *(sal_uInt32*) p = 0; + else + *(sal_uInt32*) p = *safe_cast<System::UInt32^>(val); + break; + case typelib_TypeClass_HYPER: + if (bDefault) + *(sal_Int64*) p = 0; + else + *(sal_Int64*) p = *safe_cast<System::Int64^>(val); + break; + case typelib_TypeClass_UNSIGNED_HYPER: + if (bDefault) + *(sal_uInt64*) p = 0; + else + *(sal_uInt64*) p= *safe_cast<System::UInt64^>(val); + break; + case typelib_TypeClass_FLOAT: + if (bDefault) + *(float*) p = 0.; + else + *(float*) p = *safe_cast<System::Single^>(val); + break; + case typelib_TypeClass_DOUBLE: + if (bDefault) + *(double*) p = 0.; + else + *(double*) p = *safe_cast<System::Double^>(val); + break; + default: + { // ToDo enum, should be converted here + map_to_uno(p, val, member_type, assign); + break; + } + } + } + } + catch (BridgeRuntimeError& e) + { + bException= true; + OUString str = "[map_to_uno():"; + if (cliType) + { + str += mapCliString(cliType->FullName) + "." + OUString::unacquired(&comp_td->ppMemberNames[nPos]) + " "; + } + str += e.m_message; + throw BridgeRuntimeError(str); + } + catch (System::InvalidCastException^ ) + { + bException= true; + OUString str = "[map_to_uno():"; + if (cliType) + { + str += mapCliString(cliType->FullName) + "." + OUString::unacquired(&comp_td->ppMemberNames[nPos]); + } + str += "] Value has not the required type."; + throw BridgeRuntimeError(str); + } + catch (...) + { + OSL_ASSERT(0); + bException= true; + throw; + } + __finally + { + if (bException && !assign) // if assign then caller cleans up + { + // cleanup the members which we have converted so far + for ( sal_Int32 nCleanup = 0; nCleanup < nPos; ++nCleanup ) + { + uno_type_destructData( + uno_data, comp_td->ppTypeRefs[ nCleanup ], 0 ); + } + if (0 != comp_td->pBaseTypeDescription) + { + uno_destructData( + uno_data, (typelib_TypeDescription *)comp_td->pBaseTypeDescription, 0 ); + } + } + } + break; + } + case typelib_TypeClass_SEQUENCE: + { + TypeDescr td( type ); + typelib_TypeDescriptionReference * element_type = + ((typelib_IndirectTypeDescription *)td.get())->pType; + + std::unique_ptr< rtl_mem > seq; + + System::Array^ ar = nullptr; + if (cli_data != nullptr) + { + ar = safe_cast<System::Array^>(cli_data); + sal_Int32 nElements = ar->GetLength(0); + + try + { + switch (element_type->eTypeClass) + { + case typelib_TypeClass_CHAR: + seq = seq_allocate(nElements, sizeof (sal_Unicode)); + sri::Marshal::Copy(safe_cast<cli::array<System::Char>^>(cli_data), 0, + IntPtr(& ((uno_Sequence*) seq.get())->elements), nElements); + break; + case typelib_TypeClass_BOOLEAN: + seq = seq_allocate(nElements, sizeof (sal_Bool)); + sri::Marshal::Copy(safe_cast<cli::array<System::Char>^>(cli_data), 0, + IntPtr(& ((uno_Sequence*) seq.get())->elements), nElements); + break; + case typelib_TypeClass_BYTE: + seq = seq_allocate( nElements, sizeof (sal_Int8) ); + sri::Marshal::Copy(safe_cast<cli::array<System::Byte>^>(cli_data), 0, + IntPtr(& ((uno_Sequence*) seq.get())->elements), nElements); + break; + case typelib_TypeClass_SHORT: + seq = seq_allocate(nElements, sizeof (sal_Int16)); + sri::Marshal::Copy(safe_cast<cli::array<System::Int16>^>(cli_data), 0, + IntPtr(& ((uno_Sequence*) seq.get())->elements), nElements); + break; + case typelib_TypeClass_UNSIGNED_SHORT: + seq = seq_allocate( nElements, sizeof (sal_uInt16) ); + sri::Marshal::Copy(dynamic_cast<cli::array<System::Int16>^>( + safe_cast<cli::array<System::UInt16>^>(cli_data)), 0, + IntPtr(& ((uno_Sequence*) seq.get())->elements), nElements); + break; + case typelib_TypeClass_LONG: + seq = seq_allocate(nElements, sizeof (sal_Int32)); + sri::Marshal::Copy(safe_cast<cli::array<System::Int32>^>(cli_data), 0, + IntPtr(& ((uno_Sequence*) seq.get())->elements), nElements); + break; + case typelib_TypeClass_UNSIGNED_LONG: + seq = seq_allocate( nElements, sizeof (sal_uInt32) ); + sri::Marshal::Copy(dynamic_cast<cli::array<System::Int32>^>( + safe_cast<cli::array<System::UInt32>^>(cli_data)), 0, + IntPtr(& ((uno_Sequence*) seq.get())->elements), nElements); + break; + case typelib_TypeClass_HYPER: + seq = seq_allocate(nElements, sizeof (sal_Int64)); + sri::Marshal::Copy(safe_cast<cli::array<System::Int64>^>(cli_data), 0, + IntPtr(& ((uno_Sequence*) seq.get())->elements), nElements); + break; + case typelib_TypeClass_UNSIGNED_HYPER: + seq = seq_allocate(nElements, sizeof (sal_uInt64)); + sri::Marshal::Copy(dynamic_cast<cli::array<System::Int64>^>( + safe_cast<cli::array<System::UInt64>^>(cli_data)), 0, + IntPtr(& ((uno_Sequence*) seq.get())->elements), nElements); + break; + case typelib_TypeClass_FLOAT: + seq = seq_allocate(nElements, sizeof (float)); + sri::Marshal::Copy(safe_cast<cli::array<System::Single>^>(cli_data), 0, + IntPtr(& ((uno_Sequence*) seq.get())->elements), nElements); + break; + case typelib_TypeClass_DOUBLE: + seq = seq_allocate(nElements, sizeof (double)); + sri::Marshal::Copy(safe_cast<cli::array<System::Double>^>(cli_data), 0, + IntPtr(& ((uno_Sequence*) seq.get())->elements), nElements); + break; + case typelib_TypeClass_STRING: + { + seq = seq_allocate(nElements, sizeof (rtl_uString*)); + cli::array<System::String^>^ arStr= safe_cast<cli::array<System::String^>^>(cli_data); + for (int i= 0; i < nElements; i++) + { + pin_ptr<const wchar_t> pdata= PtrToStringChars(arStr[i]); + rtl_uString** pStr= & ((rtl_uString**) & + ((uno_Sequence*) seq.get())->elements)[i]; + *pStr= NULL; + rtl_uString_newFromStr_WithLength( + pStr, + reinterpret_cast<sal_Unicode const *>(pdata), + arStr[i]->Length); + } + break; + } + case typelib_TypeClass_ENUM: + seq = seq_allocate(nElements, sizeof (sal_Int32)); + for (int i= 0; i < nElements; i++) + { + ((sal_Int32*) &((uno_Sequence*) seq.get())->elements)[i]= + System::Convert::ToInt32(ar->GetValue(i)); + } + break; + case typelib_TypeClass_TYPE: + case typelib_TypeClass_ANY: + case typelib_TypeClass_STRUCT: + case typelib_TypeClass_EXCEPTION: + case typelib_TypeClass_SEQUENCE: + case typelib_TypeClass_INTERFACE: + { + TypeDescr element_td( element_type ); + seq = seq_allocate( nElements, element_td.get()->nSize ); + + for (sal_Int32 nPos = 0; nPos < nElements; ++nPos) + { + try + { + void * p= ((uno_Sequence *) seq.get())->elements + + (nPos * element_td.get()->nSize); + System::Object^ elemData= safe_cast<System::Array^>(cli_data)->GetValue(System::Int32(nPos)); + map_to_uno( + p, elemData, element_td.get()->pWeakRef, + false /* no assign */); + } + catch (...) + { + // cleanup + for ( sal_Int32 nCleanPos = 0; nCleanPos < nPos; ++nCleanPos ) + { + void * p = + ((uno_Sequence *)seq.get())->elements + + (nCleanPos * element_td.get()->nSize); + uno_destructData( p, element_td.get(), 0 ); + } + throw; + } + } + break; + } + default: + { + throw BridgeRuntimeError("[map_to_uno():" + OUString::unacquired( &type->pTypeName ) + + "] unsupported sequence element type: " + OUString::unacquired( &element_type->pTypeName )); + } + } + } + catch (BridgeRuntimeError& e) + { + throw BridgeRuntimeError("[map_to_uno():" + OUString::unacquired( &type->pTypeName ) + "] conversion failed\n " + e.m_message); + } + catch (System::InvalidCastException^ ) + { + // Ok, checked + throw BridgeRuntimeError("[map_to_uno():" + OUString::unacquired( &type->pTypeName) + + "] could not convert sequence element type: " + OUString::unacquired( &element_type->pTypeName )); + } + catch (...) + { + OSL_ASSERT(0); + throw; + } + __finally + { + if (assign) + uno_destructData( uno_data, td.get(), 0 ); + } + } + else + { + seq = seq_allocate(0, sizeof (sal_Int32)); + } + *(uno_Sequence **)uno_data = (uno_Sequence *)seq.release(); + break; + } + case typelib_TypeClass_INTERFACE: + { + if (assign) + { + uno_Interface * p = *(uno_Interface **)uno_data; + if (0 != p) + (*p->release)( p ); + } + if (nullptr == cli_data) // null-ref + { + *(uno_Interface **)uno_data = 0; + } + else + { + TypeDescr td( type ); + uno_Interface * pUnoI = map_cli2uno(cli_data, td.get()); + *(uno_Interface **)uno_data = pUnoI; + } + break; + } + default: + { + //ToDo check + throw BridgeRuntimeError("[map_to_uno():" + OUString::unacquired( &type->pTypeName ) + "] unsupported type!"); + } + } + } + // BridgeRuntimeError are allowed to be thrown + catch (System::InvalidCastException^ ) + { + //ToDo check + throw BridgeRuntimeError("[map_to_uno():" + OUString::unacquired( &type->pTypeName ) + "] could not convert type!"); + } + catch (System::NullReferenceException ^ e) + { + throw BridgeRuntimeError("[map_to_uno()] Illegal null reference passed!\n" + mapCliString(e->StackTrace)); + } + catch (BridgeRuntimeError& ) + { + throw; + } + catch (...) + { + OSL_ASSERT(0); + throw; + } +} + +/** + @param info + The expected target type. Currently info is provided when this method is called + to convert the in/out and out parameters of a call from cli to uno. Then info + is always a byref type, e.g. "System.String&". info is used for Any and Enum conversion. + @param bDontCreateObj + false - a new object is created which holds the mapped uno value and is assigned to + cli_data. + true - cli_data already contains the newly constructed object. This is the case if + a struct is converted then on the first call to map_to_cli the new object is created. + If the struct inherits another struct then this function is called recursively while the + newly created object is passed in cli_data. + */ +void Bridge::map_to_cli( + System::Object^ *cli_data, void const * uno_data, + typelib_TypeDescriptionReference * type, System::Type^ info, + bool bDontCreateObj) const +{ + switch (type->eTypeClass) + { + case typelib_TypeClass_CHAR: + *cli_data= *(__wchar_t const*)uno_data; + break; + case typelib_TypeClass_BOOLEAN: + *cli_data = (*(bool const*)uno_data) == sal_True; + break; + case typelib_TypeClass_BYTE: + *cli_data = *(unsigned char const*) uno_data; + break; + case typelib_TypeClass_SHORT: + *cli_data= *(short const*) uno_data; + break; + case typelib_TypeClass_UNSIGNED_SHORT: + *cli_data= *(unsigned short const*) uno_data; + break; + case typelib_TypeClass_LONG: + *cli_data= *(int const*) uno_data; + break; + case typelib_TypeClass_UNSIGNED_LONG: + *cli_data= *(unsigned int const*) uno_data; + break; + case typelib_TypeClass_HYPER: + *cli_data= *(__int64 const*) uno_data; + break; + case typelib_TypeClass_UNSIGNED_HYPER: + *cli_data= *(unsigned __int64 const*) uno_data; + break; + case typelib_TypeClass_FLOAT: + *cli_data= *(float const*) uno_data; + break; + case typelib_TypeClass_DOUBLE: + *cli_data= *(double const*) uno_data; + break; + case typelib_TypeClass_STRING: + { + rtl_uString const* sVal= NULL; + sVal= *(rtl_uString* const*) uno_data; + *cli_data= gcnew System::String((__wchar_t*) sVal->buffer,0, sVal->length); + break; + } + case typelib_TypeClass_TYPE: + { + *cli_data= mapUnoType( *(typelib_TypeDescriptionReference * const *)uno_data ); + break; + } + case typelib_TypeClass_ANY: + { + uno_Any const * pAny = (uno_Any const *)uno_data; + if (typelib_TypeClass_VOID != pAny->pType->eTypeClass) + { + System::Object^ objCli= nullptr; + map_to_cli( + &objCli, pAny->pData, pAny->pType, nullptr, + false); + + uno::Any anyVal(mapUnoType(pAny->pType), objCli); + *cli_data= anyVal; + } + else + { // void any + *cli_data= uno::Any::VOID; + } + break; + } + case typelib_TypeClass_ENUM: + { + if (info != nullptr) + { + OSL_ASSERT(info->IsByRef); + info= info->GetElementType(); + *cli_data= System::Enum::ToObject(info, *(System::Int32*) uno_data); + } + else + *cli_data= System::Enum::ToObject( + mapUnoType(type), *(System::Int32*) uno_data); + break; + } + case typelib_TypeClass_STRUCT: + case typelib_TypeClass_EXCEPTION: + { + TypeDescr td( type ); + typelib_CompoundTypeDescription * comp_td = + (typelib_CompoundTypeDescription *) td.get(); + if ( ! ((typelib_TypeDescription*) comp_td)->bComplete) + ::typelib_typedescription_complete( + (typelib_TypeDescription**) & comp_td ); + + + //create the type + System::Type^ cliType= loadCliType(td.get()->pTypeName); + //detect if we recursively convert inherited structures + //If this point is reached because of a recursive call during covering a + //struct then we must not create a new object rather we use the one in + // cli_data argument. + System::Object^ cliObj; + if (bDontCreateObj) + cliObj = *cli_data; // recursive call + else + { + //Special handling for Exception conversion. We must call constructor System::Exception + //to pass the message string + if (ucss::uno::Exception::typeid->IsAssignableFrom(cliType)) + { + //We need to get the Message field. Therefore we must obtain the offset from + //the typedescription. The base interface of all exceptions is + //com::sun::star::uno::Exception which contains the message + typelib_CompoundTypeDescription* pCTD = comp_td; + while (pCTD->pBaseTypeDescription) + pCTD = pCTD->pBaseTypeDescription; + int nPos = -1; + + OUString usMessageMember("Message"); + for (int i = 0; i < pCTD->nMembers; i ++) + { + if (usMessageMember.equals(pCTD->ppMemberNames[i])) + { + nPos = i; + break; + } + } + OSL_ASSERT (nPos != -1); + int offset = pCTD->pMemberOffsets[nPos]; + //With the offset within the exception we can get the message string + System::String^ sMessage = mapUnoString(*(rtl_uString**) + ((char*) uno_data + offset)); + //We need to find a constructor for the exception that takes the message string + //We assume that the first argument is the message string + cli::array<sr::ConstructorInfo^>^ arCtorInfo = cliType->GetConstructors(); + sr::ConstructorInfo^ ctorInfo = nullptr; + int numCtors = arCtorInfo->Length; + //Constructor must at least have 2 params for the base + //unoidl.com.sun.star.uno.Exception (String, Object); + cli::array<sr::ParameterInfo^>^ arParamInfo; + for (int i = 0; i < numCtors; i++) + { + arParamInfo = arCtorInfo[i]->GetParameters(); + if (arParamInfo->Length < 2) + continue; + ctorInfo = arCtorInfo[i]; + break; + } + OSL_ASSERT(arParamInfo[0]->ParameterType->Equals(System::String::typeid) + && arParamInfo[1]->ParameterType->Equals(System::Object::typeid) + && arParamInfo[0]->Position == 0 + && arParamInfo[1]->Position == 1); + //Prepare parameters for constructor + int numArgs = arParamInfo->Length; + cli::array<System::Object^>^ args = gcnew cli::array<System::Object^>(numArgs); + //only initialize the first argument with the message + args[0] = sMessage; + cliObj = ctorInfo->Invoke(args); + } + else + cliObj = System::Activator::CreateInstance(cliType); + } + sal_Int32 * pMemberOffsets = comp_td->pMemberOffsets; + + if (comp_td->pBaseTypeDescription) + { + //convert inherited struct + //cliObj is passed inout (args in_param, out_param are true), hence the passed + // cliObj is used by the callee instead of a newly created struct + map_to_cli( + &cliObj, uno_data, + ((typelib_TypeDescription *)comp_td->pBaseTypeDescription)->pWeakRef, nullptr, + true); + } + OUString usUnoException("com.sun.star.uno.Exception"); + for (sal_Int32 nPos = comp_td->nMembers; nPos--; ) + { + typelib_TypeDescriptionReference * member_type = comp_td->ppTypeRefs[ nPos ]; + System::String^ sMemberName= mapUnoString(comp_td->ppMemberNames[nPos]); + sr::FieldInfo^ aField= cliType->GetField(sMemberName); + // special case for Exception.Message. The field has already been + // set while constructing cli object + if ( ! aField && usUnoException.equals(td.get()->pTypeName)) + { + continue; + } + void const * p = (char const *)uno_data + pMemberOffsets[ nPos ]; + switch (member_type->eTypeClass) + { + case typelib_TypeClass_CHAR: + aField->SetValue(cliObj, *(System::Char*) p); + break; + case typelib_TypeClass_BOOLEAN: + aField->SetValue(cliObj, *(System::Boolean*) p); + break; + case typelib_TypeClass_BYTE: + aField->SetValue(cliObj, *(System::Byte*) p); + break; + case typelib_TypeClass_SHORT: + aField->SetValue(cliObj, *(System::Int16*) p); + break; + case typelib_TypeClass_UNSIGNED_SHORT: + aField->SetValue(cliObj, *(System::UInt16*) p); + break; + case typelib_TypeClass_LONG: + aField->SetValue(cliObj, *(System::Int32*) p); + break; + case typelib_TypeClass_UNSIGNED_LONG: + aField->SetValue(cliObj, *(System::UInt32*) p); + break; + case typelib_TypeClass_HYPER: + aField->SetValue(cliObj, *(System::Int64*) p); + break; + case typelib_TypeClass_UNSIGNED_HYPER: + aField->SetValue(cliObj, *(System::UInt64*) p); + break; + case typelib_TypeClass_FLOAT: + aField->SetValue(cliObj, *(System::Single*) p); + break; + case typelib_TypeClass_DOUBLE: + aField->SetValue(cliObj, *(System::Double*) p); + break; + default: + { + System::Object^ cli_val; + map_to_cli( + &cli_val, p, member_type, nullptr, + false); + aField->SetValue(cliObj, cli_val); + break; + } + } + } + *cli_data= cliObj; + break; + } + case typelib_TypeClass_SEQUENCE: + { + sal_Int32 nElements; + uno_Sequence const * seq = 0; + seq = *(uno_Sequence * const *)uno_data; + nElements = seq->nElements; + + TypeDescr td( type ); + typelib_TypeDescriptionReference * element_type = + ((typelib_IndirectTypeDescription *)td.get())->pType; + + switch (element_type->eTypeClass) + { + case typelib_TypeClass_CHAR: + { + cli::array<System::Char>^ arChar= gcnew cli::array<System::Char>(nElements); + sri::Marshal::Copy( IntPtr((void*) &seq->elements), arChar, 0, nElements); + *cli_data= arChar; + break; + } + case typelib_TypeClass_BOOLEAN: + { + cli::array<System::Byte>^ arBool= gcnew cli::array<System::Byte>(nElements); + sri::Marshal::Copy( IntPtr((void*) &seq->elements), arBool, 0, nElements); + *cli_data= dynamic_cast<cli::array<System::Boolean>^>(arBool); + break; + } + case typelib_TypeClass_BYTE: + { + cli::array<System::Byte>^ arByte= gcnew cli::array<System::Byte>(nElements); + sri::Marshal::Copy( IntPtr((void*) &seq->elements), arByte, 0, nElements); + *cli_data= arByte; + break; + } + case typelib_TypeClass_SHORT: + { + cli::array<System::Int16>^ arShort= gcnew cli::array<System::Int16>(nElements); + sri::Marshal::Copy( IntPtr((void*) &seq->elements), arShort, 0, nElements); + *cli_data= arShort; + break; + } + case typelib_TypeClass_UNSIGNED_SHORT: + { + cli::array<System::UInt16>^ arUInt16= gcnew cli::array<System::UInt16>(nElements); + sri::Marshal::Copy( IntPtr((void*) &seq->elements), dynamic_cast<cli::array<System::Int16>^>(arUInt16), + 0, nElements); + *cli_data= arUInt16; + break; + } + case typelib_TypeClass_LONG: + { + cli::array<System::Int32>^ arInt32= gcnew cli::array<System::Int32>(nElements); + sri::Marshal::Copy( IntPtr((void*) &seq->elements), arInt32, 0, nElements); + *cli_data= arInt32; + break; + } + case typelib_TypeClass_UNSIGNED_LONG: + { + cli::array<System::UInt32>^ arUInt32= gcnew cli::array<System::UInt32>(nElements); + sri::Marshal::Copy( IntPtr((void*) &seq->elements), dynamic_cast<cli::array<System::Int32>^>(arUInt32), + 0, nElements); + *cli_data= arUInt32; + break; + } + case typelib_TypeClass_HYPER: + { + cli::array<System::Int64>^ arInt64= gcnew cli::array<System::Int64>(nElements); + sri::Marshal::Copy( IntPtr((void*) &seq->elements), arInt64, 0, nElements); + *cli_data= arInt64; + break; + } + //FIXME: Marshal::Copy of UInt64? + case typelib_TypeClass_UNSIGNED_HYPER: + { + cli::array<System::IntPtr>^ arUInt64= gcnew cli::array<System::IntPtr>(nElements); + sri::Marshal::Copy( IntPtr((void*) &seq->elements), arUInt64, 0, nElements); + *cli_data= dynamic_cast<cli::array<System::UInt64>^>(arUInt64); + break; + } + case typelib_TypeClass_FLOAT: + { + cli::array<System::Single>^ arSingle= gcnew cli::array<System::Single>(nElements); + sri::Marshal::Copy( IntPtr((void*) &seq->elements), arSingle, 0, nElements); + *cli_data= arSingle; + break; + } + case typelib_TypeClass_DOUBLE: + { + cli::array<System::Double>^ arDouble= gcnew cli::array<System::Double>(nElements); + sri::Marshal::Copy( IntPtr((void*) &seq->elements), arDouble, 0, nElements); + *cli_data= arDouble; + break; + } + case typelib_TypeClass_STRING: + { + cli::array<System::String^>^ arString= gcnew cli::array<System::String^>(nElements); + for (int i= 0; i < nElements; i++) + { + rtl_uString *aStr= ((rtl_uString**)(&seq->elements))[i]; + arString[i]= gcnew System::String( (__wchar_t *) &aStr->buffer, 0, aStr->length); + } + *cli_data= arString; + break; + } + case typelib_TypeClass_TYPE: + { + cli::array<System::Type^>^ arType= gcnew cli::array<System::Type^>(nElements); + for (int i= 0; i < nElements; i++) + { + arType[i]= + mapUnoType( ((typelib_TypeDescriptionReference**) seq->elements)[i]); + } + *cli_data= arType; + break; + } + case typelib_TypeClass_ANY: + { + cli::array<uno::Any>^ arCli= gcnew cli::array<uno::Any>(nElements); + uno_Any const * p = (uno_Any const *)seq->elements; + for (sal_Int32 nPos = 0; nPos < nElements; ++nPos ) + { + System::Object^ cli_obj = nullptr; + map_to_cli( + &cli_obj, &p[ nPos ], element_type, nullptr, false); + arCli[nPos]= *safe_cast<uno::Any^>(cli_obj); + } + *cli_data= arCli; + break; + } + case typelib_TypeClass_ENUM: + { + //get the Enum type + System::Type^ enumType= nullptr; + if (info != nullptr) + { + //info is EnumType[]&, remove & + OSL_ASSERT(info->IsByRef); + enumType = info->GetElementType(); + //enumType is EnumType[], remove [] + enumType = enumType->GetElementType(); + } + else + enumType= mapUnoType(element_type); + + System::Array^ arEnum = System::Array::CreateInstance( + enumType, nElements); + for (int i= 0; i < nElements; i++) + { + arEnum->SetValue(System::Enum::ToObject(enumType, + System::Int32(((sal_Int32*) seq->elements)[i])), i); + } + *cli_data = arEnum; + break; + } + case typelib_TypeClass_STRUCT: + case typelib_TypeClass_EXCEPTION: + { + TypeDescr element_td( element_type ); + System::Array^ ar= System::Array::CreateInstance( + mapUnoType(element_type),nElements); + if (0 < nElements) + { + // ToDo check this + char * p = (char *) &seq->elements; + sal_Int32 nSize = element_td.get()->nSize; + for ( sal_Int32 nPos = 0; nPos < nElements; ++nPos ) + { + System::Object^ val; + map_to_cli( + &val, p + (nSize * nPos), element_type, nullptr, false); + ar->SetValue(val, System::Int32(nPos)); + } + } + *cli_data = ar; + break; + } +// ToDo, verify + case typelib_TypeClass_SEQUENCE: + { + System::Array ^ar= System::Array::CreateInstance( + mapUnoType(element_type), nElements); + if (0 < nElements) + { + TypeDescr element_td( element_type ); + uno_Sequence ** elements = (uno_Sequence**) seq->elements; + for ( sal_Int32 nPos = 0; nPos < nElements; ++nPos ) + { + System::Object^ val; + map_to_cli( + &val, &elements[nPos], element_type, nullptr, false); + ar->SetValue(val, System::Int32(nPos)); + } + } + *cli_data = ar; + break; + } + case typelib_TypeClass_INTERFACE: + { + TypeDescr element_td( element_type ); + System::Type ^ ifaceType= mapUnoType(element_type); + System::Array^ ar= System::Array::CreateInstance(ifaceType, nElements); + + char * p = (char *)seq->elements; + sal_Int32 nSize = element_td.get()->nSize; + for ( sal_Int32 nPos = 0; nPos < nElements; ++nPos ) + { + System::Object^ val; + map_to_cli( + &val, p + (nSize * nPos), element_type, nullptr, false); + + ar->SetValue(val, System::Int32(nPos)); + } + *cli_data= ar; + break; + } + default: + { + throw BridgeRuntimeError("[map_to_cli():" + OUString::unacquired( &type->pTypeName ) + + "] unsupported element type: " + OUString::unacquired( &element_type->pTypeName )); + } + } + break; + } + case typelib_TypeClass_INTERFACE: + { + uno_Interface * pUnoI = *(uno_Interface * const *)uno_data; + if (0 != pUnoI) + { + TypeDescr td( type ); + *cli_data= map_uno2cli( pUnoI, reinterpret_cast< + typelib_InterfaceTypeDescription*>(td.get())) ; + } + else + *cli_data= nullptr; + break; + } + default: + { + //ToDo check this exception. The String is probably crippled + throw BridgeRuntimeError("[map_to_cli():" + OUString::unacquired( &type->pTypeName ) + "] unsupported type!"); + } + } //switch +} // method +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cli_ure/source/uno_bridge/cli_environment.cxx b/cli_ure/source/uno_bridge/cli_environment.cxx new file mode 100644 index 000000000..3c054b5be --- /dev/null +++ b/cli_ure/source/uno_bridge/cli_environment.cxx @@ -0,0 +1,168 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "Cli_environment.h" + +#using <cli_ure.dll> +#using <system.dll> +#include "osl/diagnose.h" +#include "cli_proxy.h" + +using namespace System::Runtime::Remoting; +using namespace System::Runtime::Remoting::Proxies; +using namespace System::Collections; +using namespace System::Text; +using namespace System::Diagnostics; +using namespace System::Threading; + +namespace cli_uno +{ + +inline System::String^ Cli_environment::createKey(System::String^ oid, System::Type^ t) +{ + return System::String::Concat(oid, t->FullName); +} + + +inline Cli_environment::Cli_environment() +{ +#if OSL_DEBUG_LEVEL >= 2 + _numRegisteredObjects = 0; +#endif +} + +Cli_environment::~Cli_environment() ///< IDisposable Cli_environment::Dispose() +{ + this->!Cli_environment(); // call finalizer +} + +Cli_environment::!Cli_environment() ///< Cli_environment::Finalize() +{ +#if OSL_DEBUG_LEVEL >= 2 + OSL_ENSURE(_numRegisteredObjects == 0, + "cli uno bridge: CLI environment contains unrevoked objects"); +#endif +} + +System::Object^ Cli_environment::registerInterface( + System::Object^ obj, System::String^ oid) +{ +#if OSL_DEBUG_LEVEL >= 1 + //obj must be a transparent proxy + OSL_ASSERT(RemotingServices::IsTransparentProxy(obj)); + _numRegisteredObjects ++; +#endif + OSL_ASSERT( ! m_objects->ContainsKey(oid)); + m_objects->Add(oid, gcnew WeakReference(obj)); + return obj; +} +System::Object^ Cli_environment::registerInterface ( + System::Object^ obj, System::String^ oid, System::Type^ type) +{ +#if OSL_DEBUG_LEVEL >= 1 + //obj must be a real cli object + OSL_ASSERT( ! RemotingServices::IsTransparentProxy(obj)); + _numRegisteredObjects ++; +#endif + System::String^ key = createKey(oid, type); + //see synchronization in map_uno2cli in cli_data.cxx + OSL_ASSERT( ! m_objects->ContainsKey(key)); + m_objects->Add(key, gcnew WeakReference(obj)); + return obj; +} + +void Cli_environment::revokeInterface(System::String^ oid, System::Type^ type) +{ + System::String^ key = type != nullptr ? createKey(oid, type) : oid; +#if OSL_DEBUG_LEVEL >= 1 + _numRegisteredObjects --; +#endif +#if OSL_DEBUG_LEVEL >= 2 + int i = 1; + if (m_objects->ContainsKey(key) == false) + { + Trace::WriteLine("cli uno bridge: try to revoke unregistered interface"); + Trace::WriteLine(oid); + i = 0; + } + Trace::WriteLine(System::String::Format( + gcnew System::String("cli uno bridge: {0} remaining registered interfaces"), + m_objects->Count - 1)); +#endif + m_objects->Remove(key); +} + +inline void Cli_environment::revokeInterface(System::String^ oid) +{ + return revokeInterface(oid, nullptr); +} + +System::Object^ Cli_environment::getRegisteredInterface(System::String^ oid, + System::Type^ type) +{ + //try if it is a UNO interface + System::Object^ ret = m_objects[oid]; + if (! ret) + { + //try if it is a proxy for a cli object + oid = createKey(oid, type); + ret = m_objects[ oid ]; + } + if (ret != nullptr) + { + System::WeakReference^ weakIface = + static_cast< System::WeakReference ^ >( ret ); + ret = weakIface->Target; + } + if (ret == nullptr) + m_objects->Remove( oid ); + return ret; +} + +System::String^ Cli_environment::getObjectIdentifier(System::Object^ obj) +{ + System::String^ oId= nullptr; + RealProxy^ aProxy= RemotingServices::GetRealProxy(obj); + if (aProxy) + { + UnoInterfaceProxy^ proxyImpl= dynamic_cast<UnoInterfaceProxy^>(aProxy); + if (proxyImpl) + oId= proxyImpl->getOid(); + } + + if (oId == nullptr) + { + StringBuilder ^ buf= gcnew StringBuilder(256); + bool bFirst = new bool(false); + System::Threading::Monitor::Enter(Cli_environment::typeid); + try { + buf->Append(m_IDGen->GetId(obj, bFirst)); + } finally + { + System::Threading::Monitor::Exit(Cli_environment::typeid); + } + + buf->Append(sOidPart); + oId= buf->ToString(); + } + return oId; +} +} //namespace cli_uno + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cli_ure/source/uno_bridge/cli_environment.h b/cli_ure/source/uno_bridge/cli_environment.h new file mode 100644 index 000000000..3828d971c --- /dev/null +++ b/cli_ure/source/uno_bridge/cli_environment.h @@ -0,0 +1,105 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CLI_URE_SOURCE_UNO_BRIDGE_CLI_ENVIRONMENT_H +#define INCLUDED_CLI_URE_SOURCE_UNO_BRIDGE_CLI_ENVIRONMENT_H + +#include "cli_base.h" + +using namespace System; +using namespace System::Collections; +using namespace System::Runtime::Serialization; + +namespace cli_uno +{ +public +ref class Cli_environment +{ + static System::String ^ sOidPart; + static Hashtable ^ m_objects; + static System::Runtime::Serialization::ObjectIDGenerator ^ m_IDGen; + inline static System::String ^ createKey(System::String ^ oid, System::Type ^ t); + +#if OSL_DEBUG_LEVEL >= 1 + int _numRegisteredObjects; +#endif + + static Cli_environment() + { + m_objects = Hashtable::Synchronized(gcnew Hashtable()); + m_IDGen = gcnew System::Runtime::Serialization::ObjectIDGenerator(); + System::Text::StringBuilder ^ buffer = gcnew System::Text::StringBuilder(256); + Guid gd = Guid::NewGuid(); + buffer->Append(";cli[0];"); + buffer->Append(gd.ToString()); + sOidPart = buffer->ToString(); + } + +public: + inline Cli_environment(); + + ~Cli_environment(); + !Cli_environment(); + + /** + Registers a UNO object as being mapped by this bridge. The resulting + cli object is represents all interfaces of the UNO object. Therefore the + object can be registered only with its OID; a type is not necessary. + */ + Object ^ registerInterface(Object ^ obj, System::String ^ oid); + /** + Registers a CLI object as being mapped by this bridge. The resulting + object represents exactly one UNO interface. + */ + Object ^ registerInterface(Object ^ obj, System::String ^ oid, System::Type ^ type); + + /** + By revoking an interface it is declared that the respective interface has + not been mapped. The proxy implementations call revoke interface in their + destructors. + */ + inline void revokeInterface(System::String ^ oid); + + void revokeInterface(System::String ^ oid, System::Type ^ type); + /** + * Retrieves an interface identified by its object id and type from this + * environment. + * + * @param oid object id of interface to be retrieved + * @param type the type description of the interface to be retrieved + * @see com.sun.star.uno.IEnvironment#getRegisteredInterface + */ + Object ^ getRegisteredInterface(System::String ^ oid, System::Type ^ type); + + /** + * Generates a worldwide unique object identifier (oid) for the given object. It is + * guaranteed, that subsequent calls to the method with the same object + * will give the same id. + * <p> + * @return the generated oid. + * @param object the object for which an Oid should be generated. + */ + static System::String ^ getObjectIdentifier(Object ^ obj); +}; + +} //namespace cli_uno + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cli_ure/source/uno_bridge/cli_proxy.cxx b/cli_ure/source/uno_bridge/cli_proxy.cxx new file mode 100644 index 000000000..22c478177 --- /dev/null +++ b/cli_ure/source/uno_bridge/cli_proxy.cxx @@ -0,0 +1,1105 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "typelib/typedescription.h" +#include "rtl/ustrbuf.hxx" +#include <sal/log.hxx> +#include "com/sun/star/uno/RuntimeException.hpp" +#include "cli_proxy.h" +#include "cli_base.h" +#include "cli_bridge.h" + +#using <cli_ure.dll> +#using <cli_uretypes.dll> + +namespace sr = System::Reflection; +namespace st = System::Text; +namespace sc = System::Collections; +namespace srrm = System::Runtime::Remoting::Messaging; +namespace srr = System::Runtime::Remoting; +namespace srrp = System::Runtime::Remoting::Proxies; +namespace sd = System::Diagnostics; +namespace ucss = unoidl::com::sun::star; + +using namespace cli_uno; + +extern "C" +{ + +void SAL_CALL cli_proxy_free( uno_ExtEnvironment * env, void * proxy ) + SAL_THROW_EXTERN_C(); + +void SAL_CALL cli_proxy_acquire( uno_Interface * pUnoI ) + SAL_THROW_EXTERN_C(); + +void SAL_CALL cli_proxy_release( uno_Interface * pUnoI ) + SAL_THROW_EXTERN_C(); + +void SAL_CALL cli_proxy_dispatch( + uno_Interface * pUnoI, typelib_TypeDescription const * member_td, + void * uno_ret, void * uno_args[], uno_Any ** uno_exc ) + SAL_THROW_EXTERN_C(); + + +} + +namespace cli_uno +{ + +UnoInterfaceInfo::UnoInterfaceInfo(Bridge const * bridge, uno_Interface* unoI, + typelib_InterfaceTypeDescription* td): + + m_unoI(unoI), + m_typeDesc(td), + m_bridge(bridge) +{ + m_bridge->acquire(); + m_type = mapUnoType(reinterpret_cast<typelib_TypeDescription*>(td)); + m_unoI->acquire(m_unoI); + typelib_typedescription_acquire(&m_typeDesc->aBase); + if ( ! m_typeDesc->aBase.bComplete) + { + typelib_TypeDescription* _pt = &m_typeDesc->aBase; + sal_Bool bComplete = ::typelib_typedescription_complete( & _pt); + if( ! bComplete) + { + throw BridgeRuntimeError("cannot make type complete: " + OUString::unacquired(& m_typeDesc->aBase.pTypeName)); + } + } +} + +UnoInterfaceInfo::~UnoInterfaceInfo() ///< IDisposable UnoInterfaceInfo::Dispose() +{ + this->!UnoInterfaceInfo(); // call finalizer +} + +UnoInterfaceInfo::!UnoInterfaceInfo() ///< UnoInterfaceInfo::Finalize() +{ + //accessing unmanaged objects is ok. + m_bridge->m_uno_env->revokeInterface( + m_bridge->m_uno_env, m_unoI ); + m_bridge->release(); + + m_unoI->release(m_unoI); + typelib_typedescription_release( + reinterpret_cast<typelib_TypeDescription*>(m_typeDesc)); +} + +UnoInterfaceProxy::UnoInterfaceProxy( + Bridge * bridge, + uno_Interface * pUnoI, + typelib_InterfaceTypeDescription* pTD, + const OUString& oid ) + :RealProxy(MarshalByRefObject::typeid), + m_bridge(bridge), + m_oid(mapUnoString(oid.pData)), + m_sTypeName(m_system_Object_String) +{ + m_bridge->acquire(); + // create the list that holds all UnoInterfaceInfos + m_listIfaces = gcnew ArrayList(10); + m_numUnoIfaces = 0; + m_listAdditionalProxies = gcnew ArrayList(); + m_nlistAdditionalProxies = 0; + //put the information of the first UNO interface into the arraylist +#if OSL_DEBUG_LEVEL >= 2 + _numInterfaces = 0; + _sInterfaces = NULL; +#endif + addUnoInterface(pUnoI, pTD); + +} + +UnoInterfaceProxy::~UnoInterfaceProxy() ///< IDisposable UnoInterfaceProxy::Dispose() +{ + this->!UnoInterfaceProxy(); // call finalizer +} + +UnoInterfaceProxy::!UnoInterfaceProxy() ///< UnoInterfaceProxy::Finalize() +{ +#if OSL_DEBUG_LEVEL >= 2 + sd::Trace::WriteLine(System::String::Format( + gcnew System::String("cli uno bridge: Destroying proxy " + "for UNO object, OID: \n\t{0} \n\twith uno interfaces: "), + m_oid)); + + sd::Trace::WriteLine( mapUnoString(_sInterfaces)); + rtl_uString_release(_sInterfaces); +#endif + //m_bridge is unmanaged, therefore we can access it in this finalizer + CliEnvHolder::g_cli_env->revokeInterface(m_oid); + m_bridge->release(); +} + +System::Object^ UnoInterfaceProxy::create( + Bridge * bridge, + uno_Interface * pUnoI, + typelib_InterfaceTypeDescription* pTD, + const OUString& oid) +{ + UnoInterfaceProxy^ proxyHandler= + gcnew UnoInterfaceProxy(bridge, pUnoI, pTD, oid); + System::Object^ proxy= proxyHandler->GetTransparentProxy(); + CliEnvHolder::g_cli_env->registerInterface(proxy, mapUnoString(oid.pData)); + return proxy; +} + + +void UnoInterfaceProxy::addUnoInterface(uno_Interface* pUnoI, + typelib_InterfaceTypeDescription* pTd) +{ + sc::IEnumerator^ enumInfos = m_listIfaces->GetEnumerator(); + System::Threading::Monitor::Enter(this); + try + { + while (enumInfos->MoveNext()) + { + UnoInterfaceInfo^ info = static_cast<UnoInterfaceInfo^>( + enumInfos->Current); + if (typelib_typedescription_equals( + reinterpret_cast<typelib_TypeDescription*>(info->m_typeDesc), + reinterpret_cast<typelib_TypeDescription*>(pTd))) + { + return; + } + } + OUString oid(mapCliString(m_oid)); + (*m_bridge->m_uno_env->registerInterface)( + m_bridge->m_uno_env, reinterpret_cast< void ** >( &pUnoI ), + oid.pData, pTd); + //This proxy does not contain the uno_Interface. Add it. + m_listIfaces->Add(gcnew UnoInterfaceInfo(m_bridge, pUnoI, pTd)); + m_numUnoIfaces = m_listIfaces->Count; +#if OSL_DEBUG_LEVEL >= 2 + System::String^ sInterfaceName = static_cast<UnoInterfaceInfo^>( + m_listIfaces[m_numUnoIfaces - 1])->m_type->FullName; + sd::Trace::WriteLine(System::String::Format( + gcnew System::String("cli uno bridge: Creating proxy for uno object, " + "id:\n\t{0}\n\t{1}"), m_oid, sInterfaceName)); + // add to the string that contains all interface names + _numInterfaces++; + OUString _sNewInterface = "\t" + OUString::number(_numInterfaces) + ". " + mapCliString(sInterfaceName) + "\n"; + pin_ptr<rtl_uString *> pp_sInterfaces = &_sInterfaces; + rtl_uString_newConcat( pp_sInterfaces, * pp_sInterfaces, + _sNewInterface.pData); +#endif + } + __finally { + System::Threading::Monitor::Exit(this); + } +} + + +// IRemotingTypeInfo +bool UnoInterfaceProxy::CanCastTo(System::Type^ fromType, + System::Object^) +{ + if (fromType == System::Object::typeid) // trivial case + return true; + + System::Threading::Monitor::Enter(this); + try + { + if (nullptr != findInfo( fromType )) // proxy supports demanded interface + return true; + + //query a uno interface for the required type + + // we use the first interface in the list (m_listIfaces) to make + // the queryInterface call + UnoInterfaceInfo^ info = + static_cast<UnoInterfaceInfo^>(m_listIfaces[0]); + css::uno::TypeDescription membertd( + reinterpret_cast<typelib_InterfaceTypeDescription*>( + info->m_typeDesc)->ppAllMembers[0]); + array<System::Object^>^ args = gcnew array<System::Object^>(1); + + args[0] = fromType; + uno::Any ^ pAny; + System::Object^ pException = nullptr; + + pAny= static_cast<uno::Any ^>( + m_bridge->call_uno( + info->m_unoI, + membertd.get(), + ((typelib_InterfaceMethodTypeDescription*) + membertd.get())->pReturnTypeRef, + 1, + ((typelib_InterfaceMethodTypeDescription*) + membertd.get())->pParams, + args, nullptr, &pException) ); + + // handle regular exception from target + OSL_ENSURE( + nullptr == pException, + OUStringToOString( + mapCliString( pException->ToString()), + RTL_TEXTENCODING_UTF8 ).getStr() ); + + if (pAny->Type != void::typeid) // has value? + { + if (nullptr != findInfo( fromType )) + { + // proxy now supports demanded interface + return true; + } + + // via aggregation: it is possible that queryInterface() returns + // and interface with a different oid. + // That way, this type is supported for the CLI + // interpreter (CanCastTo() returns true) + ::System::Object ^ obj = pAny->Value; + OSL_ASSERT( srr::RemotingServices::IsTransparentProxy( obj ) ); + if (srr::RemotingServices::IsTransparentProxy( obj )) + { + UnoInterfaceProxy ^ proxy = + static_cast< UnoInterfaceProxy ^ >( + srr::RemotingServices::GetRealProxy( obj ) ); + OSL_ASSERT( nullptr != proxy->findInfo( fromType ) ); + m_listAdditionalProxies->Add( proxy ); + m_nlistAdditionalProxies = m_listAdditionalProxies->Count; + OSL_ASSERT( nullptr != findInfo( fromType ) ); + return true; + } + } + } + catch (BridgeRuntimeError& e) + { + (void) e; // avoid warning + OSL_FAIL( + OUStringToOString( + e.m_message, RTL_TEXTENCODING_UTF8 ).getStr() ); + } + catch (System::Exception^ e) + { + System::String^ msg= gcnew System::String( + "An unexpected CLI exception occurred in " + "UnoInterfaceProxy::CanCastTo(). Original" + "message: \n"); + msg= System::String::Concat(msg, e->Message); + OSL_FAIL( + OUStringToOString( + mapCliString(msg), RTL_TEXTENCODING_UTF8 ).getStr() ); + } + catch (...) + { + OSL_FAIL( + "An unexpected native C++ exception occurred in " + "UnoInterfaceProxy::CanCastTo()" ); + } + __finally + { + System::Threading::Monitor::Exit(this); + } + return false; +} + +srrm::IMessage^ UnoInterfaceProxy::invokeObject( + sc::IDictionary^ props, + srrm::LogicalCallContext^ context, + srrm::IMethodCallMessage^ mcm) +{ + System::Object^ retMethod = nullptr; + System::String^ sMethod = static_cast<System::String^> + (props[m_methodNameString]); + array<System::Object^>^ args = static_cast<array<System::Object^>^>( + props[m_ArgsString]); + if (m_Equals_String->Equals(sMethod)) + { + // Object.Equals + OSL_ASSERT(args->Length == 1); + srrp::RealProxy^ rProxy = srr::RemotingServices::GetRealProxy(args[0]); + bool bDone = false; + if (rProxy) + { + UnoInterfaceProxy^ unoProxy = + dynamic_cast<UnoInterfaceProxy^>(rProxy); + if (unoProxy) + { + bool b = m_oid->Equals(unoProxy->getOid()); + retMethod = b; + bDone = true; + } + } + if (bDone == false) + { + //no proxy or not our proxy, therefore Equals must be false + retMethod = false; + } + } + else if (m_GetHashCode_String->Equals(sMethod)) + { + // Object.GetHashCode + int nHash = m_oid->GetHashCode(); + retMethod = nHash; + } + else if (m_GetType_String->Equals(sMethod)) + { + // Object.GetType + retMethod = System::Object::typeid; + } + else if (m_ToString_String->Equals(sMethod)) + { + // Object.ToString + st::StringBuilder^ sb = gcnew st::StringBuilder(256); +// sb->AppendFormat("Uno object proxy. Implemented interface: {0}" +// ". OID: {1}", m_type->ToString(), m_oid); + sb->AppendFormat("Uno object proxy. OID: {0}", m_oid); + retMethod = sb->ToString(); + } + else + { + //Either Object has new functions or a protected method was called + //which should not be possible + OSL_ASSERT(0); + } + srrm::IMessage^ retVal= gcnew srrm::ReturnMessage( + retMethod, gcnew array<System::Object^>(0), 0, context, mcm); + return retVal; +} + +UnoInterfaceInfo ^ UnoInterfaceProxy::findInfo( ::System::Type ^ type ) +{ + for (int i = 0; i < m_numUnoIfaces; i++) + { + UnoInterfaceInfo^ tmpInfo = static_cast<UnoInterfaceInfo^>( + m_listIfaces[i]); + if (type->IsAssignableFrom(tmpInfo->m_type)) + return tmpInfo; + } + for ( int i = 0; i < m_nlistAdditionalProxies; ++i ) + { + UnoInterfaceProxy ^ proxy = + static_cast< UnoInterfaceProxy ^ >( + m_listAdditionalProxies[ i ] ); + UnoInterfaceInfo ^ info = proxy->findInfo( type ); + if (nullptr != info) + return info; + } + return nullptr; +} + +srrm::IMessage^ UnoInterfaceProxy::Invoke(srrm::IMessage^ callmsg) +{ + try + { + sc::IDictionary^ props= callmsg->Properties; + srrm::LogicalCallContext^ context= + static_cast<srrm::LogicalCallContext^>( + props[m_CallContextString]); + srrm::IMethodCallMessage^ mcm= + static_cast<srrm::IMethodCallMessage^>(callmsg); + + //Find out which UNO interface is being called + System::String^ sTypeName = static_cast<System::String^>( + props[m_typeNameString]); + sTypeName = sTypeName->Substring(0, sTypeName->IndexOf(',')); + + // Special Handling for System.Object methods + if(sTypeName->IndexOf(m_system_Object_String) != -1) + { + return invokeObject(props, context, mcm); + } + + System::Type^ typeBeingCalled = loadCliType(sTypeName); + UnoInterfaceInfo^ info = findInfo( typeBeingCalled ); + OSL_ASSERT( nullptr != info ); + + // ToDo do without string conversion, an OUString is not needed here + // get the type description of the call + OUString usMethodName(mapCliString(static_cast<System::String^>( + props[m_methodNameString]))); + typelib_TypeDescriptionReference ** ppAllMembers = + info->m_typeDesc->ppAllMembers; + sal_Int32 numberMembers = info->m_typeDesc->nAllMembers; + for ( sal_Int32 nPos = numberMembers; nPos--; ) + { + typelib_TypeDescriptionReference * member_type = ppAllMembers[nPos]; + + // check usMethodName against fully qualified usTypeName + // of member_type; usTypeName is of the form + // <name> "::" <usMethodName> *(":@" <idx> "," <idx> ":" <name>) + OUString const & usTypeName = + OUString::unacquired( & member_type->pTypeName ); + +#if OSL_DEBUG_LEVEL >= 2 + System::String^ pTypeName; + pTypeName = mapUnoString(usTypeName.pData); +#endif + sal_Int32 offset = usTypeName.indexOf( ':' ) + 2; + OSL_ASSERT( + offset >= 2 && offset < usTypeName.getLength() + && usTypeName[offset - 1] == ':' ); + sal_Int32 remainder = usTypeName.getLength() - offset; + + if (typelib_TypeClass_INTERFACE_METHOD == member_type->eTypeClass) + { + if ((usMethodName.getLength() == remainder + || (usMethodName.getLength() < remainder + && usTypeName[offset + usMethodName.getLength()] == ':')) + && usTypeName.match(usMethodName, offset)) + { + TypeDescr member_td( member_type ); + typelib_InterfaceMethodTypeDescription * method_td = + (typelib_InterfaceMethodTypeDescription *) + member_td.get(); + + array<System::Object^>^ args = static_cast<array<System::Object^>^>( + props[m_ArgsString]); + array<System::Type^>^ argTypes = static_cast<array<System::Type^>^>( + props[m_methodSignatureString]); + System::Object^ pExc = nullptr; + System::Object ^ cli_ret = m_bridge->call_uno( + info->m_unoI, member_td.get(), + method_td->pReturnTypeRef, method_td->nParams, + method_td->pParams, args, argTypes, &pExc); + return constructReturnMessage(cli_ret, args, method_td, + callmsg, pExc); + } + } + else + { + OSL_ASSERT( typelib_TypeClass_INTERFACE_ATTRIBUTE == + member_type->eTypeClass ); + if (usMethodName.getLength() > 4 + && (usMethodName.getLength() - 4 == remainder + || (usMethodName.getLength() - 4 < remainder + && usTypeName[ + offset + (usMethodName.getLength() - 4)] == ':')) + && usMethodName[1] == 'e' && usMethodName[2] == 't' + && rtl_ustr_compare_WithLength( + usTypeName.getStr() + offset, + usMethodName.getLength() - 4, + usMethodName.getStr() + 4, + usMethodName.getLength() - 4) == 0) + { + if ('g' == usMethodName[0]) + { + TypeDescr member_td( member_type ); + typelib_InterfaceAttributeTypeDescription * attribute_td = + (typelib_InterfaceAttributeTypeDescription*) + member_td.get(); + + System::Object^ pExc = nullptr; + System::Object^ cli_ret= m_bridge->call_uno( + info->m_unoI, member_td.get(), + attribute_td->pAttributeTypeRef, + 0, 0, + nullptr, nullptr, &pExc); + return constructReturnMessage(cli_ret, nullptr, NULL, + callmsg, pExc); + } + else if ('s' == usMethodName[0]) + { + TypeDescr member_td( member_type ); + typelib_InterfaceAttributeTypeDescription * attribute_td = + (typelib_InterfaceAttributeTypeDescription *) + member_td.get(); + if (! attribute_td->bReadOnly) + { + typelib_MethodParameter param; + param.pTypeRef = attribute_td->pAttributeTypeRef; + param.bIn = sal_True; + param.bOut = sal_False; + + array<System::Object^>^ args = + static_cast<array<System::Object^>^>( + props[m_ArgsString]); + System::Object^ pExc = nullptr; + m_bridge->call_uno( + info->m_unoI, member_td.get(), + cppu::UnoType<void>::get().getTypeLibType(), + 1, ¶m, args, nullptr, &pExc); + return constructReturnMessage(nullptr, nullptr, NULL, + callmsg, pExc); + } + else + { + return constructReturnMessage(nullptr, nullptr, NULL, + callmsg, nullptr); + } + } + break; + } + } + } + // ToDo check if the message of the exception is not crippled + // the thing that should not be... no method info found! + throw BridgeRuntimeError("[cli_uno bridge]calling undeclared function on interface " + + OUString::unacquired(& ((typelib_TypeDescription *)info->m_typeDesc)->pTypeName) + + ": " + usMethodName); + } + catch (BridgeRuntimeError & err) + { + srrm::IMethodCallMessage^ mcm = + static_cast<srrm::IMethodCallMessage^>(callmsg); + return gcnew srrm::ReturnMessage(gcnew ucss::uno::RuntimeException( + mapUnoString(err.m_message.pData), nullptr), mcm); + } + catch (System::Exception^ e) + { + st::StringBuilder ^ sb = gcnew st::StringBuilder(512); + sb->Append(gcnew System::String( + "An unexpected CLI exception occurred in " + "UnoInterfaceProxy::Invoke. Original" + "message: \n")); + sb->Append(e->Message); + sb->Append((__wchar_t) '\n'); + sb->Append(e->StackTrace); + srrm::IMethodCallMessage^ mcm = + static_cast<srrm::IMethodCallMessage^>(callmsg); + return gcnew srrm::ReturnMessage(gcnew ucss::uno::RuntimeException( + sb->ToString(), nullptr), mcm); + } + catch (...) + { + System::String^ msg = gcnew System::String( + "An unexpected native C++ exception occurred in " + "UnoInterfaceProxy::Invoke."); + srrm::IMethodCallMessage^ mcm = + static_cast<srrm::IMethodCallMessage^>(callmsg); + return gcnew srrm::ReturnMessage(gcnew ucss::uno::RuntimeException( + msg, nullptr), mcm); + } + return nullptr; +} +/** If the argument args is NULL then this function is called for an attribute + method (either setXXX or getXXX). + For attributes the argument mtd is also NULL. +*/ +srrm::IMessage^ UnoInterfaceProxy::constructReturnMessage( + System::Object^ cliReturn, + array<System::Object^>^ args, + typelib_InterfaceMethodTypeDescription* mtd, + srrm::IMessage^ msg, System::Object^ exc) +{ + srrm::IMessage ^ retVal= nullptr; + srrm::IMethodCallMessage^ mcm = static_cast<srrm::IMethodCallMessage^>(msg); + if (exc) + { + retVal = gcnew srrm::ReturnMessage( + dynamic_cast<System::Exception^>(exc), mcm); + } + else + { + sc::IDictionary^ props= msg->Properties; + srrm::LogicalCallContext^ context= + static_cast<srrm::LogicalCallContext^>( + props[m_CallContextString]); + if (args != nullptr) + { + // Method + //build the array of out parameters, allocate max length + array<System::Object^>^ arOut= gcnew array<System::Object^>(mtd->nParams); + int nOut = 0; + for (int i= 0; i < mtd->nParams; i++) + { + if (mtd->pParams[i].bOut) + { + arOut[i]= args[i]; + nOut++; + } + } + retVal= gcnew srrm::ReturnMessage(cliReturn, arOut, nOut, + context, mcm); + } + else + { + // Attribute (getXXX) + retVal= gcnew srrm::ReturnMessage(cliReturn, nullptr, 0, + context, mcm); + } + } + return retVal; +} + + +CliProxy::CliProxy(Bridge const* bridge, System::Object^ cliI, + typelib_TypeDescription const* td, + const OUString& usOid): + m_ref(1), + m_bridge(bridge), + m_cliI(cliI), + m_unoType(const_cast<typelib_TypeDescription*>(td)), + m_usOid(usOid), + m_oid(mapUnoString(usOid.pData)), + m_nInheritedInterfaces(0) +{ + m_bridge->acquire(); + uno_Interface::acquire = cli_proxy_acquire; + uno_Interface::release = cli_proxy_release; + uno_Interface::pDispatcher = cli_proxy_dispatch; + + m_unoType.makeComplete(); + m_type= mapUnoType(m_unoType.get()); + + makeMethodInfos(); +#if OSL_DEBUG_LEVEL >= 2 + sd::Trace::WriteLine(System::String::Format( + gcnew System::String("cli uno bridge: Creating proxy for cli object, " + "id:\n\t{0}\n\t{1}"), m_oid, m_type)); +#endif + +} + +void CliProxy::makeMethodInfos() +{ +#if OSL_DEBUG_LEVEL >= 2 + System::Object^ cliI; + System::Type^ type; + cliI = m_cliI; + type = m_type; +#endif + + if (m_type->IsInterface == false) + return; + array<sr::MethodInfo^>^ arThisMethods = m_type->GetMethods(); + //get the inherited interfaces + array<System::Type^>^ arInheritedIfaces = m_type->GetInterfaces(); + m_nInheritedInterfaces = arInheritedIfaces->Length; + //array containing the number of methods for the interface and its + //inherited interfaces + m_arInterfaceMethodCount = gcnew array<int^>(m_nInheritedInterfaces + 1); + //determine the number of all interface methods, including the inherited + //interfaces + int numMethods = arThisMethods->Length; + for (int i= 0; i < m_nInheritedInterfaces; i++) + { + numMethods += arInheritedIfaces[i]->GetMethods()->Length; + } + //array containing MethodInfos of the cli object + m_arMethodInfos = gcnew array<sr::MethodInfo^>(numMethods); + //array containing MethodInfos of the interface + m_arInterfaceMethodInfos = gcnew array<sr::MethodInfo^>(numMethods); + //array containing the mapping of Uno interface pos to pos in + //m_arMethodInfos + m_arUnoPosToCliPos = gcnew array<System::Int32>(numMethods); + // initialize with -1 + for (int i = 0; i < numMethods; i++) + m_arUnoPosToCliPos[i] = -1; + + //fill m_arMethodInfos with the mappings + // !!! InterfaceMapping.TargetMethods should be MethodInfo*[] according + // to documentation + // but it is Type*[] instead. Bug in the framework? + System::Type^ objType = m_cliI->GetType(); + try + { + int index = 0; + // now get the methods from the inherited interface + //arInheritedIfaces[0] is the direct base interface + //arInheritedIfaces[n] is the furthest inherited interface + //Start with the base interface + int nArLength = arInheritedIfaces->Length; + for (;nArLength > 0; nArLength--) + { + sr::InterfaceMapping mapInherited = objType->GetInterfaceMap( + arInheritedIfaces[nArLength - 1]); + int numMethods = mapInherited.TargetMethods->Length; + m_arInterfaceMethodCount[nArLength - 1] = numMethods; + for (int i = 0; i < numMethods; i++, index++) + { + m_arMethodInfos[index] = safe_cast<sr::MethodInfo^>( + mapInherited.TargetMethods[i]); + + m_arInterfaceMethodInfos[index] = safe_cast<sr::MethodInfo^>( + mapInherited.InterfaceMethods[i]); + } + } + //At last come the methods of the furthest derived interface + sr::InterfaceMapping map = objType->GetInterfaceMap(m_type); + nArLength = map.TargetMethods->Length; + m_arInterfaceMethodCount[m_nInheritedInterfaces] = nArLength; + for (int i = 0; i < nArLength; i++,index++) + { + m_arMethodInfos[index]= safe_cast<sr::MethodInfo^>( + map.TargetMethods[i]); + m_arInterfaceMethodInfos[index]= safe_cast<sr::MethodInfo^>( + map.InterfaceMethods[i]); + } + } + catch (System::InvalidCastException^ ) + { + throw BridgeRuntimeError("[cli_uno bridge] preparing proxy for cli interface: " + mapCliString(m_type->ToString()) + " \nfailed!"); + } +} + +sr::MethodInfo^ CliProxy::getMethodInfo(int nUnoFunctionPos, + const OUString& usMethodName, MethodKind methodKind) +{ + sr::MethodInfo^ ret = nullptr; + //deduct 3 for XInterface methods + nUnoFunctionPos -= 3; + System::Threading::Monitor::Enter(m_arUnoPosToCliPos); + try + { + int cliPos = m_arUnoPosToCliPos[nUnoFunctionPos]; + if (cliPos != -1) + return m_arMethodInfos[cliPos]; + + //create the method function name + System::String^ sMethodName = mapUnoString(usMethodName.pData); + switch (methodKind) + { + case MK_METHOD: + break; + case MK_SET: + sMethodName = System::String::Concat( + const_cast<System::String^>(Constants::sAttributeSet), + sMethodName); + break; + case MK_GET: + sMethodName = System::String::Concat( + const_cast<System::String^>(Constants::sAttributeGet), + sMethodName); + break; + default: + OSL_ASSERT(0); + } + //Find the cli interface method that corresponds to the Uno method +// System::String* sMethodName= mapUnoString(usMethodName.pData); + int indexCliMethod = -1; + //If the cli interfaces and their methods are in the same order + //as they were declared (inheritance chain and within the interface) + //then nUnoFunctionPos should lead to the correct method. However, + //the documentation does not say that this ordering is given. + if (sMethodName->Equals(m_arInterfaceMethodInfos[nUnoFunctionPos]->Name)) + indexCliMethod = nUnoFunctionPos; + else + { + int cMethods = m_arInterfaceMethodInfos->Length; + for (int i = 0; i < cMethods; i++) + { + System::String^ cliMethod = m_arInterfaceMethodInfos[i]->Name; + if (cliMethod->Equals(sMethodName)) + { + indexCliMethod = i; + break; + } + } + } + if (indexCliMethod == -1) + { + throw BridgeRuntimeError("[cli_uno bridge] CliProxy::getMethodInfo():cli object does not implement interface method: " + usMethodName); + } + m_arUnoPosToCliPos[nUnoFunctionPos] = indexCliMethod; + ret = m_arMethodInfos[indexCliMethod]; + } + __finally + { + System::Threading::Monitor::Exit(m_arUnoPosToCliPos); + } + + return ret; +} + +CliProxy::~CliProxy() +{ +#if OSL_DEBUG_LEVEL >= 2 + sd::Trace::WriteLine(System::String::Format( + gcnew System::String( + "cli uno bridge: Destroying proxy for cli object, " + "id:\n\t{0}\n\t{1}\n"), + m_oid, m_type)); +#endif + CliEnvHolder::g_cli_env->revokeInterface(m_oid, mapUnoType(m_unoType.get())); + m_bridge->release(); +} + +uno_Interface* CliProxy::create(Bridge const * bridge, + System::Object^ cliI, + typelib_TypeDescription const* pTD, + const OUString& ousOid) +{ + uno_Interface* proxy= static_cast<uno_Interface*>( + new CliProxy(bridge, cliI, pTD, ousOid)); + + //register proxy with target environment (uno) + (*bridge->m_uno_env->registerProxyInterface)( + bridge->m_uno_env, + reinterpret_cast<void**>(&proxy), + cli_proxy_free, + ousOid.pData, (typelib_InterfaceTypeDescription*) pTD); + //register original interface + CliEnvHolder::g_cli_env->registerInterface(cliI, mapUnoString(ousOid.pData), + mapUnoType((pTD))); + + return proxy; +} + + +void SAL_CALL CliProxy::uno_DispatchMethod( + struct _uno_Interface *, + const struct _typelib_TypeDescription *, + void *, + void **, + uno_Any ** ) +{ +} +inline void CliProxy::acquire() const +{ + if (1 == osl_atomic_increment( &m_ref )) + { + // rebirth of proxy zombie + void * that = const_cast< CliProxy * >( this ); + // register at uno env + (*m_bridge->m_uno_env->registerProxyInterface)( + m_bridge->m_uno_env, &that, + cli_proxy_free, m_usOid.pData, + (typelib_InterfaceTypeDescription *)m_unoType.get() ); +#if OSL_DEBUG_LEVEL >= 2 + OSL_ASSERT( this == (void const * const)that ); +#endif + } +} + +inline void CliProxy::release() const +{ + if (0 == osl_atomic_decrement( &m_ref )) + { + // revoke from uno env on last release, + // The proxy can be resurrected if acquire is called before the uno + // environment calls cli_proxy_free. cli_proxy_free will + //delete the proxy. The environment does not acquire a registered + //proxy. + (*m_bridge->m_uno_env->revokeInterface)( + m_bridge->m_uno_env, const_cast< CliProxy * >( this ) ); + } +} +} + + +extern "C" +void SAL_CALL cli_proxy_free( uno_ExtEnvironment *, void * proxy ) + SAL_THROW_EXTERN_C() +{ + cli_uno::CliProxy * cliProxy = reinterpret_cast< + cli_uno::CliProxy * >( proxy ); + + delete cliProxy; +} + +extern "C" +void SAL_CALL cli_proxy_acquire( uno_Interface * pUnoI ) + SAL_THROW_EXTERN_C() +{ + CliProxy const * cliProxy = static_cast< CliProxy const * >( pUnoI ); + cliProxy->acquire(); +} + +extern "C" +void SAL_CALL cli_proxy_release( uno_Interface * pUnoI ) + SAL_THROW_EXTERN_C() +{ + CliProxy * cliProxy = static_cast< CliProxy * >( pUnoI ); + cliProxy->release(); +} + + +extern "C" + +void SAL_CALL cli_proxy_dispatch( + uno_Interface * pUnoI, typelib_TypeDescription const * member_td, + void * uno_ret, void * uno_args [], uno_Any ** uno_exc ) + SAL_THROW_EXTERN_C() +{ + CliProxy * proxy = static_cast< CliProxy* >( pUnoI ); + try + { + Bridge const* bridge = proxy->m_bridge; + + switch (member_td->eTypeClass) + { + case typelib_TypeClass_INTERFACE_ATTRIBUTE: + { + + sal_Int32 member_pos = ((typelib_InterfaceMemberTypeDescription *) + member_td)->nPosition; + typelib_InterfaceTypeDescription * iface_td = + (typelib_InterfaceTypeDescription *)proxy->m_unoType.get(); + OSL_ENSURE( + member_pos < iface_td->nAllMembers, + "### member pos out of range!" ); + sal_Int32 function_pos = + iface_td->pMapMemberIndexToFunctionIndex[ member_pos ]; + OSL_ENSURE( + function_pos < iface_td->nMapFunctionIndexToMemberIndex, + "### illegal function index!" ); + + if (uno_ret) // is getter method + { + OUString const& usAttrName= *(rtl_uString**)& + ((typelib_InterfaceMemberTypeDescription*) member_td) + ->pMemberName; + sr::MethodInfo^ info = proxy->getMethodInfo(function_pos, + usAttrName, CliProxy::MK_GET); + bridge->call_cli( + proxy->m_cliI, + info, + ((typelib_InterfaceAttributeTypeDescription *)member_td) + ->pAttributeTypeRef, + 0, 0, // no params + uno_ret, 0, uno_exc ); + } + else // is setter method + { + OUString const& usAttrName= *(rtl_uString**) & + ((typelib_InterfaceMemberTypeDescription*) member_td) + ->pMemberName; + sr::MethodInfo^ info = proxy->getMethodInfo(function_pos + 1, + usAttrName, CliProxy::MK_SET); + typelib_MethodParameter param; + param.pTypeRef = + ((typelib_InterfaceAttributeTypeDescription *)member_td) + ->pAttributeTypeRef; + param.bIn = sal_True; + param.bOut = sal_False; + + bridge->call_cli( + proxy->m_cliI, + // set follows get method + info, + 0 /* indicates void return */, ¶m, 1, + 0, uno_args, uno_exc ); + } + break; + } + case typelib_TypeClass_INTERFACE_METHOD: + { + sal_Int32 member_pos = ((typelib_InterfaceMemberTypeDescription *) + member_td)->nPosition; + typelib_InterfaceTypeDescription * iface_td = + (typelib_InterfaceTypeDescription *)proxy->m_unoType.get(); + OSL_ENSURE( + member_pos < iface_td->nAllMembers, + "### member pos out of range!" ); + sal_Int32 function_pos = + iface_td->pMapMemberIndexToFunctionIndex[ member_pos ]; + OSL_ENSURE( + function_pos < iface_td->nMapFunctionIndexToMemberIndex, + "### illegal function index!" ); + + switch (function_pos) + { + case 0: // queryInterface() + { + TypeDescr demanded_td( + *reinterpret_cast<typelib_TypeDescriptionReference **>( + uno_args[0])); + if (typelib_TypeClass_INTERFACE + != demanded_td.get()->eTypeClass) + { + throw BridgeRuntimeError( + "queryInterface() call demands an INTERFACE type!"); + } + + uno_Interface * pInterface = 0; + (*bridge->m_uno_env->getRegisteredInterface)( + bridge->m_uno_env, + (void **)&pInterface, proxy->m_usOid.pData, + (typelib_InterfaceTypeDescription *)demanded_td.get() ); + + if (0 == pInterface) + { + System::Type^ mgdDemandedType = + mapUnoType(demanded_td.get()); + if (mgdDemandedType->IsInstanceOfType( proxy->m_cliI )) + { +#if OSL_DEBUG_LEVEL > 0 + OUString usOid( + mapCliString( + CliEnvHolder::g_cli_env->getObjectIdentifier( + proxy->m_cliI ))); + OSL_ENSURE(usOid.equals( proxy->m_usOid ), + "### different oids!"); +#endif + uno_Interface* pUnoI = bridge->map_cli2uno( + proxy->m_cliI, demanded_td.get() ); + uno_any_construct( + (uno_Any *)uno_ret, &pUnoI, demanded_td.get(), 0 ); + (*pUnoI->release)( pUnoI ); + } + else // object does not support demanded interface + { + uno_any_construct( (uno_Any *)uno_ret, 0, 0, 0 ); + } + // no exception occurred + *uno_exc = 0; + } + else + { + uno_any_construct( + reinterpret_cast< uno_Any * >( uno_ret ), + &pInterface, demanded_td.get(), 0 ); + (*pInterface->release)( pInterface ); + *uno_exc = 0; + } + break; + } + case 1: // acquire this proxy + cli_proxy_acquire(proxy); + *uno_exc = 0; + break; + case 2: // release this proxy + cli_proxy_release(proxy); + *uno_exc = 0; + break; + default: // arbitrary method call + { + typelib_InterfaceMethodTypeDescription * method_td = + (typelib_InterfaceMethodTypeDescription *)member_td; + OUString const& usMethodName= *(rtl_uString**) & + ((typelib_InterfaceMemberTypeDescription*) member_td) + ->pMemberName; + + sr::MethodInfo^ info = proxy->getMethodInfo(function_pos, + usMethodName, CliProxy::MK_METHOD); + bridge->call_cli( + proxy->m_cliI, + info, + method_td->pReturnTypeRef, method_td->pParams, + method_td->nParams, + uno_ret, uno_args, uno_exc); + return; + } + } + break; + } + default: + { + throw BridgeRuntimeError( + "illegal member type description!" ); + } + } + } + catch (BridgeRuntimeError & err) + { + // binary identical struct + css::uno::RuntimeException exc( + "[cli_uno bridge error] " + err.m_message, + css::uno::Reference< + css::uno::XInterface >() ); + css::uno::Type const & exc_type = cppu::UnoType<decltype(exc)>::get(); + uno_type_any_construct( *uno_exc, &exc, exc_type.getTypeLibType(), 0); + SAL_WARN( "cli", exc); + } +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cli_ure/source/uno_bridge/cli_proxy.h b/cli_ure/source/uno_bridge/cli_proxy.h new file mode 100644 index 000000000..25f7d2801 --- /dev/null +++ b/cli_ure/source/uno_bridge/cli_proxy.h @@ -0,0 +1,294 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_CLI_URE_SOURCE_UNO_BRIDGE_CLI_PROXY_H +#define INCLUDED_CLI_URE_SOURCE_UNO_BRIDGE_CLI_PROXY_H + +#include "uno/environment.hxx" +#include "uno/mapping.hxx" +#include "uno/dispatcher.h" +#include "cli_bridge.h" +#include "cli_environment.h" + +#using <cli_ure.dll> + +namespace srrp = System::Runtime::Remoting::Proxies; +namespace srrm = System::Runtime::Remoting::Messaging; +namespace srr = System::Runtime::Remoting; +namespace sr = System::Reflection; +namespace sc = System::Collections; +using namespace uno; + +namespace cli_uno +{ + +public ref class UnoInterfaceInfo +{ +public: + UnoInterfaceInfo(Bridge const * bridge, uno_Interface* unoI, + typelib_InterfaceTypeDescription* td); + ~UnoInterfaceInfo(); + !UnoInterfaceInfo(); + + uno_Interface * m_unoI; // wrapped interface + System::Type ^ m_type; + typelib_InterfaceTypeDescription* m_typeDesc; + + Bridge const* m_bridge; +}; + +public ref class UnoInterfaceProxy: public srrp::RealProxy, + public srr::IRemotingTypeInfo +{ + /** used for IRemotingTypeInfo.TypeName + */ + System::String^ m_sTypeName; + /** The list is filled with UnoInterfaceInfo objects. The list can only + grow and elements are never changed. If an element was added it + must not be changed! + */ + sc::ArrayList^ m_listIfaces; + /** The number of UNO interfaces this proxy represents. It corresponds + to the number of elements in m_listIfaces. + */ + int m_numUnoIfaces; + /** The list is filled with additional UnoInterfaceProxy object due + to aggregation via bridges. Though the latter is strongly + discouraged, this has to be supported. + */ + sc::ArrayList^ m_listAdditionalProxies; + int m_nlistAdditionalProxies; + + UnoInterfaceInfo ^ findInfo( ::System::Type ^ type ); + + Bridge const* m_bridge; + System::String^ m_oid; + +#if OSL_DEBUG_LEVEL >= 2 + /** The string contains all names of UNO interfaces which are + represented by this proxy. It is used to print out the interfaces + when this proxy dies. In the destructor it is not allowed to + access m_listIfaces or any other managed object. + */ + rtl_uString * _sInterfaces; +// /** Count of interfaces. Used in conjunction with _sInterfaces. +// */ + int _numInterfaces; +#endif + +public: + + /** Creates a proxy and registers it on the dot NET side. + */ + static System::Object^ create(Bridge * bridge, + uno_Interface * pUnoI, + typelib_InterfaceTypeDescription* pTd, + const OUString& oid); + + /** RealProxy::Invoke */ + virtual srrm::IMessage^ Invoke(srrm::IMessage^ msg) override; + + /** Must be called from within a synchronized section. + Add only the interface if it is not already contained. + This method is called from the constructor and as a result + of IRemotingTypeInfo::CanCastTo + */ + void addUnoInterface(uno_Interface* pUnoI, + typelib_InterfaceTypeDescription* pTd); + ~UnoInterfaceProxy(); + !UnoInterfaceProxy(); + + inline System::String ^ getOid() + { return m_oid; } + + //IRemotingTypeInfo ---------------------------------------------- + virtual bool CanCastTo(System::Type^ fromType, System::Object^ o); + + virtual property System::String^ TypeName + { + System::String^ get() + { + return m_sTypeName; + }; + void set(System::String^ name) + { + m_sTypeName = name; + }; + } + + +private: + UnoInterfaceProxy( + Bridge * bridge, + uno_Interface * pUnoI, + typelib_InterfaceTypeDescription* pTD, + const OUString& oid ); + + static srrm::IMessage^ constructReturnMessage(System::Object^ retVal, + array<System::Object^>^ outArgs, + typelib_InterfaceMethodTypeDescription* mtd, + srrm::IMessage^ msg, System::Object^ exc); + + static System::String^ m_methodNameString = + gcnew System::String("__MethodName"); + static System::String^ m_typeNameString = gcnew System::String("__TypeName"); + static System::String^ m_ArgsString = gcnew System::String("__Args"); + static System::String^ m_CallContextString = + gcnew System::String("__CallContext"); + static System::String^ m_system_Object_String = + gcnew System::String("System.Object"); + static System::String^ m_methodSignatureString = + gcnew System::String("__MethodSignature"); + static System::String^ m_Equals_String = gcnew System::String("Equals"); + static System::String^ m_GetHashCode_String = + gcnew System::String("GetHashCode"); + static System::String^ m_GetType_String = gcnew System::String("GetType"); + static System::String^ m_ToString_String = gcnew System::String("ToString"); + +protected: + srrm::IMessage^ invokeObject(sc::IDictionary^ properties, + srrm::LogicalCallContext^ context, + srrm::IMethodCallMessage^ mcm); +}; + + +//Cannot make this __gc because a managed type cannot derive from unmanaged type +struct CliProxy: public uno_Interface +{ + mutable oslInterlockedCount m_ref; + const Bridge* m_bridge; + const gcroot<System::Object^> m_cliI; + gcroot<System::Type^> m_type; + const css::uno::TypeDescription m_unoType; + const gcroot<System::String^> m_oid; + const OUString m_usOid; + + enum MethodKind {MK_METHOD = 0, MK_SET, MK_GET}; + /** The array contains MethodInfos of the cli object. Each one reflects an + implemented interface method of the interface for which this proxy was + created. The MethodInfos are from the object's method and not from the + interface type. That is, they can be used to invoke the methods. The + order of the MethodInfo objects corresponds to the order of the + interface methods (see member m_type). Position 0 contains the + MethodInfo of the first method of the interface which represents the + root of the inheritance chain. The last MethodInfo represents the last + method of the furthest derived interface. + + The array is completely initialized in the constructor of this object. + + When the uno_DispatchMethod is called for this proxy then it receives + a typelib_TypeDescription of the member which is either an attribute + (setter or getter) or method. After determining the position of the + method within the UNO interface one can use the position to obtain the + MethodInfo of the corresponding cli method. To obtain the index for the + m_arMethodInfos array the function position has to be decreased by 3. + This is because, the cli interface does not contain the XInterface + methods. + */ + gcroot<array<sr::MethodInfo^>^> m_arMethodInfos; + + /** This array is similar to m_arMethodInfos but it contains the MethodInfo + objects of the interface (not the object). When a call is made from uno + to cli then the uno method name is compared to the cli method name. The + cli method name can be obtained from the MethodInfo object in this + array. The name of the actual implemented method may not be the same as + the interface method. + */ + gcroot<array<sr::MethodInfo^>^> m_arInterfaceMethodInfos; + + /** Maps the position of the method in the UNO interface to the position of + the corresponding MethodInfo in m_arMethodInfos. The Uno position must + not include the XInterface methods. For example, + pos 0 = XInterface::queryInterface + pos 1 = XInterface::acquire + pos 2 = XInterface::release + + That is the real Uno position has to be deducted by 3. Then + arUnoPosToCliPos[pos] contains the index for m_arMethodInfos. + + */ + gcroot<array<System::Int32>^> m_arUnoPosToCliPos; + + /** Count of inherited interfaces of the cli interface. + */ + int m_nInheritedInterfaces; + /** Contains the number of methods of each interface. + */ + gcroot<array<System::Int32^>^> m_arInterfaceMethodCount; + + CliProxy( Bridge const* bridge, System::Object^ cliI, + typelib_TypeDescription const* pTD, + const OUString& usOid); + ~CliProxy(); + + static uno_Interface* create(Bridge const * bridge, + System::Object^ cliI, + typelib_TypeDescription const * TD, + OUString const & usOid ); + + /** Prepares an array (m_arMethoInfos) containing MethodInfo object of the + interface and all inherited interfaces. At index null is the first + method of the base interface and at the last position is the last method + of the furthest derived interface. + If a UNO call is received then one can determine the position of the + method (or getter or setter for an attribute) from the passed type + information. The position minus 3 (there is no XInterface in the cli + mapping) corresponds to the index of the cli interface method in the + array. + */ + void makeMethodInfos(); + + /**Obtains a MethodInfo which can be used to invoke the cli object. + Internally it maps nUnoFunctionPos to an index that is used to get the + corresponding MethodInfo object from m_arMethoInfos. The mapping table + is dynamically initialized. If the cli interface has no base interface + or exactly one then the mapping table is initialized in one go at the + first call. In all ensuing calls the MethodInfo object is immediately + retrieved through the mapping table. + + If the interface has more than one interface in its inheritance chain, + that is Type.GetInterfaces return more than one Type, then the mapping + table is partially initialized. On the first call the mappings for the + methods of the belonging interface are created. + + The implementation assumes that the order of interface methods as + provided by InterfaceMapping.InterfaceMethods corresponds to the order + of methods in the interface declaration. + + @param nUnoFunctionPos + Position of the method in the uno interface. + */ + sr::MethodInfo^ getMethodInfo(int nUnoFunctionPos, + const OUString & usMethodName, + MethodKind mk); + + void SAL_CALL uno_DispatchMethod( + struct _uno_Interface * pUnoI, + const struct _typelib_TypeDescription * pMemberType, + void * pReturn, + void * pArgs[], + uno_Any ** ppException ); + + inline void acquire() const; + inline void release() const; +}; +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cli_ure/source/uno_bridge/cli_uno.cxx b/cli_ure/source/uno_bridge/cli_uno.cxx new file mode 100644 index 000000000..6af17e065 --- /dev/null +++ b/cli_ure/source/uno_bridge/cli_uno.cxx @@ -0,0 +1,277 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <memory> +#include <sal/alloca.h> +#include <osl/diagnose.h> +#include "rtl/ustrbuf.hxx" +#include "cli_base.h" +#include "cli_bridge.h" + +namespace sr=System::Reflection; + + +namespace cli_uno +{ + +union largest +{ + sal_Int64 n; + double d; + void * p; + uno_Any a; +}; + +System::Object^ Bridge::call_uno(uno_Interface * pUnoI, + typelib_TypeDescription* member_td, + typelib_TypeDescriptionReference * return_type, + sal_Int32 nParams, typelib_MethodParameter const * pParams, + array<System::Object^>^ args, array<System::Type^>^ argTypes, + System::Object^* ppExc) const +{ + // return mem + sal_Int32 return_size = sizeof (largest); + if ((0 != return_type) && + (typelib_TypeClass_STRUCT == return_type->eTypeClass || + typelib_TypeClass_EXCEPTION == return_type->eTypeClass)) + { + TypeDescr return_td( return_type ); + if (return_td.get()->nSize > sizeof (largest)) + return_size = return_td.get()->nSize; + } + //Prepare memory that contains all converted arguments and return value + //The memory block contains first pointers to the arguments which are in the same block + // For example, 2 arguments, 1 ret. + // + // | Pointer + // | Pointer + // | Return value + // | Arg 1 + // | Arg 2 + // + // If an argument is larger then union largest, such as some structures, then the pointer + // points to an extra block of memory. The same goes for a big return value. + + char * mem = (char *)alloca( + (nParams * sizeof (void *)) + return_size + (nParams * sizeof (largest)) ); + //array of pointers to args + void ** uno_args = (void **)mem; + //If an attribute is set, then uno_ret must be null, e.g void setAttribute(int ) + void * uno_ret= NULL; + if ( !(member_td->eTypeClass == typelib_TypeClass_INTERFACE_ATTRIBUTE && nParams == 1)) + uno_ret = (mem + (nParams * sizeof (void *))); + largest * uno_args_mem = (largest *)(mem + (nParams * sizeof (void *)) + return_size); + + OSL_ASSERT( (0 == nParams) || (nParams == args->Length) ); + for ( sal_Int32 nPos = 0; nPos < nParams; ++nPos ) + { + typelib_MethodParameter const & param = pParams[ nPos ]; + typelib_TypeDescriptionReference * type = param.pTypeRef; + + uno_args[ nPos ] = &uno_args_mem[ nPos ]; + if (typelib_TypeClass_STRUCT == type->eTypeClass || + typelib_TypeClass_EXCEPTION == type->eTypeClass) + { + TypeDescr td( type ); + if (td.get()->nSize > sizeof (largest)) + uno_args[ nPos ] = alloca( td.get()->nSize ); + } + + if (param.bIn) + { + try + { + // in, in/out params + map_to_uno( + uno_args[ nPos ],args[nPos] , type, false /* no assign */); + } + catch (...) + { + // cleanup uno in args + for (sal_Int32 n = 0; n < nPos; ++n) + { + typelib_MethodParameter const & param = pParams[n]; + if (param.bIn) + { + uno_type_destructData(uno_args[n], param.pTypeRef, 0); + } + } + throw; + } + } + } + uno_Any uno_exc_holder; + uno_Any * uno_exc = &uno_exc_holder; + // call binary uno + + (*pUnoI->pDispatcher)( pUnoI, member_td, uno_ret, uno_args, &uno_exc ); + + if (0 == uno_exc) + { + // convert out args; destruct uno args + for ( sal_Int32 nPos = 0; nPos < nParams; ++nPos ) + { + typelib_MethodParameter const & param = pParams[ nPos ]; + typelib_TypeDescriptionReference * type = param.pTypeRef; + if (param.bOut) + { + try + { + pin_ptr<System::Object^> ptr = &args[nPos]; + map_to_cli( + ptr, uno_args[nPos], param.pTypeRef, + argTypes != nullptr ? argTypes[nPos] : nullptr, false ); + } + catch (...) + { + // cleanup further uno args + for ( sal_Int32 n = nPos; n < nParams; ++n ) + { + uno_type_destructData( uno_args[n], pParams[n].pTypeRef, 0 ); + } + // cleanup uno return value + uno_type_destructData( uno_ret, return_type, 0 ); + throw; + } + } + //cleanup args + if (typelib_TypeClass_DOUBLE < type->eTypeClass && + typelib_TypeClass_ENUM != type->eTypeClass) // opt + { + uno_type_destructData(uno_args[nPos], type, 0); + } + } + + if ((0 != return_type) && + (typelib_TypeClass_VOID != return_type->eTypeClass)) + { + // convert uno return value + try + { + System::Object^ cli_ret; + map_to_cli( + &cli_ret, uno_ret, return_type, nullptr, false); + uno_type_destructData(uno_ret, return_type, 0); + return cli_ret; + } + catch (...) + { + uno_type_destructData(uno_ret, return_type, 0); + throw; + } + } + return nullptr; // void return + } + else // exception occurred + { + // destruct uno in args + for ( sal_Int32 nPos = 0; nPos < nParams; ++nPos ) + { + typelib_MethodParameter const & param = pParams[ nPos ]; + if (param.bIn) + { + uno_type_destructData( uno_args[ nPos ], param.pTypeRef, 0 ); + } + } + map_to_cli(ppExc, uno_exc_holder.pData, + uno_exc_holder.pType, nullptr, false); + return nullptr; + } +} + +void Bridge::call_cli( + System::Object^ cliI, + sr::MethodInfo^ method, + typelib_TypeDescriptionReference * return_type, + typelib_MethodParameter * params, int nParams, + void * uno_ret, void * uno_args [], uno_Any ** uno_exc ) const +{ + array<System::Object^>^ args= gcnew array<System::Object^>(nParams); + for (int nPos= 0; nPos < nParams; nPos++) + { + typelib_MethodParameter const & param= params[nPos]; + if (param.bIn) + { + pin_ptr<System::Object^> ptr = &args[nPos]; + map_to_cli( ptr, + uno_args[nPos], param.pTypeRef, nullptr, false); + } + } + System::Object^ retInvoke= nullptr; + try + { + retInvoke= method->Invoke(cliI, args); + } + catch (sr::TargetInvocationException^ e) + { + System::Exception^ exc= e->InnerException; + css::uno::TypeDescription td(mapCliType(exc->GetType())); + // memory for exception + std::unique_ptr< rtl_mem > memExc(rtl_mem::allocate(td.get()->nSize)); + map_to_uno(memExc.get(), exc, td.get()->pWeakRef, false); + (*uno_exc)->pType= td.get()->pWeakRef; + (*uno_exc)->pData= memExc.release(); + return; + } + catch (System::Exception^ e) + { + throw BridgeRuntimeError("Unexpected exception during invocation of cli object. Original message is: \n" + mapCliString(e->Message)); + } + + //convert out, in/out params + for (int nPos = 0; nPos < nParams; ++nPos ) + { + typelib_MethodParameter const & param = params[ nPos ]; + + if (param.bOut) + { + try + { + map_to_uno( + uno_args[ nPos ], args[ nPos ], param.pTypeRef, + sal_True == param.bIn /* assign if inout */); + // out array + } + catch (...) + { + // cleanup uno pure out + for ( sal_Int32 n = 0; n < nPos; ++n ) + { + typelib_MethodParameter const & param = params[ n ]; + if (! param.bIn) + uno_type_destructData( uno_args[ n ], param.pTypeRef, 0 ); + } + throw; + } + } + } + // return value + if (0 != return_type) + { + map_to_uno( + uno_ret, retInvoke, return_type, false /* no assign */); + } + // no exception occurred + *uno_exc = 0; +} + + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cli_ure/source/ure/assembly.cs b/cli_ure/source/ure/assembly.cs new file mode 100644 index 000000000..1b946180d --- /dev/null +++ b/cli_ure/source/ure/assembly.cs @@ -0,0 +1,21 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +[assembly:System.Reflection.AssemblyDescription( "CLI-UNO Runtime Library" )] +[assembly:System.Reflection.AssemblyCompany( "OpenOffice.org" )] +[assembly:System.Reflection.AssemblyVersion( "@CLI_URE_NEW_VERSION@" )] diff --git a/cli_ure/source/ure/cli_ure_config b/cli_ure/source/ure/cli_ure_config new file mode 100644 index 000000000..14dfd7a5a --- /dev/null +++ b/cli_ure/source/ure/cli_ure_config @@ -0,0 +1,11 @@ +<?xml version="1.0"?> +<configuration> + <runtime> + <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> + <dependentAssembly> + <assemblyIdentity name="cli_ure" publicKeyToken="ce2cb7e279207b9e"/> + <bindingRedirect oldVersion="CLI_URE_OLD_VERSION" newVersion="CLI_URE_NEW_VERSION" /> + </dependentAssembly> + </assemblyBinding> + </runtime> +</configuration>
\ No newline at end of file diff --git a/cli_ure/source/ure/uno/util/DisposeGuard.cs b/cli_ure/source/ure/uno/util/DisposeGuard.cs new file mode 100644 index 000000000..ce2e90ddf --- /dev/null +++ b/cli_ure/source/ure/uno/util/DisposeGuard.cs @@ -0,0 +1,50 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +using System; +using unoidl.com.sun.star.lang; + +namespace uno.util +{ + +/** Helper class to conveniently auto dispose UNO objects from within + managed code. +*/ +public struct DisposeGuard : IDisposable +{ + private XComponent m_xComponent; + + /** ctor. + + @param obj target object + */ + public DisposeGuard( XComponent obj ) + { + m_xComponent = obj; + } + + /** System.IDisposable impl + */ + public void Dispose() + { + if (null != m_xComponent) + m_xComponent.dispose(); + } +} + +} diff --git a/cli_ure/source/ure/uno/util/WeakAdapter.cs b/cli_ure/source/ure/uno/util/WeakAdapter.cs new file mode 100644 index 000000000..508a4b0f1 --- /dev/null +++ b/cli_ure/source/ure/uno/util/WeakAdapter.cs @@ -0,0 +1,111 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +using System; +using unoidl.com.sun.star.uno; +using unoidl.com.sun.star.lang; + +namespace uno.util +{ + +/** An XAdapter implementation that holds a weak reference + (System.WeakReference) to an object. + Clients can register listeners (unoidl.com.sun.star.lang.XReference) + which are notified when the object (the one which is kept weak) is + being finalized. That is, that object is being destroyed because there + are not any hard references to it. +*/ +public class WeakAdapter : XAdapter +{ + // references the XWeak implementation + private WeakReference m_weakRef; + // contains XReference objects registered by addReference + private delegate void XReference_dispose(); + private XReference_dispose m_XReference_dispose; + + /** ctor. + + @param obj the object that is to be held weakly + */ + public WeakAdapter( Object obj ) + { + m_weakRef = new WeakReference( obj ); + m_XReference_dispose = null; + } + + /** Called by the XWeak implementation (WeakBase) when it is being + finalized. It is only being called once. + The registererd XReference listeners are notified. On notification + they are to unregister themselves. The notification is thread-safe. + However, it is possible to add a listener during the notification + process, which will never receive a notification. + To prevent this, one would have to synchronize this method with + the addReference method. But this can result in deadlocks in a + multithreaded environment. + */ + internal /* non-virtual */ void referentDying() + { + XReference_dispose call; + lock (this) + { + call = m_XReference_dispose; + m_XReference_dispose = null; + } + if (null != call) + call(); + } + + // XAdapter impl + + /** Called to obtain a hard reference o the object which is kept weakly + by this instance. + + @return hard reference to the object + */ + public Object queryAdapted() + { + return m_weakRef.Target; + } + /** Called by clients to register listener which are notified when the + weak object is dying. + + @param xReference a listener + */ + public void removeReference( XReference xReference ) + { + lock (this) + { + m_XReference_dispose -= + new XReference_dispose( xReference.dispose ); + } + } + /** Called by clients to unregister listeners. + + @param xReference a listener + */ + public void addReference( XReference xReference ) + { + lock (this) + { + m_XReference_dispose += + new XReference_dispose( xReference.dispose ); + } + } +} + +} diff --git a/cli_ure/source/ure/uno/util/WeakBase.cs b/cli_ure/source/ure/uno/util/WeakBase.cs new file mode 100644 index 000000000..561d2e577 --- /dev/null +++ b/cli_ure/source/ure/uno/util/WeakBase.cs @@ -0,0 +1,135 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +using System; +using System.Collections; +using unoidl.com.sun.star.uno; +using unoidl.com.sun.star.lang; + +namespace uno.util +{ + +/** This class can be used as a base class for UNO objects. + It implements the capability to be kept weakly + (unoidl.com.sun.star.uno.XWeak) and it implements + unoidl.com.sun.star.lang.XTypeProvider which is necessary for + using the object from StarBasic. +*/ +public class WeakBase : XWeak, XTypeProvider +{ + // Contains all WeakAdapter which have been created in this class + // They have to be notified when this object dies + private WeakAdapter m_adapter = null; + + protected static Hashtable s_types = new Hashtable(); + protected static Hashtable s_impl_ids = new Hashtable(); + + // XWeak impl + /** The returned XAdapter implementation can be used to keap a + weak reference to this object. + + @return a weak adapter + */ + public XAdapter queryAdapter() + { + if (null == m_adapter) + { + lock (this) + { + if (null == m_adapter) + m_adapter = new WeakAdapter( this ); + } + } + return m_adapter; + } + + /** Overrides of Object.Finalize method. + When there are no references to this object anymore, then the + garbage collector calls this method, thereby causing the adapter + object to be notified. The adapter, in turn, notifies all + listeners (unoidl.com.sun.star.uno.XReference). + */ + ~WeakBase() + { + if (null != m_adapter) + m_adapter.referentDying(); + } + + // XTypeProvider impl + + /** Returns an array of Type objects which represent all implemented + UNO interfaces of this object. + + @return Type objects of all implemented interfaces. + */ + public Type [] getTypes() + { + Type [] types; + Type type = GetType(); + lock (s_types) + { + types = (Type []) s_types[ type ]; + if (null == types) + { + Type [] interfaces = type.GetInterfaces(); + ArrayList list = new ArrayList( interfaces.Length ); + for ( Int32 pos = 0; pos < interfaces.Length; ++pos ) + { + Type iface = interfaces[ pos ]; + // xxx todo: as long as the bridge cannot introduce + // native CTS types into UNO on the fly + if (iface.FullName.StartsWith( "unoidl." )) + { + list.Add( iface ); + } + } + Int32 len = list.Count; + Type [] ar = new Type [ len ]; + for ( Int32 pos = 0; pos < len; ++pos ) + ar[ pos ] = (Type) list[ pos ]; + s_types[ type ] = ar; + types = ar; + } + } + return types; + } + + public byte [] getImplementationId() + { + return new byte[0]; + } + + // System.Object + public override String ToString() + { + System.Text.StringBuilder buf = + new System.Text.StringBuilder( base.ToString(), 256 ); + buf.Append( "\nUNO Object Implementation:\n\tInterfaces: " ); + Type [] types = getTypes(); + for ( Int32 pos = 0; pos < types.Length; ++pos ) + { + buf.Append( types[ pos ].FullName ); + if (pos < (types.Length -1)) + buf.Append( ", " ); + } + return buf.ToString(); + } +} + +} + diff --git a/cli_ure/source/ure/uno/util/WeakComponentBase.cs b/cli_ure/source/ure/uno/util/WeakComponentBase.cs new file mode 100644 index 000000000..48715afc6 --- /dev/null +++ b/cli_ure/source/ure/uno/util/WeakComponentBase.cs @@ -0,0 +1,185 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +using System; +using unoidl.com.sun.star.lang; + +namespace uno.util +{ + +/** This class can be used as a base class for UNO objects. + It implements the capability to be kept weakly + (unoidl.com.sun.star.uno.XWeak) and it implements + unoidl.com.sun.star.lang.XTypeProvider which is necessary for + using the object from StarBasic. + In addition, it implements the interface + unoidl.com.sun.star.lang.XComponent to be disposed explicitly. +*/ +public class WeakComponentBase : WeakBase, XComponent +{ + private delegate void t_disposing( EventObject evt ); + private t_disposing m_disposing = null; + private bool m_inDispose = false; + private bool m_disposed = false; + + /** Indicates whether object is already disposed. + + @return + true, if object has been disposed + */ + protected bool isDisposed() + { + lock (this) + { + return m_disposed; + } + } + + /** Checks whether this object is disposed and throws a DisposedException + if it is already disposed. + */ + protected void checkUnDisposed() + { + if (! isDisposed()) + { + throw new unoidl.com.sun.star.lang.DisposedException( + "object already disposed!", this ); + } + } + + ~WeakComponentBase() + { + bool doDispose; + lock (this) + { + doDispose = (!m_inDispose && !m_disposed); + } + if (doDispose) + { + dispose(); + } + } + + /** Override to perform extra clean-up work. Provided for subclasses. + It is called during dispose() + */ + protected void preDisposing() + { + } + + /** Override to become notified right before the disposing action is + performed. + */ + protected void postDisposing() + { + } + + // XComponent impl + /** This method is called by the owner of this object to explicitly + dispose it. This implementation of dispose() first notifies this object + via preDisposing(), then all registered event listeners and + finally this object again calling postDisposing(). + */ + public void dispose() + { + // Determine in a thread-safe way if this is the first call to this + // method. Only then we proceed with the notification of event + // listeners. It is an error to call this method more than once. + bool doDispose = false; + t_disposing call = null; + lock (this) + { + if (! m_inDispose && !m_disposed) + { + call = m_disposing; + m_disposing = null; + m_inDispose = true; + doDispose = true; + } + } + // The notification occurs in an unsynchronized block in order to avoid + // deadlocks if one of the listeners calls back in a different thread on + // a synchronized method which uses the same object. + if (doDispose) + { + try + { + // call sub class + preDisposing(); + // send disposing notifications to listeners + if (null != call) + { + EventObject evt = new EventObject( this ); + call( evt ); + } + // call sub class + postDisposing(); + } + finally + { + // finally makes sure that the flags are set ensuring + // that this function is only called once. + m_disposed = true; + m_inDispose = false; + } + } + else + { + // in a multithreaded environment, it can't be avoided, + // that dispose is called twice. + // However this condition is traced, because it MAY indicate an + // error. +#if DEBUG + Console.WriteLine( + "WeakComponentBase.dispose() - dispose called twice" ); +#endif +// Debug.Fail( "WeakComponentBase.dispose() - dispose called twice" ); + } + } + /** Registers an event listener being notified when this object is disposed. + + @param xListener event listener + */ + public void addEventListener( XEventListener xListener ) + { + bool add; + lock (this) + { + add = (! m_inDispose && !m_disposed); + if (add) + m_disposing += new t_disposing( xListener.disposing ); + } + if (! add) + xListener.disposing( new EventObject( this ) ); + } + /** Revokes an event listener from being notified when this object + is disposed. + + @param xListener event listener + */ + public void removeEventListener( XEventListener xListener ) + { + lock (this) + { + if (! m_inDispose && !m_disposed) + m_disposing -= new t_disposing( xListener.disposing ); + } + } +} + +} |