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/climaker | |
parent | Initial commit. (diff) | |
download | libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.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 'cli_ure/source/climaker')
-rw-r--r-- | cli_ure/source/climaker/climaker_app.cxx | 675 | ||||
-rw-r--r-- | cli_ure/source/climaker/climaker_emit.cxx | 2279 | ||||
-rw-r--r-- | cli_ure/source/climaker/climaker_share.h | 258 |
3 files changed, 3212 insertions, 0 deletions
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: */ |