/* -*- 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 <algorithm>
#include <cassert>
#include <cstdlib>
#include <map>
#include <memory>
#include <set>
#include <utility>
#include <vector>
 
#include <codemaker/codemaker.hxx>
#include <codemaker/exceptiontree.hxx>
#include <codemaker/generatedtypeset.hxx>
#include <codemaker/global.hxx>
#include <codemaker/options.hxx>
#include <codemaker/typemanager.hxx>
#include <codemaker/unotype.hxx>
#include <codemaker/commonjava.hxx>
#include <rtl/ref.hxx>
#include <rtl/strbuf.hxx>
#include <rtl/string.hxx>
#include <rtl/ustrbuf.hxx>
#include <rtl/ustring.hxx>
#include <sal/types.h>
#include <unoidl/unoidl.hxx>
 
#include "classfile.hxx"
#include "javaoptions.hxx"
#include "javatype.hxx"
 
using codemaker::javamaker::ClassFile;
 
namespace {
 
void appendUnoName(
    rtl::Reference< TypeManager > const & manager, OUString const & nucleus,
    sal_Int32 rank, std::vector< OUString > const & arguments,
    OUStringBuffer * buffer)
{
    assert(manager.is());
    assert(rank >= 0);
    assert(buffer != nullptr);
    for (sal_Int32 i = 0; i != rank; ++i) {
        buffer->append("[]");
    }
    buffer->append(nucleus);
    if (!arguments.empty()) {
        buffer->append('<');
        for (std::vector< OUString >::const_iterator i(arguments.begin());
             i != arguments.end(); ++i)
        {
            if (i != arguments.begin()) {
                buffer->append(',');
            }
            OUString n;
            sal_Int32 k;
            std::vector< OUString > args;
            manager->decompose(*i, false, &n, &k, &args, nullptr);
            appendUnoName(manager, n, k, args, buffer);
        }
        buffer->append('>');
    }
}
 
// Translate the name of a UNOIDL entity (enum type, plain struct type,
// polymorphic struct type template, or interface type, decomposed into nucleus,
// sequence rank, and template arguments) into a core UNO type name:
OUString createUnoName(
    rtl::Reference< TypeManager > const & manager, OUString const & nucleus,
    sal_Int32 rank, std::vector< OUString > const & arguments)
{
    OUStringBuffer buf;
    appendUnoName(manager, nucleus, rank, arguments, &buf);
    return buf.makeStringAndClear();
}
 
enum SpecialType {
    SPECIAL_TYPE_NONE,
    SPECIAL_TYPE_ANY,
    SPECIAL_TYPE_UNSIGNED,
    SPECIAL_TYPE_INTERFACE
};
 
bool isSpecialType(SpecialType special) {
    return special >= SPECIAL_TYPE_UNSIGNED;
}
 
OString translateUnoidlEntityNameToJavaFullyQualifiedName(
    OUString const & name, OString const & prefix)
{
    assert(!name.startsWith("[]"));
    assert(name.indexOf('<') == -1);
    sal_Int32 i = name.lastIndexOf('.') + 1;
    return codemaker::convertString(name.copy(0, i)).replace('.', '/')
        + codemaker::java::translateUnoToJavaIdentifier(
            codemaker::convertString(name.copy(i)), prefix);
}
 
struct PolymorphicUnoType {
    PolymorphicUnoType(): kind(KIND_NONE) {}
 
    enum Kind { KIND_NONE, KIND_STRUCT, KIND_SEQUENCE };
    Kind kind;
    OUString name;
};
 
SpecialType translateUnoTypeToDescriptor(
    rtl::Reference< TypeManager > const & manager, OUString const & type,
    bool array, bool classType, std::set<OUString> * dependencies,
    OStringBuffer * descriptor, OStringBuffer * signature,
    bool * needsSignature, PolymorphicUnoType * polymorphicUnoType);
 
SpecialType translateUnoTypeToDescriptor(
    rtl::Reference< TypeManager > const & manager,
    codemaker::UnoType::Sort sort, OUString const & nucleus, sal_Int32 rank,
    std::vector< OUString > const & arguments, bool array, bool classType,
    std::set<OUString> * dependencies, OStringBuffer * descriptor,
    OStringBuffer * signature, bool * needsSignature,
    PolymorphicUnoType * polymorphicUnoType)
{
    assert(rank >= 0);
    assert((signature == nullptr) == (needsSignature == nullptr));
    assert(
        arguments.empty()
        == (sort
            != codemaker::UnoType::Sort::InstantiatedPolymorphicStruct));
    if (rank > 0xFF - (array ? 1 : 0)) {
        throw CannotDumpException(
            "Too many array dimensions for Java class file format");
    }
    if (array) {
        ++rank;
    }
    for (sal_Int32 i = 0; i != rank; ++i) {
        if (descriptor != nullptr) {
            descriptor->append('[');
        }
        if (signature != nullptr) {
            signature->append('[');
        }
    }
    if (polymorphicUnoType != nullptr) {
        if (sort
            == codemaker::UnoType::Sort::InstantiatedPolymorphicStruct)
        {
            polymorphicUnoType->kind = rank == 0
                ? PolymorphicUnoType::KIND_STRUCT
                : PolymorphicUnoType::KIND_SEQUENCE;
            polymorphicUnoType->name = createUnoName(
                manager, nucleus, rank, arguments);
        } else {
            polymorphicUnoType->kind = PolymorphicUnoType::KIND_NONE;
        }
    }
    switch (sort) {
    case codemaker::UnoType::Sort::Void:
    case codemaker::UnoType::Sort::Boolean:
    case codemaker::UnoType::Sort::Byte:
    case codemaker::UnoType::Sort::Short:
    case codemaker::UnoType::Sort::UnsignedShort:
    case codemaker::UnoType::Sort::Long:
    case codemaker::UnoType::Sort::UnsignedLong:
    case codemaker::UnoType::Sort::Hyper:
    case codemaker::UnoType::Sort::UnsignedHyper:
    case codemaker::UnoType::Sort::Float:
    case codemaker::UnoType::Sort::Double:
    case codemaker::UnoType::Sort::Char:
    case codemaker::UnoType::Sort::String:
    case codemaker::UnoType::Sort::Type:
    case codemaker::UnoType::Sort::Any:
        {
            static char const * const
                simpleTypeDescriptors[static_cast<int>(codemaker::UnoType::Sort::Any) + 1][2] = {
                { "V", "Ljava/lang/Void;" },
                { "Z", "Ljava/lang/Boolean;" },
                { "B", "Ljava/lang/Byte;" },
                { "S", "Ljava/lang/Short;" },
                { "S", "Ljava/lang/Short;" },
                { "I", "Ljava/lang/Integer;" },
                { "I", "Ljava/lang/Integer;" },
                { "J", "Ljava/lang/Long;" },
                { "J", "Ljava/lang/Long;" },
                { "F", "Ljava/lang/Float;" },
                { "D", "Ljava/lang/Double;" },
                { "C", "Ljava/lang/Character;" },
                { "Ljava/lang/String;", "Ljava/lang/String;" },
                { "Lcom/sun/star/uno/Type;", "Lcom/sun/star/uno/Type;" },
                { "Ljava/lang/Object;", "Ljava/lang/Object;" } };
            char const * s
                = simpleTypeDescriptors[static_cast<int>(sort)][rank == 0 && classType];
            if (descriptor != nullptr) {
                descriptor->append(s);
            }
            if (signature != nullptr) {
                signature->append(s);
            }
            static SpecialType const
                simpleTypeSpecials[static_cast<int>(codemaker::UnoType::Sort::Any) + 1] = {
                SPECIAL_TYPE_NONE, SPECIAL_TYPE_NONE, SPECIAL_TYPE_NONE,
                SPECIAL_TYPE_NONE, SPECIAL_TYPE_UNSIGNED, SPECIAL_TYPE_NONE,
                SPECIAL_TYPE_UNSIGNED, SPECIAL_TYPE_NONE, SPECIAL_TYPE_UNSIGNED,
                SPECIAL_TYPE_NONE, SPECIAL_TYPE_NONE, SPECIAL_TYPE_NONE,
                SPECIAL_TYPE_NONE, SPECIAL_TYPE_NONE, SPECIAL_TYPE_ANY };
            return simpleTypeSpecials[static_cast<int>(sort)];
        }
    case codemaker::UnoType::Sort::Interface:
        if (nucleus == "com.sun.star.uno.XInterface") {
            if (descriptor != nullptr) {
                descriptor->append("Ljava/lang/Object;");
            }
            if (signature != nullptr) {
                signature->append("Ljava/lang/Object;");
            }
            return SPECIAL_TYPE_INTERFACE;
        }
        SAL_FALLTHROUGH;
    case codemaker::UnoType::Sort::Sequence:
    case codemaker::UnoType::Sort::Enum:
    case codemaker::UnoType::Sort::PlainStruct:
    case codemaker::UnoType::Sort::InstantiatedPolymorphicStruct:
        if (dependencies != nullptr) {
            dependencies->insert(nucleus);
        }
        if (descriptor != nullptr) {
            descriptor->append(
                "L" + codemaker::convertString(nucleus).replace('.', '/')
                + ";");
        }
        if (signature != nullptr) {
            signature->append(
                "L" + codemaker::convertString(nucleus).replace('.', '/'));
            if (!arguments.empty()) {
                signature->append('<');
                for (const OUString& arg : arguments)
                {
                    translateUnoTypeToDescriptor(
                        manager, arg, false, true, dependencies, nullptr, signature,
                        needsSignature, nullptr);
                }
                signature->append('>');
                *needsSignature = true;
            }
            signature->append(';');
        }
        return SPECIAL_TYPE_NONE;
    default:
        throw CannotDumpException(
            "unexpected nucleus \"" + nucleus
            + "\" in call to translateUnoTypeToDescriptor");
    }
}
 
SpecialType translateUnoTypeToDescriptor(
    rtl::Reference< TypeManager > const & manager, OUString const & type,
    bool array, bool classType, std::set<OUString> * dependencies,
    OStringBuffer * descriptor, OStringBuffer * signature,
    bool * needsSignature, PolymorphicUnoType * polymorphicUnoType)
{
    assert(manager.is());
    OUString nucleus;
    sal_Int32 rank;
    std::vector< OUString > args;
    codemaker::UnoType::Sort sort = manager->decompose(
        type, true, &nucleus, &rank, &args, nullptr);
    return translateUnoTypeToDescriptor(
        manager, sort, nucleus, rank, args, array, classType, dependencies,
        descriptor, signature, needsSignature, polymorphicUnoType);
}
 
SpecialType getFieldDescriptor(
    rtl::Reference< TypeManager > const & manager, std::set<OUString> * dependencies,
    OUString const & type, OString * descriptor, OString * signature,
    PolymorphicUnoType * polymorphicUnoType)
{
    assert(descriptor != nullptr);
    OStringBuffer desc;
    OStringBuffer sig;
    bool needsSig = false;
    SpecialType specialType = translateUnoTypeToDescriptor(
        manager, type, false, false, dependencies, &desc, &sig, &needsSig,
        polymorphicUnoType);
    *descriptor = desc.makeStringAndClear();
    if (signature != nullptr) {
        if (needsSig) {
            *signature = sig.makeStringAndClear();
        } else {
            signature->clear();
        }
    }
    return specialType;
}
 
class MethodDescriptor {
public:
    MethodDescriptor(
        rtl::Reference< TypeManager > const & manager,
        std::set<OUString> * dependencies, OUString const & returnType,
        SpecialType * specialReturnType,
        PolymorphicUnoType * polymorphicUnoType);
 
    SpecialType addParameter(
        OUString const & type, bool array, bool dependency,
        PolymorphicUnoType * polymorphicUnoType);
 
    void addTypeParameter(OUString const & name);
 
    OString getDescriptor() const;
 
    OString getSignature() const { return m_needsSignature ? m_signatureStart.toString() + m_signatureEnd : OString();}
 
private:
    rtl::Reference< TypeManager > m_manager;
    std::set<OUString> * m_dependencies;
    OStringBuffer m_descriptorStart;
    OString m_descriptorEnd;
    OStringBuffer m_signatureStart;
    OString m_signatureEnd;
    bool m_needsSignature;
};
 
MethodDescriptor::MethodDescriptor(
    rtl::Reference< TypeManager > const & manager, std::set<OUString> * dependencies,
    OUString const & returnType, SpecialType * specialReturnType,
    PolymorphicUnoType * polymorphicUnoType):
    m_manager(manager), m_dependencies(dependencies), m_needsSignature(false)
{
    assert(dependencies != nullptr);
    m_descriptorStart.append('(');
    m_signatureStart.append('(');
    OStringBuffer descEnd;
    descEnd.append(')');
    OStringBuffer sigEnd;
    sigEnd.append(')');
    SpecialType special = translateUnoTypeToDescriptor(
        m_manager, returnType, false, false, m_dependencies, &descEnd, &sigEnd,
        &m_needsSignature, polymorphicUnoType);
    m_descriptorEnd = descEnd.makeStringAndClear();
    m_signatureEnd = sigEnd.makeStringAndClear();
    if (specialReturnType != nullptr) {
        *specialReturnType = special;
    }
}
 
SpecialType MethodDescriptor::addParameter(
    OUString const & type, bool array, bool dependency,
    PolymorphicUnoType * polymorphicUnoType)
{
    return translateUnoTypeToDescriptor(
        m_manager, type, array, false, dependency ? m_dependencies : nullptr,
        &m_descriptorStart, &m_signatureStart, &m_needsSignature,
        polymorphicUnoType);
}
 
void MethodDescriptor::addTypeParameter(OUString const & name) {
    m_descriptorStart.append("Ljava/lang/Object;");
    m_signatureStart.append("T" + codemaker::convertString(name) + ";");
    m_needsSignature = true;
}
 
OString MethodDescriptor::getDescriptor() const {
    OStringBuffer buf(m_descriptorStart);
    buf.append(m_descriptorEnd);
    return buf.makeStringAndClear();
}
 
 
class TypeInfo {
public:
    enum Kind { KIND_MEMBER, KIND_ATTRIBUTE, KIND_METHOD, KIND_PARAMETER };
 
    // Same values as in com/sun/star/lib/uno/typeinfo/TypeInfo.java:
    enum Flags {
        FLAG_READONLY = 0x008, FLAG_BOUND = 0x100
    };
 
    // KIND_MEMBER:
    TypeInfo(
        OString const & name, SpecialType specialType, sal_Int32 index,
        PolymorphicUnoType const & polymorphicUnoType,
        sal_Int32 typeParameterIndex);
 
    // KIND_ATTRIBUTE/METHOD:
    TypeInfo(
        Kind kind, OString const & name, SpecialType specialType, Flags flags,
        sal_Int32 index, PolymorphicUnoType const & polymorphicUnoType);
 
    // KIND_PARAMETER:
    TypeInfo(
        OString const & parameterName, SpecialType specialType,
        bool inParameter, bool outParameter, OString const & methodName,
        sal_Int32 index, PolymorphicUnoType const & polymorphicUnoType);
 
    sal_uInt16 generateCode(ClassFile::Code & code, std::set<OUString> * dependencies)
        const;
 
    void generatePolymorphicUnoTypeCode(
        ClassFile::Code & code, std::set<OUString> * dependencies) const;
 
private:
    Kind m_kind;
    OString m_name;
    sal_Int32 m_flags;
    sal_Int32 m_index;
    OString m_methodName;
    PolymorphicUnoType m_polymorphicUnoType;
    sal_Int32 m_typeParameterIndex;
};
 
sal_Int32 translateSpecialTypeFlags(
    SpecialType specialType, bool inParameter, bool outParameter)
{
    static sal_Int32 const specialTypeFlags[SPECIAL_TYPE_INTERFACE + 1] = {
        0, 0x0040 /* ANY */, 0x0004 /* UNSIGNED */, 0x0080 /* INTERFACE */ };
    sal_Int32 flags = specialTypeFlags[specialType];
    if (inParameter) {
        flags |= 0x0001; /* IN */
    }
    if (outParameter) {
        flags |= 0x0002; /* OUT */
    }
    return flags;
}
 
TypeInfo::TypeInfo(
    OString const & name, SpecialType specialType, sal_Int32 index,
    PolymorphicUnoType const & polymorphicUnoType,
    sal_Int32 typeParameterIndex):
    m_kind(KIND_MEMBER), m_name(name),
    m_flags(translateSpecialTypeFlags(specialType, false, false)),
    m_index(index), m_polymorphicUnoType(polymorphicUnoType),
    m_typeParameterIndex(typeParameterIndex)
{
    assert(
        polymorphicUnoType.kind == PolymorphicUnoType::KIND_NONE
        ? typeParameterIndex >= -1 : typeParameterIndex == -1);
}
 
TypeInfo::TypeInfo(
    Kind kind, OString const & name, SpecialType specialType, Flags flags,
    sal_Int32 index, PolymorphicUnoType const & polymorphicUnoType):
    m_kind(kind), m_name(name),
    m_flags(flags | translateSpecialTypeFlags(specialType, false, false)),
    m_index(index), m_polymorphicUnoType(polymorphicUnoType),
    m_typeParameterIndex(0)
{
    assert(kind == KIND_ATTRIBUTE || kind == KIND_METHOD);
}
 
TypeInfo::TypeInfo(
    OString const & parameterName, SpecialType specialType, bool inParameter,
    bool outParameter, OString const & methodName, sal_Int32 index,
    PolymorphicUnoType const & polymorphicUnoType):
    m_kind(KIND_PARAMETER), m_name(parameterName),
    m_flags(translateSpecialTypeFlags(specialType, inParameter, outParameter)),
    m_index(index), m_methodName(methodName),
    m_polymorphicUnoType(polymorphicUnoType),
    m_typeParameterIndex(0)
{}
 
sal_uInt16 TypeInfo::generateCode(
    ClassFile::Code & code, std::set<OUString> * dependencies) const
{
    switch (m_kind) {
    case KIND_MEMBER:
        code.instrNew("com/sun/star/lib/uno/typeinfo/MemberTypeInfo");
        code.instrDup();
        code.loadStringConstant(m_name);
        code.loadIntegerConstant(m_index);
        code.loadIntegerConstant(m_flags);
        if (m_polymorphicUnoType.kind != PolymorphicUnoType::KIND_NONE) {
            generatePolymorphicUnoTypeCode(code, dependencies);
            code.loadIntegerConstant(m_typeParameterIndex);
            code.instrInvokespecial(
                "com/sun/star/lib/uno/typeinfo/MemberTypeInfo", "<init>",
                "(Ljava/lang/String;IILcom/sun/star/uno/Type;I)V");
            return 8;
        } else if (m_typeParameterIndex >= 0) {
            code.instrAconstNull();
            code.loadIntegerConstant(m_typeParameterIndex);
            code.instrInvokespecial(
                "com/sun/star/lib/uno/typeinfo/MemberTypeInfo", "<init>",
                "(Ljava/lang/String;IILcom/sun/star/uno/Type;I)V");
            return 6;
        } else {
            code.instrInvokespecial(
                "com/sun/star/lib/uno/typeinfo/MemberTypeInfo", "<init>",
                "(Ljava/lang/String;II)V");
            return 4;
        }
    case KIND_ATTRIBUTE:
        code.instrNew("com/sun/star/lib/uno/typeinfo/AttributeTypeInfo");
        code.instrDup();
        code.loadStringConstant(m_name);
        code.loadIntegerConstant(m_index);
        code.loadIntegerConstant(m_flags);
        if (m_polymorphicUnoType.kind != PolymorphicUnoType::KIND_NONE) {
            generatePolymorphicUnoTypeCode(code, dependencies);
            code.instrInvokespecial(
                "com/sun/star/lib/uno/typeinfo/AttributeTypeInfo", "<init>",
                "(Ljava/lang/String;IILcom/sun/star/uno/Type;)V");
            return 8;
        } else {
            code.instrInvokespecial(
                "com/sun/star/lib/uno/typeinfo/AttributeTypeInfo", "<init>",
                "(Ljava/lang/String;II)V");
            return 4;
        }
    case KIND_METHOD:
        code.instrNew("com/sun/star/lib/uno/typeinfo/MethodTypeInfo");
        code.instrDup();
        code.loadStringConstant(m_name);
        code.loadIntegerConstant(m_index);
        code.loadIntegerConstant(m_flags);
        if (m_polymorphicUnoType.kind != PolymorphicUnoType::KIND_NONE) {
            generatePolymorphicUnoTypeCode(code, dependencies);
            code.instrInvokespecial(
                "com/sun/star/lib/uno/typeinfo/MethodTypeInfo", "<init>",
                "(Ljava/lang/String;IILcom/sun/star/uno/Type;)V");
            return 8;
        } else {
            code.instrInvokespecial(
                "com/sun/star/lib/uno/typeinfo/MethodTypeInfo", "<init>",
                "(Ljava/lang/String;II)V");
            return 4;
        }
    case KIND_PARAMETER:
        code.instrNew("com/sun/star/lib/uno/typeinfo/ParameterTypeInfo");
        code.instrDup();
        code.loadStringConstant(m_name);
        code.loadStringConstant(m_methodName);
        code.loadIntegerConstant(m_index);
        code.loadIntegerConstant(m_flags);
        if (m_polymorphicUnoType.kind != PolymorphicUnoType::KIND_NONE) {
            generatePolymorphicUnoTypeCode(code, dependencies);
            code.instrInvokespecial(
                "com/sun/star/lib/uno/typeinfo/ParameterTypeInfo", "<init>",
                ("(Ljava/lang/String;Ljava/lang/String;II"
                 "Lcom/sun/star/uno/Type;)V"));
            return 9;
        } else {
            code.instrInvokespecial(
                "com/sun/star/lib/uno/typeinfo/ParameterTypeInfo", "<init>",
                "(Ljava/lang/String;Ljava/lang/String;II)V");
            return 5;
        }
    default:
        assert(false);
        return 0;
    }
}
 
void TypeInfo::generatePolymorphicUnoTypeCode(
    ClassFile::Code & code, std::set<OUString> * dependencies) const
{
    assert(dependencies != nullptr);
    assert(m_polymorphicUnoType.kind != PolymorphicUnoType::KIND_NONE);
    code.instrNew("com/sun/star/uno/Type");
    code.instrDup();
    code.loadStringConstant(
        codemaker::convertString(m_polymorphicUnoType.name));
    if (m_polymorphicUnoType.kind == PolymorphicUnoType::KIND_STRUCT) {
        code.instrGetstatic(
            "com/sun/star/uno/TypeClass", "STRUCT",
            "Lcom/sun/star/uno/TypeClass;");
    } else {
        code.instrGetstatic(
            "com/sun/star/uno/TypeClass", "SEQUENCE",
            "Lcom/sun/star/uno/TypeClass;");
    }
    dependencies->insert("com.sun.star.uno.TypeClass");
    code.instrInvokespecial(
        "com/sun/star/uno/Type", "<init>",
        "(Ljava/lang/String;Lcom/sun/star/uno/TypeClass;)V");
}
 
void writeClassFile(
    JavaOptions const & options, OString const & type,
    ClassFile const & classFile)
{
    OString path;
    if (options.isValid("-O")) {
        path = options.getOption("-O");
    }
    OString filename(createFileNameFromType(path, type, ".class"));
    bool bCheck = false;
    if (fileExists(filename)) {
        if (options.isValid("-G")) {
            return;
        }
        bCheck = options.isValid("-Gc");
    }
    FileStream tempfile;
    tempfile.createTempFile(getTempDir(filename));
    if (!tempfile.isValid()) {
        throw CannotDumpException(
            "Cannot create temporary file for " + b2u(filename));
    }
    OString tempname(tempfile.getName());
    try {
        classFile.write(tempfile);
    } catch (...) {
        // Remove existing file for consistency:
        if (fileExists(filename)) {
            removeTypeFile(filename);
        }
        tempfile.close();
        removeTypeFile(tempname);
        throw;
    }
    tempfile.close();
    if (!makeValidTypeFile(filename, tempname, bCheck)) {
        throw CannotDumpException(
            "Cannot create " + b2u(filename) + " from temporary file "
            + b2u(tempname));
    }
}
 
void addTypeInfo(
    OString const & className, std::vector< TypeInfo > const & typeInfo,
    std::set<OUString> * dependencies, ClassFile * classFile)
{
    assert(classFile != nullptr);
    std::vector< TypeInfo >::size_type typeInfos = typeInfo.size();
    if (typeInfos > SAL_MAX_INT32) {
        throw CannotDumpException(
            "UNOTYPEINFO array too big for Java class file format");
    }
    if (typeInfos != 0) {
        classFile->addField(
            static_cast< ClassFile::AccessFlags >(
                ClassFile::ACC_PUBLIC | ClassFile::ACC_STATIC
                | ClassFile::ACC_FINAL),
            "UNOTYPEINFO", "[Lcom/sun/star/lib/uno/typeinfo/TypeInfo;",
            0, "");
        std::unique_ptr< ClassFile::Code > code(classFile->newCode());
        code->loadIntegerConstant(static_cast< sal_Int32 >(typeInfos));
        code->instrAnewarray("com/sun/star/lib/uno/typeinfo/TypeInfo");
        sal_Int32 index = 0;
        sal_uInt16 stack = 0;
        for (const TypeInfo& ti : typeInfo)
        {
            code->instrDup();
            code->loadIntegerConstant(index++);
            stack = std::max(stack, ti.generateCode(*code, dependencies));
            code->instrAastore();
        }
        code->instrPutstatic(
            className, "UNOTYPEINFO",
            "[Lcom/sun/star/lib/uno/typeinfo/TypeInfo;");
        code->instrReturn();
        if (stack > SAL_MAX_UINT16 - 4) {
            throw CannotDumpException(
                "Stack too big for Java class file format");
        }
        code->setMaxStackAndLocals(static_cast< sal_uInt16 >(stack + 4), 0);
        classFile->addMethod(
            static_cast< ClassFile::AccessFlags >(
                ClassFile::ACC_PRIVATE | ClassFile::ACC_STATIC),
            "<clinit>", "()V", code.get(), std::vector< OString >(), "");
    }
}
 
void handleEnumType(
    const OUString& name, rtl::Reference< unoidl::EnumTypeEntity > const & entity,
    JavaOptions const & options)
{
    assert(entity.is());
    OString className(codemaker::convertString(name).replace('.', '/'));
    std::unique_ptr< ClassFile > cf(
        new ClassFile(
            static_cast< ClassFile::AccessFlags >(
                ClassFile::ACC_PUBLIC | ClassFile::ACC_FINAL
                | ClassFile::ACC_SUPER),
            className, "com/sun/star/uno/Enum", ""));
    OString classDescriptor("L" + className + ";");
    for (const unoidl::EnumTypeEntity::Member& member : entity->getMembers())
    {
        OString fieldName(codemaker::convertString(member.name));
        cf->addField(
            static_cast< ClassFile::AccessFlags >(
                ClassFile::ACC_PUBLIC | ClassFile::ACC_STATIC
                | ClassFile::ACC_FINAL),
            fieldName, classDescriptor, 0, OString());
        cf->addField(
            static_cast< ClassFile::AccessFlags >(
                ClassFile::ACC_PUBLIC | ClassFile::ACC_STATIC
                | ClassFile::ACC_FINAL),
            fieldName + "_value", "I",
            cf->addIntegerInfo(member.value), "");
    }
    std::unique_ptr< ClassFile::Code > code(cf->newCode());
    code->loadLocalReference(0);
    code->loadLocalInteger(1);
    code->instrInvokespecial("com/sun/star/uno/Enum", "<init>", "(I)V");
    code->instrReturn();
    code->setMaxStackAndLocals(2, 2);
    cf->addMethod(
        ClassFile::ACC_PRIVATE,
        "<init>", "(I)V", code.get(),
        std::vector< OString >(), "");
    code.reset(cf->newCode());
    code->instrGetstatic(
        className,
        codemaker::convertString(entity->getMembers()[0].name),
        classDescriptor);
    code->instrAreturn();
    code->setMaxStackAndLocals(1, 0);
    cf->addMethod(
        static_cast< ClassFile::AccessFlags >(
            ClassFile::ACC_PUBLIC | ClassFile::ACC_STATIC),
        "getDefault", "()" + classDescriptor,
        code.get(), std::vector< OString >(), "");
    code.reset(cf->newCode());
    code->loadLocalInteger(0);
    std::map< sal_Int32, OString > map;
    sal_Int32 min = SAL_MAX_INT32;
    sal_Int32 max = SAL_MIN_INT32;
    for (const unoidl::EnumTypeEntity::Member& member : entity->getMembers())
    {
        min = std::min(min, member.value);
        max = std::max(max, member.value);
        map.emplace(member.value, codemaker::convertString(member.name));
    }
    sal_uInt64 size = static_cast< sal_uInt64 >(map.size());
    if ((static_cast< sal_uInt64 >(max) - static_cast< sal_uInt64 >(min)
         <= 2 * size)
        || size > SAL_MAX_INT32)
    {
        std::unique_ptr< ClassFile::Code > defCode(cf->newCode());
        defCode->instrAconstNull();
        defCode->instrAreturn();
        std::vector< ClassFile::Code * > blocks;
            //FIXME: pointers contained in blocks may leak
        sal_Int32 last = SAL_MAX_INT32;
        for (const auto& pair : map)
        {
            sal_Int32 value = pair.first;
            if (last != SAL_MAX_INT32) {
                for (sal_Int32 j = last + 1; j < value; ++j) {
                    blocks.push_back(nullptr);
                }
            }
            last = value;
            std::unique_ptr< ClassFile::Code > blockCode(cf->newCode());
            blockCode->instrGetstatic(className, pair.second, classDescriptor);
            blockCode->instrAreturn();
            blocks.push_back(blockCode.get());
            blockCode.release();
        }
        code->instrTableswitch(defCode.get(), min, blocks);
        for (ClassFile::Code *p : blocks)
        {
            delete p;
        }
    } else{
        std::unique_ptr< ClassFile::Code > defCode(cf->newCode());
        defCode->instrAconstNull();
        defCode->instrAreturn();
        std::vector< std::pair< sal_Int32, ClassFile::Code * > > blocks;
            //FIXME: pointers contained in blocks may leak
        for (const auto& pair : map )
        {
            std::unique_ptr< ClassFile::Code > blockCode(cf->newCode());
            blockCode->instrGetstatic(className, pair.second, classDescriptor);
            blockCode->instrAreturn();
            blocks.emplace_back(pair.first, blockCode.get());
            blockCode.release();
        }
        code->instrLookupswitch(defCode.get(), blocks);
        for (const std::pair< sal_Int32, ClassFile::Code * >& pair : blocks)
        {
            delete pair.second;
        }
    }
    code->setMaxStackAndLocals(1, 1);
    cf->addMethod(
        static_cast< ClassFile::AccessFlags >(
            ClassFile::ACC_PUBLIC | ClassFile::ACC_STATIC),
        "fromInt", "(I)" + classDescriptor, code.get(),
        std::vector< OString >(), "");
    code.reset(cf->newCode());
    for (const unoidl::EnumTypeEntity::Member& member : entity->getMembers())
    {
        code->instrNew(className);
        code->instrDup();
        code->loadIntegerConstant(member.value);
        code->instrInvokespecial(className, "<init>", "(I)V");
        code->instrPutstatic(
            className, codemaker::convertString(member.name), classDescriptor);
    }
    code->instrReturn();
    code->setMaxStackAndLocals(3, 0);
    cf->addMethod(
        static_cast< ClassFile::AccessFlags >(
            ClassFile::ACC_PRIVATE | ClassFile::ACC_STATIC),
        "<clinit>", "()V", code.get(), std::vector< OString >(), "");
    writeClassFile(options, className, *cf.get());
}
 
void addField(
    rtl::Reference< TypeManager > const & manager, std::set<OUString> * dependencies,
    ClassFile * classFile, std::vector< TypeInfo > * typeInfo,
    sal_Int32 typeParameterIndex, OUString const & type, OUString const & name,
    sal_Int32 index)
{
    assert(classFile != nullptr);
    assert(typeInfo != nullptr);
    OString descriptor;
    OString signature;
    SpecialType specialType;
    PolymorphicUnoType polymorphicUnoType;
    if (typeParameterIndex >= 0) {
        descriptor = "Ljava/lang/Object;";
        signature = "T" + codemaker::convertString(type).replace('.', '/')
            + ";";
        specialType = SPECIAL_TYPE_NONE; //TODO: SPECIAL_TYPE_TYPE_PARAMETER?
    } else {
        specialType = getFieldDescriptor(
            manager, dependencies, type, &descriptor, &signature,
            &polymorphicUnoType);
    }
    classFile->addField(
        ClassFile::ACC_PUBLIC, codemaker::convertString(name), descriptor, 0,
        signature);
    typeInfo->push_back(
        TypeInfo(
            codemaker::convertString(name), specialType, index,
            polymorphicUnoType, typeParameterIndex));
}
 
sal_uInt16 addFieldInit(
    rtl::Reference< TypeManager > const & manager, OString const & className,
    OUString const & fieldName, bool typeParameter, OUString const & fieldType,
    std::set<OUString> * dependencies, ClassFile::Code * code)
{
    assert(manager.is());
    assert(code != nullptr);
    if (typeParameter) {
        return 0;
    }
    OString name(codemaker::convertString(fieldName));
    OUString nucleus;
    sal_Int32 rank;
    std::vector< rtl::OUString > args;
    rtl::Reference< unoidl::Entity > ent;
    codemaker::UnoType::Sort sort = manager->decompose(
        fieldType, true, &nucleus, &rank, &args, &ent);
    if (rank == 0) {
        switch (sort) {
        case codemaker::UnoType::Sort::Boolean:
        case codemaker::UnoType::Sort::Byte:
        case codemaker::UnoType::Sort::Short:
        case codemaker::UnoType::Sort::UnsignedShort:
        case codemaker::UnoType::Sort::Long:
        case codemaker::UnoType::Sort::UnsignedLong:
        case codemaker::UnoType::Sort::Hyper:
        case codemaker::UnoType::Sort::UnsignedHyper:
        case codemaker::UnoType::Sort::Float:
        case codemaker::UnoType::Sort::Double:
        case codemaker::UnoType::Sort::Char:
        case codemaker::UnoType::Sort::Interface:
            return 0;
        case codemaker::UnoType::Sort::String:
            code->loadLocalReference(0);
            code->loadStringConstant(OString());
            code->instrPutfield(className, name, "Ljava/lang/String;");
            return 2;
        case codemaker::UnoType::Sort::Type:
            code->loadLocalReference(0);
            code->instrGetstatic(
                "com/sun/star/uno/Type", "VOID", "Lcom/sun/star/uno/Type;");
            code->instrPutfield(className, name, "Lcom/sun/star/uno/Type;");
            return 2;
        case codemaker::UnoType::Sort::Any:
            code->loadLocalReference(0);
            code->instrGetstatic(
                "com/sun/star/uno/Any", "VOID", "Lcom/sun/star/uno/Any;");
            code->instrPutfield(className, name, "Ljava/lang/Object;");
            return 2;
        case codemaker::UnoType::Sort::Enum:
            {
                rtl::Reference< unoidl::EnumTypeEntity > ent2(
                    dynamic_cast< unoidl::EnumTypeEntity * >(ent.get()));
                assert(ent2.is());
                code->loadLocalReference(0);
                OStringBuffer descBuf;
                translateUnoTypeToDescriptor(
                    manager, sort, nucleus, 0, std::vector< OUString >(), false,
                    false, dependencies, &descBuf, nullptr, nullptr, nullptr);
                OString desc(descBuf.makeStringAndClear());
                code->instrGetstatic(
                    codemaker::convertString(nucleus).replace('.', '/'),
                    codemaker::convertString(ent2->getMembers()[0].name), desc);
                code->instrPutfield(className, name, desc);
                return 2;
            }
        case codemaker::UnoType::Sort::PlainStruct:
        case codemaker::UnoType::Sort::InstantiatedPolymorphicStruct:
            {
                code->loadLocalReference(0);
                code->instrNew(
                    codemaker::convertString(nucleus).replace('.', '/'));
                code->instrDup();
                code->instrInvokespecial(
                    codemaker::convertString(nucleus).replace('.', '/'),
                    "<init>", "()V");
                OStringBuffer desc;
                translateUnoTypeToDescriptor(
                    manager, sort, nucleus, 0, args, false, false, dependencies,
                    &desc, nullptr, nullptr, nullptr);
                code->instrPutfield(className, name, desc.makeStringAndClear());
                return 3;
            }
        case codemaker::UnoType::Sort::Sequence:
        case codemaker::UnoType::Sort::Typedef:
            for (;;) std::abort(); // this cannot happen
        default:
            throw CannotDumpException(
                "unexpected entity \"" + fieldType
                + "\" in call to addFieldInit");
        }
    }
    code->loadLocalReference(0);
    code->loadIntegerConstant(0);
    if (rank == 1) {
        if (sort >= codemaker::UnoType::Sort::Boolean
            && sort <= codemaker::UnoType::Sort::Char)
        {
            code->instrNewarray(sort);
        } else {
            code->instrAnewarray(
                codemaker::java::translateUnoToJavaType(
                    sort, codemaker::convertString(nucleus).replace('.', '/'),
                    false));
        }
    } else {
        OStringBuffer desc;
        translateUnoTypeToDescriptor(
            manager, sort, nucleus, rank - 1, std::vector< OUString >(), false,
            false, dependencies, &desc, nullptr, nullptr, nullptr);
        code->instrAnewarray(desc.makeStringAndClear());
    }
    OStringBuffer desc;
    translateUnoTypeToDescriptor(
        manager, sort, nucleus, rank, std::vector< OUString >(), false, false,
        dependencies, &desc, nullptr, nullptr, nullptr);
    code->instrPutfield(className, name, desc.makeStringAndClear());
    return 2;
}
 
sal_uInt16 addLoadLocal(
    rtl::Reference< TypeManager > const & manager, ClassFile::Code * code,
    sal_uInt16 * index, bool typeParameter, OUString const & type, bool any,
    std::set<OUString> * dependencies)
{
    assert(manager.is());
    assert(code != nullptr);
    assert(index != nullptr);
    assert(!(typeParameter && any));
    assert(dependencies != nullptr);
    sal_uInt16 stack = 1;
    sal_uInt16 size = 1;
    if (typeParameter) {
        code->loadLocalReference(*index);
        stack = size = 1;
    } else {
        OUString nucleus;
        sal_Int32 rank;
        std::vector< OUString > args;
        codemaker::UnoType::Sort sort = manager->decompose(
            type, true, &nucleus, &rank, &args, nullptr);
        if (rank == 0) {
            switch (sort) {
            case codemaker::UnoType::Sort::Boolean:
                if (any) {
                    code->instrNew("java/lang/Boolean");
                    code->instrDup();
                    code->loadLocalInteger(*index);
                    code->instrInvokespecial(
                        "java/lang/Boolean", "<init>", "(Z)V");
                    stack = 3;
                } else {
                    code->loadLocalInteger(*index);
                    stack = 1;
                }
                size = 1;
                break;
            case codemaker::UnoType::Sort::Byte:
                if (any) {
                    code->instrNew("java/lang/Byte");
                    code->instrDup();
                    code->loadLocalInteger(*index);
                    code->instrInvokespecial(
                        "java/lang/Byte", "<init>", "(B)V");
                    stack = 3;
                } else {
                    code->loadLocalInteger(*index);
                    stack = 1;
                }
                size = 1;
                break;
            case codemaker::UnoType::Sort::Short:
                if (any) {
                    code->instrNew("java/lang/Short");
                    code->instrDup();
                    code->loadLocalInteger(*index);
                    code->instrInvokespecial(
                        "java/lang/Short", "<init>", "(S)V");
                    stack = 3;
                } else {
                    code->loadLocalInteger(*index);
                    stack = 1;
                }
                size = 1;
                break;
            case codemaker::UnoType::Sort::UnsignedShort:
                if (any) {
                    code->instrNew("com/sun/star/uno/Any");
                    code->instrDup();
                    code->instrGetstatic(
                        "com/sun/star/uno/Type", "UNSIGNED_SHORT",
                        "Lcom/sun/star/uno/Type;");
                    code->instrNew("java/lang/Short");
                    code->instrDup();
                    code->loadLocalInteger(*index);
                    code->instrInvokespecial(
                        "java/lang/Short", "<init>", "(S)V");
                    code->instrInvokespecial(
                        "com/sun/star/uno/Any", "<init>",
                        "(Lcom/sun/star/uno/Type;Ljava/lang/Object;)V");
                    stack = 6;
                } else {
                    code->loadLocalInteger(*index);
                    stack = 1;
                }
                size = 1;
                break;
            case codemaker::UnoType::Sort::Long:
                if (any) {
                    code->instrNew("java/lang/Integer");
                    code->instrDup();
                    code->loadLocalInteger(*index);
                    code->instrInvokespecial(
                        "java/lang/Integer", "<init>", "(I)V");
                    stack = 3;
                } else {
                    code->loadLocalInteger(*index);
                    stack = 1;
                }
                size = 1;
                break;
            case codemaker::UnoType::Sort::UnsignedLong:
                if (any) {
                    code->instrNew("com/sun/star/uno/Any");
                    code->instrDup();
                    code->instrGetstatic(
                        "com/sun/star/uno/Type", "UNSIGNED_LONG",
                        "Lcom/sun/star/uno/Type;");
                    code->instrNew("java/lang/Integer");
                    code->instrDup();
                    code->loadLocalInteger(*index);
                    code->instrInvokespecial(
                        "java/lang/Integer", "<init>", "(I)V");
                    code->instrInvokespecial(
                        "com/sun/star/uno/Any", "<init>",
                        "(Lcom/sun/star/uno/Type;Ljava/lang/Object;)V");
                    stack = 6;
                } else {
                    code->loadLocalInteger(*index);
                    stack = 1;
                }
                size = 1;
                break;
            case codemaker::UnoType::Sort::Hyper:
                if (any) {
                    code->instrNew("java/lang/Long");
                    code->instrDup();
                    code->loadLocalLong(*index);
                    code->instrInvokespecial(
                        "java/lang/Long", "<init>", "(J)V");
                    stack = 4;
                } else {
                    code->loadLocalLong(*index);
                    stack = 2;
                }
                size = 2;
                break;
            case codemaker::UnoType::Sort::UnsignedHyper:
                if (any) {
                    code->instrNew("com/sun/star/uno/Any");
                    code->instrDup();
                    code->instrGetstatic(
                        "com/sun/star/uno/Type", "UNSIGNED_HYPER",
                        "Lcom/sun/star/uno/Type;");
                    code->instrNew("java/lang/Long");
                    code->instrDup();
                    code->loadLocalLong(*index);
                    code->instrInvokespecial(
                        "java/lang/Long", "<init>", "(J)V");
                    code->instrInvokespecial(
                        "com/sun/star/uno/Any", "<init>",
                        "(Lcom/sun/star/uno/Type;Ljava/lang/Object;)V");
                    stack = 7;
                } else {
                    code->loadLocalLong(*index);
                    stack = 2;
                }
                size = 2;
                break;
            case codemaker::UnoType::Sort::Float:
                if (any) {
                    code->instrNew("java/lang/Float");
                    code->instrDup();
                    code->loadLocalFloat(*index);
                    code->instrInvokespecial(
                        "java/lang/Float", "<init>", "(F)V");
                    stack = 3;
                } else {
                    code->loadLocalFloat(*index);
                    stack = 1;
                }
                size = 1;
                break;
            case codemaker::UnoType::Sort::Double:
                if (any) {
                    code->instrNew("java/lang/Double");
                    code->instrDup();
                    code->loadLocalDouble(*index);
                    code->instrInvokespecial(
                        "java/lang/Double", "<init>", "(D)V");
                    stack = 4;
                } else {
                    code->loadLocalDouble(*index);
                    stack = 2;
                }
                size = 2;
                break;
            case codemaker::UnoType::Sort::Char:
                if (any) {
                    code->instrNew("java/lang/Character");
                    code->instrDup();
                    code->loadLocalInteger(*index);
                    code->instrInvokespecial(
                        "java/lang/Character", "<init>", "(C)V");
                    stack = 3;
                } else {
                    code->loadLocalInteger(*index);
                    stack = 1;
                }
                size = 1;
                break;
            case codemaker::UnoType::Sort::String:
            case codemaker::UnoType::Sort::Type:
            case codemaker::UnoType::Sort::Any:
                code->loadLocalReference(*index);
                stack = size = 1;
                break;
            case codemaker::UnoType::Sort::Enum:
                // Assuming that no Java types are derived from Java types that
                // are directly derived from com.sun.star.uno.Enum:
                code->loadLocalReference(*index);
                stack = size = 1;
                break;
            case codemaker::UnoType::Sort::PlainStruct:
            case codemaker::UnoType::Sort::InstantiatedPolymorphicStruct:
                if (any) {
                    code->instrNew("com/sun/star/uno/Any");
                    code->instrDup();
                    code->instrNew("com/sun/star/uno/Type");
                    code->instrDup();
                    code->loadStringConstant(
                        codemaker::convertString(
                            createUnoName(manager, nucleus, rank, args)));
                    code->instrGetstatic(
                        "com/sun/star/uno/TypeClass", "STRUCT",
                        "Lcom/sun/star/uno/TypeClass;");
                    dependencies->insert("com.sun.star.uno.TypeClass");
                    code->instrInvokespecial(
                        "com/sun/star/uno/Type", "<init>",
                        "(Ljava/lang/String;Lcom/sun/star/uno/TypeClass;)V");
                    code->loadLocalReference(*index);
                    code->instrInvokespecial(
                        "com/sun/star/uno/Any", "<init>",
                        "(Lcom/sun/star/uno/Type;Ljava/lang/Object;)V");
                    stack = 6;
                } else {
                    code->loadLocalReference(*index);
                    stack = 1;
                }
                size = 1;
                break;
            case codemaker::UnoType::Sort::Interface:
                if (any && nucleus != "com.sun.star.uno.XInterface") {
                    code->instrNew("com/sun/star/uno/Any");
                    code->instrDup();
                    code->instrNew("com/sun/star/uno/Type");
                    code->instrDup();
                    code->loadStringConstant(codemaker::convertString(nucleus));
                    code->instrGetstatic(
                        "com/sun/star/uno/TypeClass", "INTERFACE",
                        "Lcom/sun/star/uno/TypeClass;");
                    dependencies->insert("com.sun.star.uno.TypeClass");
                    code->instrInvokespecial(
                        "com/sun/star/uno/Type", "<init>",
                        "(Ljava/lang/String;Lcom/sun/star/uno/TypeClass;)V");
                    code->loadLocalReference(*index);
                    code->instrInvokespecial(
                        "com/sun/star/uno/Any", "<init>",
                        "(Lcom/sun/star/uno/Type;Ljava/lang/Object;)V");
                    stack = 6;
                } else {
                    code->loadLocalReference(*index);
                    stack = 1;
                }
                size = 1;
                break;
            case codemaker::UnoType::Sort::Sequence:
            case codemaker::UnoType::Sort::Typedef:
                for (;;) std::abort(); // this cannot happen
            default:
                throw CannotDumpException(
                    "unexpected entity \"" + type
                    + "\" in call to addLoadLocal");
            }
        } else {
            bool bWrap = false;
            if (any) {
                switch (sort) {
                case codemaker::UnoType::Sort::Boolean:
                case codemaker::UnoType::Sort::Byte:
                case codemaker::UnoType::Sort::Short:
                case codemaker::UnoType::Sort::Long:
                case codemaker::UnoType::Sort::Hyper:
                case codemaker::UnoType::Sort::Float:
                case codemaker::UnoType::Sort::Double:
                case codemaker::UnoType::Sort::Char:
                case codemaker::UnoType::Sort::String:
                case codemaker::UnoType::Sort::Type:
                        // assuming that no Java types are derived from
                        // com.sun.star.uno.Type
                case codemaker::UnoType::Sort::Enum:
                        // assuming that no Java types are derived from Java
                        // types that are directly derived from
                        // com.sun.star.uno.Enum
                    break;
                case codemaker::UnoType::Sort::UnsignedShort:
                case codemaker::UnoType::Sort::UnsignedLong:
                case codemaker::UnoType::Sort::UnsignedHyper:
                case codemaker::UnoType::Sort::Any:
                case codemaker::UnoType::Sort::PlainStruct:
                case codemaker::UnoType::Sort::InstantiatedPolymorphicStruct:
                case codemaker::UnoType::Sort::Interface:
                    bWrap = true;
                    break;
                case codemaker::UnoType::Sort::Sequence:
                case codemaker::UnoType::Sort::Typedef:
                    for (;;) std::abort(); // this cannot happen
                default:
                    throw CannotDumpException(
                        "unexpected entity \"" + type
                        + "\" in call to addLoadLocal");
                }
            }
            if (bWrap) {
                code->instrNew("com/sun/star/uno/Any");
                code->instrDup();
                code->instrNew("com/sun/star/uno/Type");
                code->instrDup();
                code->loadStringConstant(
                    codemaker::convertString(
                        createUnoName(manager, nucleus, rank, args)));
                code->instrInvokespecial(
                    "com/sun/star/uno/Type", "<init>", "(Ljava/lang/String;)V");
                code->loadLocalReference(*index);
                code->instrInvokespecial(
                    "com/sun/star/uno/Any", "<init>",
                    "(Lcom/sun/star/uno/Type;Ljava/lang/Object;)V");
                stack = 5;
            } else {
                code->loadLocalReference(*index);
                stack = 1;
            }
            size = 1;
        }
    }
    if (*index > SAL_MAX_UINT16 - size) {
        throw CannotDumpException(
            "Too many local variables for Java class file format");
    }
    *index = *index + size;
    return stack;
}
 
sal_uInt16 addDirectArgument(
    rtl::Reference< TypeManager > const & manager, std::set<OUString> * dependencies,
    MethodDescriptor * methodDescriptor, ClassFile::Code * code,
    sal_uInt16 * index, OString const & className, OString const & fieldName,
    bool typeParameter, OUString const & fieldType)
{
    assert(methodDescriptor != nullptr);
    assert(code != nullptr);
    OString desc;
    if (typeParameter) {
        methodDescriptor->addTypeParameter(fieldType);
        desc = "Ljava/lang/Object;";
    } else {
        methodDescriptor->addParameter(fieldType, false, true, nullptr);
        getFieldDescriptor(manager, dependencies, fieldType, &desc, nullptr, nullptr);
    }
    code->loadLocalReference(0);
    sal_uInt16 stack = addLoadLocal(
        manager, code, index, typeParameter, fieldType, false, dependencies);
    code->instrPutfield(className, fieldName, desc);
    return stack + 1;
}
 
void addPlainStructBaseArguments(
    rtl::Reference< TypeManager > const & manager, std::set<OUString> * dependencies,
    MethodDescriptor * methodDescriptor, ClassFile::Code * code,
    OUString const & base, sal_uInt16 * index)
{
    assert(manager.is());
    assert(methodDescriptor != nullptr);
    rtl::Reference< unoidl::Entity > ent;
    if (manager->getSort(base, &ent)
        != codemaker::UnoType::Sort::PlainStruct)
    {
        throw CannotDumpException(
            "unexpected entity \"" + base
            + "\" in call to addPlainStructBaseArguments");
    }
    unoidl::PlainStructTypeEntity& ent2(
        dynamic_cast<unoidl::PlainStructTypeEntity&>(*ent.get()));
    if (!ent2.getDirectBase().isEmpty()) {
        addPlainStructBaseArguments(
            manager, dependencies, methodDescriptor, code,
            ent2.getDirectBase(), index);
    }
    for (const unoidl::PlainStructTypeEntity::Member& member : ent2.getDirectMembers())
    {
        methodDescriptor->addParameter(member.type, false, true, nullptr);
        addLoadLocal(manager, code, index, false, member.type, false, dependencies);
    }
}
 
void handlePlainStructType(
    const OUString& name,
    rtl::Reference< unoidl::PlainStructTypeEntity > const & entity,
    rtl::Reference< TypeManager > const & manager, JavaOptions const & options,
    std::set<OUString> * dependencies)
{
    assert(entity.is());
    assert(dependencies != nullptr);
    OString className(codemaker::convertString(name).replace('.', '/'));
    OString superClass;
    if (entity->getDirectBase().isEmpty()) {
        superClass = "java/lang/Object";
    } else {
        superClass = codemaker::convertString(entity->getDirectBase()).
            replace('.', '/');
        dependencies->insert(entity->getDirectBase());
    }
    std::unique_ptr< ClassFile > cf(
        new ClassFile(
            static_cast< ClassFile::AccessFlags >(
                ClassFile::ACC_PUBLIC | ClassFile::ACC_SUPER),
            className, superClass, ""));
    std::vector< TypeInfo > typeInfo;
    sal_Int32 index = 0;
    for (const unoidl::PlainStructTypeEntity::Member& member : entity->getDirectMembers())
    {
        addField(
            manager, dependencies, cf.get(), &typeInfo, -1, member.type, member.name,
            index++);
    }
    std::unique_ptr< ClassFile::Code > code(cf->newCode());
    code->loadLocalReference(0);
    code->instrInvokespecial(superClass, "<init>", "()V");
    sal_uInt16 stack = 0;
    for (const unoidl::PlainStructTypeEntity::Member& member : entity->getDirectMembers())
    {
        stack = std::max(
            stack,
            addFieldInit(
                manager, className, member.name, false, member.type, dependencies,
                code.get()));
    }
    code->instrReturn();
    code->setMaxStackAndLocals(stack + 1, 1);
    cf->addMethod(
        ClassFile::ACC_PUBLIC, "<init>", "()V", code.get(),
        std::vector< OString >(), "");
    MethodDescriptor desc(manager, dependencies, "void", nullptr, nullptr);
    code.reset(cf->newCode());
    code->loadLocalReference(0);
    sal_uInt16 index2 = 1;
    if (!entity->getDirectBase().isEmpty()) {
        addPlainStructBaseArguments(
            manager, dependencies, &desc, code.get(), entity->getDirectBase(),
            &index2);
    }
    code->instrInvokespecial(superClass, "<init>", desc.getDescriptor());
    sal_uInt16 maxSize = index2;
    for (const unoidl::PlainStructTypeEntity::Member& member : entity->getDirectMembers())
    {
        maxSize = std::max(
            maxSize,
            addDirectArgument(
                manager, dependencies, &desc, code.get(), &index2, className,
                codemaker::convertString(member.name), false, member.type));
    }
    code->instrReturn();
    code->setMaxStackAndLocals(maxSize, index2);
    cf->addMethod(
        ClassFile::ACC_PUBLIC, "<init>", desc.getDescriptor(), code.get(),
        std::vector< OString >(), desc.getSignature());
    addTypeInfo(className, typeInfo, dependencies, cf.get());
    writeClassFile(options, className, *cf.get());
}
 
void handlePolyStructType(
    const OUString& name,
    rtl::Reference< unoidl::PolymorphicStructTypeTemplateEntity > const &
        entity,
    rtl::Reference< TypeManager > const & manager, JavaOptions const & options,
    std::set<OUString> * dependencies)
{
    assert(entity.is());
    OString className(codemaker::convertString(name).replace('.', '/'));
    std::map< OUString, sal_Int32 > typeParameters;
    OStringBuffer sig("<");
    sal_Int32 index = 0;
    for (const OUString& param : entity->getTypeParameters())
    {
        sig.append(codemaker::convertString(param) + ":Ljava/lang/Object;");
        if (!typeParameters.emplace(param, index++).second)
        {
            throw CannotDumpException("Bad type information"); //TODO
        }
    }
    sig.append(">Ljava/lang/Object;");
    std::unique_ptr< ClassFile > cf(
        new ClassFile(
            static_cast< ClassFile::AccessFlags >(
                ClassFile::ACC_PUBLIC | ClassFile::ACC_SUPER),
            className, "java/lang/Object", sig.makeStringAndClear()));
    std::vector< TypeInfo > typeInfo;
    index = 0;
    for (const unoidl::PolymorphicStructTypeTemplateEntity::Member& member : entity->getMembers())
    {
        sal_Int32 typeParameterIndex;
        if (member.parameterized) {
            std::map< OUString, sal_Int32 >::iterator it(
                typeParameters.find(member.type));
            if (it == typeParameters.end()) {
                throw CannotDumpException("Bad type information"); //TODO
            }
            typeParameterIndex = it->second;
        } else {
            typeParameterIndex = -1;
        }
        addField(
            manager, dependencies, cf.get(), &typeInfo, typeParameterIndex,
            member.type, member.name, index++);
    }
    std::unique_ptr< ClassFile::Code > code(cf->newCode());
    code->loadLocalReference(0);
    code->instrInvokespecial("java/lang/Object", "<init>", "()V");
    sal_uInt16 stack = 0;
    for (const unoidl::PolymorphicStructTypeTemplateEntity::Member& member : entity->getMembers())
    {
        stack = std::max(
            stack,
            addFieldInit(
                manager, className, member.name, member.parameterized, member.type,
                dependencies, code.get()));
    }
    code->instrReturn();
    code->setMaxStackAndLocals(stack + 1, 1);
    cf->addMethod(
        ClassFile::ACC_PUBLIC, "<init>", "()V", code.get(),
        std::vector< OString >(), "");
    MethodDescriptor desc(manager, dependencies, "void", nullptr, nullptr);
    code.reset(cf->newCode());
    code->loadLocalReference(0);
    sal_uInt16 index2 = 1;
    code->instrInvokespecial(
        "java/lang/Object", "<init>", desc.getDescriptor());
    sal_uInt16 maxSize = index2;
    for (const unoidl::PolymorphicStructTypeTemplateEntity::Member& member : entity->getMembers())
    {
        maxSize = std::max(
            maxSize,
            addDirectArgument(
                manager, dependencies, &desc, code.get(), &index2, className,
                codemaker::convertString(member.name), member.parameterized, member.type));
    }
    code->instrReturn();
    code->setMaxStackAndLocals(maxSize, index2);
    cf->addMethod(
        ClassFile::ACC_PUBLIC, "<init>", desc.getDescriptor(), code.get(),
        std::vector< OString >(), desc.getSignature());
    addTypeInfo(className, typeInfo, dependencies, cf.get());
    writeClassFile(options, className, *cf.get());
}
 
void addExceptionBaseArguments(
    rtl::Reference< TypeManager > const & manager, std::set<OUString> * dependencies,
    MethodDescriptor * methodDescriptor, ClassFile::Code * code,
    OUString const & base, sal_uInt16 * index)
{
    assert(manager.is());
    assert(methodDescriptor != nullptr);
    rtl::Reference< unoidl::Entity > ent;
    if (manager->getSort(base, &ent) != codemaker::UnoType::Sort::Exception)
    {
        throw CannotDumpException(
            "unexpected entity \"" + base
            + "\" in call to addExceptionBaseArguments");
    }
    unoidl::ExceptionTypeEntity& ent2(
        dynamic_cast<unoidl::ExceptionTypeEntity&>(*ent.get()));
    bool baseException = base == "com.sun.star.uno.Exception";
    if (!baseException) {
        addExceptionBaseArguments(
            manager, dependencies, methodDescriptor, code,
            ent2.getDirectBase(), index);
    }
    for (std::vector< unoidl::ExceptionTypeEntity::Member >::const_iterator i(
             ent2.getDirectMembers().begin());
         i != ent2.getDirectMembers().end(); ++i)
    {
        if (!baseException || i != ent2.getDirectMembers().begin()) {
            methodDescriptor->addParameter(i->type, false, true, nullptr);
            addLoadLocal(
                manager, code, index, false, i->type, false, dependencies);
        }
    }
}
 
void handleExceptionType(
    const OUString& name, rtl::Reference< unoidl::ExceptionTypeEntity > const & entity,
    rtl::Reference< TypeManager > const & manager, JavaOptions const & options,
    std::set<OUString> * dependencies)
{
    assert(entity.is());
    assert(dependencies != nullptr);
    OString className(codemaker::convertString(name).replace('.', '/'));
    bool baseException = false;
    bool baseRuntimeException = false;
    OString superClass;
    if (className == "com/sun/star/uno/Exception") {
        baseException = true;
        superClass = "java/lang/Exception";
    } else if (className == "com/sun/star/uno/RuntimeException") {
        baseRuntimeException = true;
        superClass = "java/lang/RuntimeException";
    } else {
        if (entity->getDirectBase().isEmpty()) {
            throw CannotDumpException(
                "Exception type \"" + name + "\" lacks base");
        }
        superClass = codemaker::convertString(entity->getDirectBase()).
            replace('.', '/');
        dependencies->insert(entity->getDirectBase());
    }
    std::unique_ptr< ClassFile > cf(
        new ClassFile(
            static_cast< ClassFile::AccessFlags >(
                ClassFile::ACC_PUBLIC | ClassFile::ACC_SUPER),
            className, superClass, ""));
    std::vector< TypeInfo > typeInfo;
    sal_Int32 index = 0;
    if (baseRuntimeException) {
        addField(
            manager, dependencies, cf.get(), &typeInfo, -1,
            "com.sun.star.uno.XInterface", "Context", index++);
    }
    for (std::vector< unoidl::ExceptionTypeEntity::Member >::const_iterator i(
             entity->getDirectMembers().begin());
         i != entity->getDirectMembers().end(); ++i)
    {
        if (!baseException || i != entity->getDirectMembers().begin()) {
            addField(
                manager, dependencies, cf.get(), &typeInfo, -1, i->type,
                i->name, index++);
        }
    }
 
    // create default constructor
    std::unique_ptr< ClassFile::Code > code(cf->newCode());
    code->loadLocalReference(0);
    code->instrInvokespecial(superClass, "<init>", "()V");
    sal_uInt16 stack = 0;
    if (baseRuntimeException) {
        stack = std::max(
            stack,
            addFieldInit(
                manager, className, "Context", false,
                "com.sun.star.uno.XInterface", dependencies, code.get()));
    }
    for (std::vector< unoidl::ExceptionTypeEntity::Member >::const_iterator i(
             entity->getDirectMembers().begin());
         i != entity->getDirectMembers().end(); ++i)
    {
        if (!baseException || i != entity->getDirectMembers().begin()) {
            stack = std::max(
                stack,
                addFieldInit(
                    manager, className, i->name, false, i->type, dependencies,
                    code.get()));
        }
    }
    code->instrReturn();
    code->setMaxStackAndLocals(stack + 1, 1);
    cf->addMethod(
        ClassFile::ACC_PUBLIC, "<init>", "()V", code.get(),
        std::vector< OString >(), "");
 
 
    // create (Throwable Cause) constructor
    code.reset(cf->newCode());
    code->loadLocalReference(0);
    code->loadLocalReference(1);
    code->instrInvokespecial(superClass, "<init>", "(Ljava/lang/Throwable;)V");
    stack = 0;
    if (baseRuntimeException) {
        stack = std::max(
            stack,
            addFieldInit(
                manager, className, "Context", false,
                "com.sun.star.uno.XInterface", dependencies, code.get()));
    }
    for (std::vector< unoidl::ExceptionTypeEntity::Member >::const_iterator i(
             entity->getDirectMembers().begin());
         i != entity->getDirectMembers().end(); ++i)
    {
        if (!baseException || i != entity->getDirectMembers().begin()) {
            stack = std::max(
                stack,
                addFieldInit(
                    manager, className, i->name, false, i->type, dependencies,
                    code.get()));
        }
    }
    code->instrReturn();
    code->setMaxStackAndLocals(stack + 2, 2);
    cf->addMethod(
        ClassFile::ACC_PUBLIC, "<init>", "(Ljava/lang/Throwable;)V", code.get(),
        std::vector< OString >(), "");
 
    // create (Throwable Cause, String Message) constructor
    code.reset(cf->newCode());
    code->loadLocalReference(0);
    if (baseException || baseRuntimeException) {
        code->loadLocalReference(2);
        code->loadLocalReference(1);
        code->instrInvokespecial(superClass, "<init>", "(Ljava/lang/String;Ljava/lang/Throwable;)V");
    } else {
        code->loadLocalReference(1);
        code->loadLocalReference(2);
        code->instrInvokespecial(superClass, "<init>", "(Ljava/lang/Throwable;Ljava/lang/String;)V");
    }
    stack = 0;
    if (baseRuntimeException) {
        stack = std::max(
            stack,
            addFieldInit(
                manager, className, "Context", false,
                "com.sun.star.uno.XInterface", dependencies, code.get()));
    }
    for (std::vector< unoidl::ExceptionTypeEntity::Member >::const_iterator i(
             entity->getDirectMembers().begin());
         i != entity->getDirectMembers().end(); ++i)
    {
        if (!baseException || i != entity->getDirectMembers().begin()) {
            stack = std::max(
                stack,
                addFieldInit(
                    manager, className, i->name, false, i->type, dependencies,
                    code.get()));
        }
    }
    code->instrReturn();
    code->setMaxStackAndLocals(stack + 3, 3);
    cf->addMethod(
        ClassFile::ACC_PUBLIC, "<init>", "(Ljava/lang/Throwable;Ljava/lang/String;)V", code.get(),
        std::vector< OString >(), "");
 
    // create (String Message) constructor
    code.reset(cf->newCode());
    code->loadLocalReference(0);
    code->loadLocalReference(1);
    code->instrInvokespecial(superClass, "<init>", "(Ljava/lang/String;)V");
    stack = 0;
    if (baseRuntimeException) {
        stack = std::max(
            stack,
            addFieldInit(
                manager, className, "Context", false,
                "com.sun.star.uno.XInterface", dependencies, code.get()));
    }
    for (std::vector< unoidl::ExceptionTypeEntity::Member >::const_iterator i(
             entity->getDirectMembers().begin());
         i != entity->getDirectMembers().end(); ++i)
    {
        if (!baseException || i != entity->getDirectMembers().begin()) {
            stack = std::max(
                stack,
                addFieldInit(
                    manager, className, i->name, false, i->type, dependencies,
                    code.get()));
        }
    }
    code->instrReturn();
    code->setMaxStackAndLocals(stack + 2, 2);
    cf->addMethod(
        ClassFile::ACC_PUBLIC, "<init>", "(Ljava/lang/String;)V", code.get(),
        std::vector< OString >(), "");
 
 
    // create (String Message, Object Context, T1 m1, ..., Tn mn) constructor
    MethodDescriptor desc1(manager, dependencies, "void", nullptr, nullptr);
    code.reset(cf->newCode());
    code->loadLocalReference(0);
    sal_uInt16 index2 = 1;
    code->loadLocalReference(index2++);
    desc1.addParameter("string", false, true, nullptr);
    if (!(baseException || baseRuntimeException)) {
        addExceptionBaseArguments(
            manager, dependencies, &desc1, code.get(), entity->getDirectBase(),
            &index2);
    }
    code->instrInvokespecial(superClass, "<init>", desc1.getDescriptor());
    sal_uInt16 maxSize = index2;
    if (baseRuntimeException) {
        maxSize = std::max(
            maxSize,
            addDirectArgument(
                manager, dependencies, &desc1, code.get(), &index2, className,
                "Context", false, "com.sun.star.uno.XInterface"));
    }
    for (std::vector< unoidl::ExceptionTypeEntity::Member >::const_iterator i(
             entity->getDirectMembers().begin());
         i != entity->getDirectMembers().end(); ++i)
    {
        if (!baseException || i != entity->getDirectMembers().begin()) {
            maxSize = std::max(
                maxSize,
                addDirectArgument(
                    manager, dependencies, &desc1, code.get(), &index2,
                    className, codemaker::convertString(i->name), false,
                    i->type));
        }
    }
    code->instrReturn();
    code->setMaxStackAndLocals(maxSize, index2);
    cf->addMethod(
        ClassFile::ACC_PUBLIC, "<init>", desc1.getDescriptor(), code.get(),
        std::vector< OString >(), desc1.getSignature());
 
    // create (Throwable Cause, String Message, Object Context, T1 m1, ..., Tn mn) constructor
    MethodDescriptor desc2(manager, dependencies, "void", nullptr, nullptr);
    code.reset(cf->newCode());
    code->loadLocalReference(0);
    sal_uInt16 index3 = 3;
    // Note that we hack in the java.lang.Throwable parameter further down,
    // because MethodDescriptor does not know how to handle it.
    desc2.addParameter("string", false, true, nullptr);
    if (baseException || baseRuntimeException) {
        code->loadLocalReference(2);
        code->loadLocalReference(1);
        code->instrInvokespecial(superClass, "<init>", "(Ljava/lang/String;Ljava/lang/Throwable;)V");
    } else {
        code->loadLocalReference(1);
        code->loadLocalReference(2);
        addExceptionBaseArguments(
            manager, dependencies, &desc2, code.get(), entity->getDirectBase(),
            &index3);
        code->instrInvokespecial(superClass, "<init>", "(Ljava/lang/Throwable;" + desc2.getDescriptor().copy(1));
    }
    sal_uInt16 maxSize2 = index3;
    if (baseRuntimeException) {
        maxSize2 = std::max(
            maxSize2,
            addDirectArgument(
                manager, dependencies, &desc2, code.get(), &index3, className,
                "Context", false, "com.sun.star.uno.XInterface"));
    }
    for (std::vector< unoidl::ExceptionTypeEntity::Member >::const_iterator i(
             entity->getDirectMembers().begin());
         i != entity->getDirectMembers().end(); ++i)
    {
        if (!baseException || i != entity->getDirectMembers().begin()) {
            maxSize2 = std::max(
                maxSize2,
                addDirectArgument(
                    manager, dependencies, &desc2, code.get(), &index3,
                    className, codemaker::convertString(i->name), false,
                    i->type));
        }
    }
    code->instrReturn();
    code->setMaxStackAndLocals(maxSize2, index3);
    cf->addMethod(
        ClassFile::ACC_PUBLIC, "<init>", "(Ljava/lang/Throwable;" + desc2.getDescriptor().copy(1), code.get(),
        std::vector< OString >(), desc2.getSignature());
 
    addTypeInfo(className, typeInfo, dependencies, cf.get());
    writeClassFile(options, className, *cf.get());
}
 
void createExceptionsAttribute(
    rtl::Reference< TypeManager > const & manager,
    std::vector< OUString > const & exceptionTypes,
    std::set<OUString> * dependencies, std::vector< OString > * exceptions,
    codemaker::ExceptionTree * tree)
{
    assert(dependencies != nullptr);
    assert(exceptions != nullptr);
    for (const OUString& ex : exceptionTypes)
    {
        dependencies->insert(ex);
        OString type(codemaker::convertString(ex).replace('.', '/'));
        exceptions->push_back(type);
        if (tree != nullptr) {
            tree->add(type.replace('/', '.'), manager);
        }
    }
}
 
void handleInterfaceType(
    const OUString& name, rtl::Reference< unoidl::InterfaceTypeEntity > const & entity,
    rtl::Reference< TypeManager > const & manager, JavaOptions const & options,
    std::set<OUString> * dependencies)
{
    assert(entity.is());
    assert(dependencies != nullptr);
    OString className(codemaker::convertString(name).replace('.', '/'));
    std::unique_ptr< ClassFile > cf(
        new ClassFile(
            static_cast< ClassFile::AccessFlags >(
                ClassFile::ACC_PUBLIC | ClassFile::ACC_INTERFACE
                | ClassFile::ACC_ABSTRACT),
            className, "java/lang/Object", ""));
    for (const unoidl::AnnotatedReference& ar : entity->getDirectMandatoryBases())
    {
        dependencies->insert(ar.name);
        cf->addInterface(codemaker::convertString(ar.name).replace('.', '/'));
    }
    // As a special case, let com.sun.star.lang.XEventListener extend
    // java.util.EventListener ("A tagging interface that all event listener
    // interfaces must extend"):
    if (className == "com/sun/star/lang/XEventListener") {
        cf->addInterface("java/util/EventListener");
    }
    std::vector< TypeInfo > typeInfo;
    if (className != "com/sun/star/uno/XInterface") {
        sal_Int32 index = 0;
        for (const unoidl::InterfaceTypeEntity::Attribute& attr : entity->getDirectAttributes())
        {
            SpecialType specialType;
            PolymorphicUnoType polymorphicUnoType;
            MethodDescriptor gdesc(
                manager, dependencies, attr.type, &specialType,
                &polymorphicUnoType);
            std::vector< OString > exc;
            createExceptionsAttribute(
                manager, attr.getExceptions, dependencies, &exc, nullptr);
            OString attrName(codemaker::convertString(attr.name));
            cf->addMethod(
                static_cast< ClassFile::AccessFlags >(
                    ClassFile::ACC_PUBLIC | ClassFile::ACC_ABSTRACT),
                "get" + attrName, gdesc.getDescriptor(), nullptr, exc,
                gdesc.getSignature());
            if (!attr.readOnly) {
                MethodDescriptor sdesc(manager, dependencies, "void", nullptr, nullptr);
                sdesc.addParameter(attr.type, false, true, nullptr);
                std::vector< OString > exc2;
                createExceptionsAttribute(
                    manager, attr.setExceptions, dependencies, &exc2, nullptr);
                cf->addMethod(
                    static_cast< ClassFile::AccessFlags >(
                        ClassFile::ACC_PUBLIC | ClassFile::ACC_ABSTRACT),
                    "set" + attrName, sdesc.getDescriptor(), nullptr, exc2,
                    sdesc.getSignature());
            }
            typeInfo.emplace_back(
                    TypeInfo::KIND_ATTRIBUTE, attrName, specialType,
                    static_cast< TypeInfo::Flags >(
                        (attr.readOnly ? TypeInfo::FLAG_READONLY : 0)
                        | (attr.bound ? TypeInfo::FLAG_BOUND : 0)),
                    index, polymorphicUnoType);
            index += (attr.readOnly ? 1 : 2);
        }
        for (const unoidl::InterfaceTypeEntity::Method& method : entity->getDirectMethods())
        {
            OString methodName(codemaker::convertString(method.name));
            SpecialType specialReturnType;
            PolymorphicUnoType polymorphicUnoReturnType;
            MethodDescriptor desc(
                manager, dependencies, method.returnType, &specialReturnType,
                &polymorphicUnoReturnType);
            typeInfo.emplace_back(
                    TypeInfo::KIND_METHOD, methodName, specialReturnType,
                    static_cast< TypeInfo::Flags >(0), index++,
                    polymorphicUnoReturnType);
            sal_Int32 paramIndex = 0;
            for (const unoidl::InterfaceTypeEntity::Method::Parameter& param : method.parameters)
            {
                bool in = param.direction
                    != unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_OUT;
                bool out = param.direction
                    != unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN;
                PolymorphicUnoType polymorphicUnoType;
                SpecialType specialType = desc.addParameter(
                    param.type, out, true, &polymorphicUnoType);
                if (out || isSpecialType(specialType)
                    || polymorphicUnoType.kind != PolymorphicUnoType::KIND_NONE)
                {
                    typeInfo.emplace_back(
                            codemaker::convertString(param.name), specialType, in,
                            out, methodName, paramIndex, polymorphicUnoType);
                }
                ++paramIndex;
            }
            std::vector< OString > exc2;
            createExceptionsAttribute(
                manager, method.exceptions, dependencies, &exc2, nullptr);
            cf->addMethod(
                static_cast< ClassFile::AccessFlags >(
                    ClassFile::ACC_PUBLIC | ClassFile::ACC_ABSTRACT),
                methodName, desc.getDescriptor(), nullptr, exc2, desc.getSignature());
        }
    }
    addTypeInfo(className, typeInfo, dependencies, cf.get());
    writeClassFile(options, className, *cf.get());
}
 
void handleTypedef(
    rtl::Reference< unoidl::TypedefEntity > const & entity,
    rtl::Reference< TypeManager > const & manager, std::set<OUString> * dependencies)
{
    assert(entity.is());
    assert(manager.is());
    assert(dependencies != nullptr);
    OUString nucleus;
    switch (manager->decompose(entity->getType(), false, &nucleus, nullptr, nullptr, nullptr))
    {
    case codemaker::UnoType::Sort::Boolean:
    case codemaker::UnoType::Sort::Byte:
    case codemaker::UnoType::Sort::Short:
    case codemaker::UnoType::Sort::UnsignedShort:
    case codemaker::UnoType::Sort::Long:
    case codemaker::UnoType::Sort::UnsignedLong:
    case codemaker::UnoType::Sort::Hyper:
    case codemaker::UnoType::Sort::UnsignedHyper:
    case codemaker::UnoType::Sort::Float:
    case codemaker::UnoType::Sort::Double:
    case codemaker::UnoType::Sort::Char:
    case codemaker::UnoType::Sort::String:
    case codemaker::UnoType::Sort::Type:
    case codemaker::UnoType::Sort::Any:
        break;
    case codemaker::UnoType::Sort::Enum:
    case codemaker::UnoType::Sort::PlainStruct:
    case codemaker::UnoType::Sort::Interface:
    case codemaker::UnoType::Sort::Typedef:
        dependencies->insert(nucleus);
        break;
    default:
        assert(false); // this cannot happen
    }
}
 
void handleConstantGroup(
    const OUString& name, rtl::Reference< unoidl::ConstantGroupEntity > const & entity,
    rtl::Reference< TypeManager > const & manager, JavaOptions const & options,
    std::set<OUString> * dependencies)
{
    assert(entity.is());
    OString className(codemaker::convertString(name).replace('.', '/'));
    std::unique_ptr< ClassFile > cf(
        new ClassFile(
            static_cast< ClassFile::AccessFlags >(
                ClassFile::ACC_PUBLIC | ClassFile::ACC_INTERFACE
                | ClassFile::ACC_ABSTRACT),
            className, "java/lang/Object", ""));
    for (const unoidl::ConstantGroupEntity::Member& member : entity->getMembers())
    {
        OUString type;
        sal_uInt16 valueIndex = sal_uInt16(); // avoid false warnings
        switch (member.value.type) {
        case unoidl::ConstantValue::TYPE_BOOLEAN:
            type = "boolean";
            valueIndex = cf->addIntegerInfo(sal_Int32(member.value.booleanValue));
            break;
        case unoidl::ConstantValue::TYPE_BYTE:
            type = "byte";
            valueIndex = cf->addIntegerInfo(member.value.byteValue);
            break;
        case unoidl::ConstantValue::TYPE_SHORT:
            type = "short";
            valueIndex = cf->addIntegerInfo(member.value.shortValue);
            break;
        case unoidl::ConstantValue::TYPE_UNSIGNED_SHORT:
            type = "unsigned short";
            valueIndex = cf->addIntegerInfo(member.value.unsignedShortValue);
            break;
        case unoidl::ConstantValue::TYPE_LONG:
            type = "long";
            valueIndex = cf->addIntegerInfo(member.value.longValue);
            break;
        case unoidl::ConstantValue::TYPE_UNSIGNED_LONG:
            type = "unsigned long";
            valueIndex = cf->addIntegerInfo(
                static_cast< sal_Int32 >(member.value.unsignedLongValue));
            break;
        case unoidl::ConstantValue::TYPE_HYPER:
            type = "hyper";
            valueIndex = cf->addLongInfo(member.value.hyperValue);
            break;
        case unoidl::ConstantValue::TYPE_UNSIGNED_HYPER:
            type = "unsigned hyper";
            valueIndex = cf->addLongInfo(
                static_cast< sal_Int64 >(member.value.unsignedHyperValue));
            break;
        case unoidl::ConstantValue::TYPE_FLOAT:
            type = "float";
            valueIndex = cf->addFloatInfo(member.value.floatValue);
            break;
        case unoidl::ConstantValue::TYPE_DOUBLE:
            type = "double";
            valueIndex = cf->addDoubleInfo(member.value.doubleValue);
            break;
        }
        OString desc;
        OString sig;
        getFieldDescriptor(manager, dependencies, type, &desc, &sig, nullptr);
        cf->addField(
            static_cast< ClassFile::AccessFlags >(
                ClassFile::ACC_PUBLIC | ClassFile::ACC_STATIC
                | ClassFile::ACC_FINAL),
            codemaker::convertString(member.name), desc, valueIndex, sig);
    }
    writeClassFile(options, className, *cf.get());
}
 
void addExceptionHandlers(
    codemaker::ExceptionTreeNode const * node,
    ClassFile::Code::Position start, ClassFile::Code::Position end,
    ClassFile::Code::Position handler, ClassFile::Code * code)
{
    assert(node != nullptr);
    assert(code != nullptr);
    if (node->present) {
        code->addException(start, end, handler, node->name.replace('.', '/'));
    } else {
        for (std::unique_ptr<codemaker::ExceptionTreeNode> const & p : node->children)
        {
            addExceptionHandlers(p.get(), start, end, handler, code);
        }
    }
}
 
void addConstructor(
    rtl::Reference< TypeManager > const & manager,
    OString const & realJavaBaseName, OString const & unoName,
    OString const & className,
    unoidl::SingleInterfaceBasedServiceEntity::Constructor const & constructor,
    OUString const & returnType, std::set<OUString> * dependencies,
    ClassFile * classFile)
{
    assert(dependencies != nullptr);
    assert(classFile != nullptr);
    MethodDescriptor desc(manager, dependencies, returnType, nullptr, nullptr);
    desc.addParameter("com.sun.star.uno.XComponentContext", false, false, nullptr);
    std::unique_ptr< ClassFile::Code > code(classFile->newCode());
    code->loadLocalReference(0);
    // stack: context
    code->instrInvokeinterface(
        "com/sun/star/uno/XComponentContext", "getServiceManager",
        "()Lcom/sun/star/lang/XMultiComponentFactory;", 1);
    // stack: factory
    code->loadStringConstant(unoName);
    // stack: factory serviceName
    codemaker::ExceptionTree tree;
    ClassFile::Code::Position tryStart;
    ClassFile::Code::Position tryEnd;
    std::vector< OString > exc;
    sal_uInt16 stack;
    sal_uInt16 localIndex = 1;
    ClassFile::AccessFlags access = static_cast< ClassFile::AccessFlags >(
        ClassFile::ACC_PUBLIC | ClassFile::ACC_STATIC);
    if (constructor.defaultConstructor) {
        code->loadLocalReference(0);
        // stack: factory serviceName context
        tryStart = code->getPosition();
        code->instrInvokeinterface(
            "com/sun/star/lang/XMultiComponentFactory",
            "createInstanceWithContext",
            ("(Ljava/lang/String;Lcom/sun/star/uno/XComponentContext;)"
             "Ljava/lang/Object;"),
            3);
        tryEnd = code->getPosition();
        // stack: instance
        stack = 3;
    } else {
        if (constructor.parameters.size() == 1
            && constructor.parameters[0].rest)
        {
            desc.addParameter("any", true, true, nullptr);
            code->loadLocalReference(localIndex++);
            // stack: factory serviceName args
            stack = 4;
            access = static_cast< ClassFile::AccessFlags >(
                access | ClassFile::ACC_VARARGS);
        } else {
            code->loadIntegerConstant(constructor.parameters.size());
            // stack: factory serviceName N
            code->instrAnewarray("java/lang/Object");
            // stack: factory serviceName args
            stack = 0;
            sal_Int32 n = 0;
            for (const unoidl::SingleInterfaceBasedServiceEntity::Constructor::Parameter& param :
                         constructor.parameters)
            {
                desc.addParameter(param.type, false, true, nullptr);
                code->instrDup();
                // stack: factory serviceName args args
                code->loadIntegerConstant(n++);
                // stack: factory serviceName args args i
                stack = std::max(
                    stack,
                    addLoadLocal(
                        manager, code.get(), &localIndex, false, param.type, true,
                        dependencies));
                // stack: factory serviceName args args i any
                code->instrAastore();
                // stack: factory serviceName args
            }
            stack += 5;
        }
        code->loadLocalReference(0);
        // stack: factory serviceName args context
        tryStart = code->getPosition();
        code->instrInvokeinterface(
            "com/sun/star/lang/XMultiComponentFactory",
            "createInstanceWithArgumentsAndContext",
            ("(Ljava/lang/String;[Ljava/lang/Object;"
             "Lcom/sun/star/uno/XComponentContext;)Ljava/lang/Object;"),
            4);
        tryEnd = code->getPosition();
        // stack: instance
        createExceptionsAttribute(
            manager, constructor.exceptions, dependencies, &exc, &tree);
    }
    code->loadLocalReference(0);
    // stack: instance context
    code->instrInvokestatic(
        className, "$castInstance",
        ("(Ljava/lang/Object;Lcom/sun/star/uno/XComponentContext;)"
         "Ljava/lang/Object;"));
    // stack: instance
    code->instrCheckcast(
        codemaker::convertString(returnType).replace('.', '/'));
    // stack: instance
    code->instrAreturn();
    if (!tree.getRoot().present) {
        ClassFile::Code::Position pos1 = code->getPosition();
        // stack: e
        code->instrInvokevirtual(
            "java/lang/Throwable", "toString", "()Ljava/lang/String;");
        // stack: str
        localIndex = std::max< sal_uInt16 >(localIndex, 2);
        code->storeLocalReference(1);
        // stack: -
        code->instrNew("com/sun/star/uno/DeploymentException");
        // stack: ex
        code->instrDup();
        // stack: ex ex
        code->loadStringConstant(
            "component context fails to supply service " + unoName + " of type "
            + realJavaBaseName + ": ");
        // stack: ex ex "..."
        code->loadLocalReference(1);
        // stack: ex ex "..." str
        code->instrInvokevirtual(
            "java/lang/String", "concat",
            "(Ljava/lang/String;)Ljava/lang/String;");
        // stack: ex ex "..."
        code->loadLocalReference(0);
        // stack: ex ex "..." context
        code->instrInvokespecial(
            "com/sun/star/uno/DeploymentException", "<init>",
            "(Ljava/lang/String;Ljava/lang/Object;)V");
        // stack: ex
        ClassFile::Code::Position pos2 = code->getPosition();
        code->instrAthrow();
        addExceptionHandlers(
            &tree.getRoot(), tryStart, tryEnd, pos2, code.get());
        code->addException(
            tryStart, tryEnd, pos1, "com/sun/star/uno/Exception");
        dependencies->insert("com.sun.star.uno.Exception");
        stack = std::max< sal_uInt16 >(stack, 4);
    }
    code->setMaxStackAndLocals(stack, localIndex);
    classFile->addMethod(
        access,
        codemaker::java::translateUnoToJavaIdentifier(
            (constructor.defaultConstructor
             ? OString("create") : codemaker::convertString(constructor.name)),
            "method"),
        desc.getDescriptor(), code.get(), exc, desc.getSignature());
}
 
void handleService(
    const OUString& name,
    rtl::Reference< unoidl::SingleInterfaceBasedServiceEntity > const & entity,
    rtl::Reference< TypeManager > const & manager, JavaOptions const & options,
    std::set<OUString> * dependencies)
{
    assert(entity.is());
    assert(dependencies != nullptr);
    OString unoName(codemaker::convertString(name));
    OString className(
        translateUnoidlEntityNameToJavaFullyQualifiedName(name, "service"));
    std::unique_ptr< ClassFile > cf(
        new ClassFile(
            static_cast< ClassFile::AccessFlags >(
                ClassFile::ACC_PUBLIC | ClassFile::ACC_FINAL
                | ClassFile::ACC_SUPER),
            className, "java/lang/Object", ""));
    if (!entity->getConstructors().empty()) {
        OString realJavaBaseName(
            codemaker::convertString(entity->getBase()));
        dependencies->insert(entity->getBase());
        dependencies->insert("com.sun.star.lang.XMultiComponentFactory");
        dependencies->insert("com.sun.star.uno.DeploymentException");
        dependencies->insert("com.sun.star.uno.TypeClass");
        dependencies->insert("com.sun.star.uno.XComponentContext");
        for (const unoidl::SingleInterfaceBasedServiceEntity::Constructor& cons :
                 entity->getConstructors())
        {
            addConstructor(
                manager, realJavaBaseName, unoName, className, cons,
                entity->getBase(), dependencies, cf.get());
        }
        // Synthetic castInstance method:
        {
            std::unique_ptr< ClassFile::Code > code(cf->newCode());
            code->instrNew("com/sun/star/uno/Type");
            // stack: type
            code->instrDup();
            // stack: type type
            code->loadStringConstant(realJavaBaseName);
            // stack: type type "..."
            code->instrGetstatic(
                "com/sun/star/uno/TypeClass", "INTERFACE",
                "Lcom/sun/star/uno/TypeClass;");
            // stack: type type "..." INTERFACE
            code->instrInvokespecial(
                "com/sun/star/uno/Type", "<init>",
                "(Ljava/lang/String;Lcom/sun/star/uno/TypeClass;)V");
            // stack: type
            code->loadLocalReference(0);
            // stack: type instance
            code->instrInvokestatic(
                "com/sun/star/uno/UnoRuntime", "queryInterface",
                ("(Lcom/sun/star/uno/Type;Ljava/lang/Object;)"
                 "Ljava/lang/Object;"));
            // stack: instance
            code->instrDup();
            // stack: instance instance
            ClassFile::Code::Branch branch = code->instrIfnull();
            // stack: instance
            code->instrAreturn();
            code->branchHere(branch);
            code->instrPop();
            // stack: -
            code->instrNew("com/sun/star/uno/DeploymentException");
            // stack: ex
            code->instrDup();
            // stack: ex ex
            code->loadStringConstant(
                "component context fails to supply service " + unoName
                + " of type " + realJavaBaseName);
            // stack: ex ex "..."
            code->loadLocalReference(1);
            // stack: ex ex "..." context
            code->instrInvokespecial(
                "com/sun/star/uno/DeploymentException", "<init>",
                "(Ljava/lang/String;Ljava/lang/Object;)V");
            // stack: ex
            code->instrAthrow();
            code->setMaxStackAndLocals(4, 2);
            cf->addMethod(
                static_cast< ClassFile::AccessFlags >(
                    ClassFile::ACC_PRIVATE | ClassFile::ACC_STATIC
                    | ClassFile::ACC_SYNTHETIC),
                "$castInstance",
                ("(Ljava/lang/Object;Lcom/sun/star/uno/XComponentContext;)"
                 "Ljava/lang/Object;"),
                code.get(), std::vector< OString >(), "");
        }
    }
    writeClassFile(options, className, *cf.get());
}
 
void handleSingleton(
    const OUString& name,
    rtl::Reference< unoidl::InterfaceBasedSingletonEntity > const & entity,
    rtl::Reference< TypeManager > const & manager, JavaOptions const & options,
    std::set<OUString> * dependencies)
{
    assert(entity.is());
    assert(dependencies != nullptr);
    OString realJavaBaseName(codemaker::convertString(entity->getBase()));
    OString base(realJavaBaseName.replace('.', '/'));
    dependencies->insert(entity->getBase());
    OString unoName(codemaker::convertString(name));
    OString className(
        translateUnoidlEntityNameToJavaFullyQualifiedName(name, "singleton"));
    dependencies->insert("com.sun.star.uno.DeploymentException");
    dependencies->insert("com.sun.star.uno.TypeClass");
    dependencies->insert("com.sun.star.uno.XComponentContext");
    std::unique_ptr< ClassFile > cf(
        new ClassFile(
            static_cast< ClassFile::AccessFlags >(
                ClassFile::ACC_PUBLIC | ClassFile::ACC_FINAL
                | ClassFile::ACC_SUPER),
            className, "java/lang/Object", ""));
    MethodDescriptor desc(manager, dependencies, entity->getBase(), nullptr, nullptr);
    desc.addParameter("com.sun.star.uno.XComponentContext", false, false, nullptr);
    std::unique_ptr< ClassFile::Code > code(cf->newCode());
    code->loadLocalReference(0);
    // stack: context
    code->loadStringConstant("/singletons/" + unoName);
    // stack: context "..."
    code->instrInvokeinterface(
        "com/sun/star/uno/XComponentContext", "getValueByName",
        "(Ljava/lang/String;)Ljava/lang/Object;", 2);
    // stack: value
    code->instrDup();
    // stack: value value
    code->instrInstanceof("com/sun/star/uno/Any");
    // stack: value 0/1
    ClassFile::Code::Branch branch1 = code->instrIfeq();
    // stack: value
    code->instrCheckcast("com/sun/star/uno/Any");
    // stack: value
    code->instrDup();
    // stack: value value
    code->instrInvokevirtual(
        "com/sun/star/uno/Any", "getType", "()Lcom/sun/star/uno/Type;");
    // stack: value type
    code->instrInvokevirtual(
        "com/sun/star/uno/Type", "getTypeClass",
        "()Lcom/sun/star/uno/TypeClass;");
    // stack: value typeClass
    code->instrGetstatic(
        "com/sun/star/uno/TypeClass", "INTERFACE",
        "Lcom/sun/star/uno/TypeClass;");
    // stack: value typeClass INTERFACE
    ClassFile::Code::Branch branch2 = code->instrIfAcmpne();
    // stack: value
    code->instrInvokevirtual(
        "com/sun/star/uno/Any", "getObject", "()Ljava/lang/Object;");
    // stack: value
    code->branchHere(branch1);
    code->instrNew("com/sun/star/uno/Type");
    // stack: value type
    code->instrDup();
    // stack: value type type
    code->loadStringConstant(realJavaBaseName);
    // stack: value type type "..."
    code->instrGetstatic(
        "com/sun/star/uno/TypeClass", "INTERFACE",
        "Lcom/sun/star/uno/TypeClass;");
    // stack: value type type "..." INTERFACE
    code->instrInvokespecial(
        "com/sun/star/uno/Type", "<init>",
        "(Ljava/lang/String;Lcom/sun/star/uno/TypeClass;)V");
    // stack: value type
    code->instrSwap();
    // stack: type value
    code->instrInvokestatic(
        "com/sun/star/uno/UnoRuntime", "queryInterface",
        "(Lcom/sun/star/uno/Type;Ljava/lang/Object;)Ljava/lang/Object;");
    // stack: instance
    code->instrDup();
    // stack: instance instance
    ClassFile::Code::Branch branch3 = code->instrIfnull();
    // stack: instance
    code->instrCheckcast(base);
    // stack: instance
    code->instrAreturn();
    code->branchHere(branch2);
    code->branchHere(branch3);
    code->instrPop();
    // stack: -
    code->instrNew("com/sun/star/uno/DeploymentException");
    // stack: ex
    code->instrDup();
    // stack: ex ex
    code->loadStringConstant(
        "component context fails to supply singleton " + unoName + " of type "
        + realJavaBaseName);
    // stack: ex ex "..."
    code->loadLocalReference(0);
    // stack: ex ex "..." context
    code->instrInvokespecial(
        "com/sun/star/uno/DeploymentException", "<init>",
        "(Ljava/lang/String;Ljava/lang/Object;)V");
    // stack: ex
    code->instrAthrow();
    code->setMaxStackAndLocals(5, 1);
    cf->addMethod(
        static_cast< ClassFile::AccessFlags >(
            ClassFile::ACC_PUBLIC | ClassFile::ACC_STATIC),
        "get", desc.getDescriptor(), code.get(), std::vector< OString >(),
        desc.getSignature());
    writeClassFile(options, className, *cf.get());
}
 
}
 
void produce(
    OUString const & name, rtl::Reference< TypeManager > const & manager,
    codemaker::GeneratedTypeSet & generated, JavaOptions const & options)
{
    if (generated.contains(u2b(name))) {
        return;
    }
    generated.add(u2b(name));
    if (!manager->foundAtPrimaryProvider(name)) {
        return;
    }
    std::set<OUString> deps;
    rtl::Reference< unoidl::Entity > ent;
    rtl::Reference< unoidl::MapCursor > cur;
    switch (manager->getSort(name, &ent, &cur)) {
    case codemaker::UnoType::Sort::Module:
        {
            OUString prefix;
            if (!name.isEmpty()) {
                prefix = name + ".";
            }
            for (;;) {
                OUString mem;
                if (!cur->getNext(&mem).is()) {
                    break;
                }
                produce(prefix + mem, manager, generated, options);
            }
            return;
        }
    case codemaker::UnoType::Sort::Enum:
        handleEnumType(
            name, dynamic_cast< unoidl::EnumTypeEntity * >(ent.get()), options);
        break;
    case codemaker::UnoType::Sort::PlainStruct:
        handlePlainStructType(
            name, dynamic_cast< unoidl::PlainStructTypeEntity * >(ent.get()),
            manager, options, &deps);
        break;
    case codemaker::UnoType::Sort::PolymorphicStructTemplate:
        handlePolyStructType(
            name,
            dynamic_cast< unoidl::PolymorphicStructTypeTemplateEntity * >(
                ent.get()),
            manager, options, &deps);
        break;
    case codemaker::UnoType::Sort::Exception:
        handleExceptionType(
            name, dynamic_cast< unoidl::ExceptionTypeEntity * >(ent.get()),
            manager, options, &deps);
        break;
    case codemaker::UnoType::Sort::Interface:
        handleInterfaceType(
            name, dynamic_cast< unoidl::InterfaceTypeEntity * >(ent.get()),
            manager, options, &deps);
        break;
    case codemaker::UnoType::Sort::Typedef:
        handleTypedef(
            dynamic_cast< unoidl::TypedefEntity * >(ent.get()), manager, &deps);
        break;
    case codemaker::UnoType::Sort::ConstantGroup:
        handleConstantGroup(
            name, dynamic_cast< unoidl::ConstantGroupEntity * >(ent.get()),
            manager, options, &deps);
        break;
    case codemaker::UnoType::Sort::SingleInterfaceBasedService:
        handleService(
            name,
            dynamic_cast< unoidl::SingleInterfaceBasedServiceEntity * >(
                ent.get()),
            manager, options, &deps);
        break;
    case codemaker::UnoType::Sort::InterfaceBasedSingleton:
        handleSingleton(
            name,
            dynamic_cast< unoidl::InterfaceBasedSingletonEntity * >(ent.get()),
            manager, options, &deps);
        break;
    case codemaker::UnoType::Sort::AccumulationBasedService:
    case codemaker::UnoType::Sort::ServiceBasedSingleton:
        break;
    default:
        throw CannotDumpException(
            "unexpected entity \"" + name + "\" in call to produce");
    }
    if (!options.isValid("-nD")) {
        for (const OUString& d : deps) {
            produce(d, manager, generated, options);
        }
    }
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V1016 The value '0' is out of range of enum values. This causes unspecified or undefined behavior.

V1016 The value is out of range of enum 'AccessFlags' values. This causes unspecified or undefined behavior.