diff options
Diffstat (limited to 'codemaker/source/javamaker')
-rw-r--r-- | codemaker/source/javamaker/classfile.cxx | 828 | ||||
-rw-r--r-- | codemaker/source/javamaker/classfile.hxx | 239 | ||||
-rw-r--r-- | codemaker/source/javamaker/javamaker.cxx | 98 | ||||
-rw-r--r-- | codemaker/source/javamaker/javaoptions.cxx | 249 | ||||
-rw-r--r-- | codemaker/source/javamaker/javaoptions.hxx | 41 | ||||
-rw-r--r-- | codemaker/source/javamaker/javatype.cxx | 2539 | ||||
-rw-r--r-- | codemaker/source/javamaker/javatype.hxx | 35 |
7 files changed, 4029 insertions, 0 deletions
diff --git a/codemaker/source/javamaker/classfile.cxx b/codemaker/source/javamaker/classfile.cxx new file mode 100644 index 000000000..67177a718 --- /dev/null +++ b/codemaker/source/javamaker/classfile.cxx @@ -0,0 +1,828 @@ +/* -*- 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 "classfile.hxx" + +#include <codemaker/global.hxx> +#include <codemaker/options.hxx> +#include <codemaker/unotype.hxx> + +#include <osl/diagnose.h> +#include <rtl/string.h> +#include <rtl/string.hxx> +#include <sal/types.h> + +#include <map> +#include <utility> +#include <vector> + +using codemaker::javamaker::ClassFile; + +namespace { + +void appendU1(std::vector< unsigned char > & stream, sal_uInt8 data) { + stream.push_back(static_cast< unsigned char >(data)); +} + +void appendU2(std::vector< unsigned char > & stream, sal_uInt16 data) { + stream.push_back(static_cast< unsigned char >(data >> 8)); + stream.push_back(static_cast< unsigned char >(data & 0xFF)); +} + +void appendU4(std::vector< unsigned char > & stream, sal_uInt32 data) { + stream.push_back(static_cast< unsigned char >(data >> 24)); + stream.push_back(static_cast< unsigned char >((data >> 16) & 0xFF)); + stream.push_back(static_cast< unsigned char >((data >> 8) & 0xFF)); + stream.push_back(static_cast< unsigned char >(data & 0xFF)); +} + +void appendU8(std::vector< unsigned char > & stream, sal_uInt64 data) { + stream.push_back(static_cast< unsigned char >(data >> 56)); + stream.push_back(static_cast< unsigned char >((data >> 48) & 0xFF)); + stream.push_back(static_cast< unsigned char >((data >> 40) & 0xFF)); + stream.push_back(static_cast< unsigned char >((data >> 32) & 0xFF)); + stream.push_back(static_cast< unsigned char >((data >> 24) & 0xFF)); + stream.push_back(static_cast< unsigned char >((data >> 16) & 0xFF)); + stream.push_back(static_cast< unsigned char >((data >> 8) & 0xFF)); + stream.push_back(static_cast< unsigned char >(data & 0xFF)); +} + +void appendStream( + std::vector< unsigned char > & stream, + std::vector< unsigned char > const & data) +{ + stream.insert(stream.end(), data.begin(), data.end()); +} + +void write(FileStream & file, void const * buffer, sal_uInt64 size) { + if (!file.write(buffer, size)) + throw CannotDumpException("Error writing file"); +} + +void writeU2(FileStream & file, sal_uInt16 data) { + unsigned char buf[] = { + static_cast< unsigned char >(data >> 8), + static_cast< unsigned char >(data & 0xFF) }; + write(file, buf, sizeof buf); +} + +void writeU4(FileStream & file, sal_uInt32 data) { + unsigned char buf[] = { + static_cast< unsigned char >(data >> 24), + static_cast< unsigned char >((data >> 16) & 0xFF), + static_cast< unsigned char >((data >> 8) & 0xFF), + static_cast< unsigned char >(data & 0xFF) }; + write(file, buf, sizeof buf); +} + +void writeStream(FileStream & file, std::vector< unsigned char > const & stream) +{ + std::vector< unsigned char >::size_type n = stream.size(); + static_assert( + sizeof (std::vector< unsigned char >::size_type) + <= sizeof (sal_uInt64), "must be at most equal in size"); + // both unsigned integral, so sizeof is a practically sufficient + // approximation of std::numeric_limits<T1>::max() <= + // std::numeric_limits<T2>::max() + if (n != 0) { + write(file, stream.data(), static_cast< sal_uInt64 >(n)); + } +} + +} + +ClassFile::Code::~Code() {} + +void ClassFile::Code::instrAastore() { + // aastore: + appendU1(m_code, 0x53); +} + +void ClassFile::Code::instrAconstNull() { + // aconst_null: + appendU1(m_code, 0x01); +} + +void ClassFile::Code::instrAnewarray(OString const & type) { + // anewarray <indexbyte1> <indexbyte2>: + appendU1(m_code, 0xBD); + appendU2(m_code, m_classFile.addClassInfo(type)); +} + +void ClassFile::Code::instrAreturn() { + // areturn: + appendU1(m_code, 0xB0); +} + +void ClassFile::Code::instrAthrow() { + // athrow: + appendU1(m_code, 0xBF); +} + +void ClassFile::Code::instrCheckcast(OString const & type) { + // checkcast <indexbyte1> <indexbyte2>: + appendU1(m_code, 0xC0); + appendU2(m_code, m_classFile.addClassInfo(type)); +} + +void ClassFile::Code::instrDup() { + // dup: + appendU1(m_code, 0x59); +} + +void ClassFile::Code::instrGetstatic( + OString const & type, OString const & name, + OString const & descriptor) +{ + // getstatic <indexbyte1> <indexbyte2>: + appendU1(m_code, 0xB2); + appendU2(m_code, m_classFile.addFieldrefInfo(type, name, descriptor)); +} + +ClassFile::Code::Branch ClassFile::Code::instrIfAcmpne() { + // if_acmpne <branchbyte1> <branchbyte2>: + Branch branch = m_code.size(); + appendU1(m_code, 0xA6); + appendU2(m_code, 0); + return branch; +} + +ClassFile::Code::Branch ClassFile::Code::instrIfeq() { + // ifeq <branchbyte1> <branchbyte2>: + Branch branch = m_code.size(); + appendU1(m_code, 0x99); + appendU2(m_code, 0); + return branch; +} + +ClassFile::Code::Branch ClassFile::Code::instrIfnull() { + // ifnull <branchbyte1> <branchbyte2>: + Branch branch = m_code.size(); + appendU1(m_code, 0xC6); + appendU2(m_code, 0); + return branch; +} + +void ClassFile::Code::instrInstanceof(OString const & type) { + // instanceof <indexbyte1> <indexbyte2>: + appendU1(m_code, 0xC1); + appendU2(m_code, m_classFile.addClassInfo(type)); +} + +void ClassFile::Code::instrInvokeinterface( + OString const & type, OString const & name, + OString const & descriptor, sal_uInt8 args) +{ + // invokeinterface <indexbyte1> <indexbyte2> <nargs> 0: + appendU1(m_code, 0xB9); + appendU2( + m_code, m_classFile.addInterfaceMethodrefInfo(type, name, descriptor)); + appendU1(m_code, args); + appendU1(m_code, 0); +} + +void ClassFile::Code::instrInvokespecial( + OString const & type, OString const & name, + OString const & descriptor) +{ + // invokespecial <indexbyte1> <indexbyte2>: + appendU1(m_code, 0xB7); + appendU2(m_code, m_classFile.addMethodrefInfo(type, name, descriptor)); +} + +void ClassFile::Code::instrInvokestatic( + OString const & type, OString const & name, + OString const & descriptor) +{ + // invokestatic <indexbyte1> <indexbyte2>: + appendU1(m_code, 0xB8); + appendU2(m_code, m_classFile.addMethodrefInfo(type, name, descriptor)); +} + +void ClassFile::Code::instrInvokevirtual( + OString const & type, OString const & name, + OString const & descriptor) +{ + // invokevirtual <indexbyte1> <indexbyte2>: + appendU1(m_code, 0xB6); + appendU2(m_code, m_classFile.addMethodrefInfo(type, name, descriptor)); +} + +void ClassFile::Code::instrLookupswitch( + Code const * defaultBlock, + std::vector< std::pair< sal_Int32, Code * > > const & blocks) +{ + // lookupswitch <0--3 byte pad> <defaultbyte1> <defaultbyte2> <defaultbyte3> + // <defaultbyte4> <npairs1> <npairs2> <npairs3> <npairs4> + // <match--offset pairs...>: + std::vector< std::pair< sal_Int32, Code * > >::size_type size = blocks.size(); + if (size > SAL_MAX_INT32) { + throw CannotDumpException("Lookup-switch too large for Java class file format"); + } + Position pos1 = m_code.size(); + appendU1(m_code, 0xAB); + int pad = (pos1 + 1) % 4; + for (int i = 0; i < pad; ++i) { + appendU1(m_code, 0); + } + Position pos2 = pos1 + 1 + pad + 8 + blocks.size() * 8; //FIXME: overflow + appendU4(m_code, static_cast< sal_uInt32 >(pos2 - pos1)); //FIXME: overflow + pos2 += defaultBlock->m_code.size(); //FIXME: overflow + appendU4(m_code, static_cast< sal_uInt32 >(size)); + for (const std::pair< sal_Int32, Code * >& pair : blocks) + { + appendU4(m_code, static_cast< sal_uInt32 >(pair.first)); + appendU4(m_code, static_cast< sal_uInt32 >(pos2 - pos1)); + //FIXME: overflow + pos2 += pair.second->m_code.size(); //FIXME: overflow + } + appendStream(m_code, defaultBlock->m_code); + for (const std::pair< sal_Int32, Code * >& pair : blocks) + { + appendStream(m_code, pair.second->m_code); + } +} + +void ClassFile::Code::instrNew(OString const & type) { + // new <indexbyte1> <indexbyte2>: + appendU1(m_code, 0xBB); + appendU2(m_code, m_classFile.addClassInfo(type)); +} + +void ClassFile::Code::instrNewarray(codemaker::UnoType::Sort sort) { + OSL_ASSERT( + sort >= codemaker::UnoType::Sort::Boolean + && sort <= codemaker::UnoType::Sort::Char); + // newarray <atype>: + appendU1(m_code, 0xBC); + static sal_uInt8 const atypes[static_cast<int>(codemaker::UnoType::Sort::Char)] = { + 0x04, 0x08, 0x09, 0x09, 0x0A, 0x0A, 0x0B, 0x0B, 0x06, 0x07, 0x05 }; + appendU1(m_code, atypes[static_cast<int>(sort) - 1]); +} + +void ClassFile::Code::instrPop() { + // pop: + appendU1(m_code, 0x57); +} + +void ClassFile::Code::instrPutfield( + OString const & type, OString const & name, + OString const & descriptor) +{ + // putfield <indexbyte1> <indexbyte2>: + appendU1(m_code, 0xB5); + appendU2(m_code, m_classFile.addFieldrefInfo(type, name, descriptor)); +} + +void ClassFile::Code::instrPutstatic( + OString const & type, OString const & name, + OString const & descriptor) +{ + // putstatic <indexbyte1> <indexbyte2>: + appendU1(m_code, 0xB3); + appendU2(m_code, m_classFile.addFieldrefInfo(type, name, descriptor)); +} + +void ClassFile::Code::instrReturn() { + // return: + appendU1(m_code, 0xB1); +} + +void ClassFile::Code::instrSwap() { + // swap: + appendU1(m_code, 0x5F); +} + +void ClassFile::Code::instrTableswitch( + Code const * defaultBlock, sal_Int32 low, + std::vector< std::unique_ptr<Code> > const & blocks) +{ + // tableswitch <0--3 byte pad> <defaultbyte1> <defaultbyte2> <defaultbyte3> + // <defaultbyte4> <lowbyte1> <lowbyte2> <lowbyte3> <lowbyte4> <highbyte1> + // <highbyte2> <highbyte3> <highbyte4> <jump offsets...>: + Position pos1 = m_code.size(); + appendU1(m_code, 0xAA); + int pad = (pos1 + 1) % 4; + for (int i = 0; i < pad; ++i) { + appendU1(m_code, 0); + } + std::vector< Code * >::size_type size = blocks.size(); + Position pos2 = pos1 + 1 + pad + 12 + size * 4; //FIXME: overflow + sal_uInt32 defaultOffset = static_cast< sal_uInt32 >(pos2 - pos1); + //FIXME: overflow + appendU4(m_code, defaultOffset); + pos2 += defaultBlock->m_code.size(); //FIXME: overflow + appendU4(m_code, static_cast< sal_uInt32 >(low)); + appendU4(m_code, static_cast< sal_uInt32 >(low + (size - 1))); + for (std::unique_ptr<Code> const & pCode : blocks) + { + if (pCode == nullptr) { + appendU4(m_code, defaultOffset); + } else { + appendU4(m_code, static_cast< sal_uInt32 >(pos2 - pos1)); + //FIXME: overflow + pos2 += pCode->m_code.size(); //FIXME: overflow + } + } + appendStream(m_code, defaultBlock->m_code); + for (std::unique_ptr<Code> const & pCode : blocks) + { + if (pCode != nullptr) { + appendStream(m_code, pCode->m_code); + } + } +} + +void ClassFile::Code::loadIntegerConstant(sal_Int32 value) { + if (value >= -1 && value <= 5) { + // iconst_<i>: + appendU1(m_code, static_cast< sal_uInt8 >(0x02 + value + 1)); + } else if (value >= -128 && value <= 127) { + // bipush <byte>: + appendU1(m_code, 0x10); + appendU1(m_code, static_cast< sal_uInt8 >(value)); + } else if (value >= -32768 && value <= 32767) { + // sipush <byte1> <byte2>: + appendU1(m_code, 0x11); + appendU2(m_code, static_cast< sal_uInt16 >(value)); + } else { + ldc(m_classFile.addIntegerInfo(value)); + } +} + +void ClassFile::Code::loadStringConstant(OString const & value) { + ldc(m_classFile.addStringInfo(value)); +} + +void ClassFile::Code::loadLocalInteger(sal_uInt16 index) { + accessLocal(index, 0x1A, 0x15); // iload_<n>, iload +} + +void ClassFile::Code::loadLocalLong(sal_uInt16 index) { + accessLocal(index, 0x1E, 0x16); // load_<n>, load +} + +void ClassFile::Code::loadLocalFloat(sal_uInt16 index) { + accessLocal(index, 0x22, 0x17); // load_<n>, load +} + +void ClassFile::Code::loadLocalDouble(sal_uInt16 index) { + accessLocal(index, 0x26, 0x18); // load_<n>, load +} + +void ClassFile::Code::loadLocalReference(sal_uInt16 index) { + accessLocal(index, 0x2A, 0x19); // aload_<n>, aload +} + +void ClassFile::Code::storeLocalReference(sal_uInt16 index) { + accessLocal(index, 0x4B, 0x3A); // astore_<n>, astore +} + +void ClassFile::Code::branchHere(Branch branch) { + std::vector< unsigned char >::size_type n = m_code.size(); + OSL_ASSERT(n > branch && n - branch <= SAL_MAX_INT16); + n -= branch; + m_code[branch + 1] = static_cast< sal_uInt8 >(n >> 8); + m_code[branch + 2] = static_cast< sal_uInt8 >(n & 0xFF); +} + +void ClassFile::Code::addException( + Position start, Position end, Position handler, OString const & type) +{ + OSL_ASSERT(start < end && end <= m_code.size() && handler <= m_code.size()); + if (m_exceptionTableLength == SAL_MAX_UINT16) { + throw CannotDumpException("Too many exception handlers for Java class file format"); + } + ++m_exceptionTableLength; + appendU2(m_exceptionTable, static_cast< sal_uInt16 >(start)); + //FIXME: overflow + appendU2(m_exceptionTable, static_cast< sal_uInt16 >(end)); + //FIXME: overflow + appendU2(m_exceptionTable, static_cast< sal_uInt16 >(handler)); + //FIXME: overflow + appendU2(m_exceptionTable, m_classFile.addClassInfo(type)); +} + +ClassFile::Code::Position ClassFile::Code::getPosition() const { + return m_code.size(); +} + +ClassFile::Code::Code(ClassFile & classFile) + : m_classFile(classFile) + , m_maxStack(0) + , m_maxLocals(0) + , m_exceptionTableLength(0) +{} + +void ClassFile::Code::ldc(sal_uInt16 index) { + if (index <= 0xFF) { + // ldc <index>: + appendU1(m_code, 0x12); + appendU1(m_code, static_cast< sal_uInt8 >(index)); + } else { + // ldc_w <indexbyte1> <indexbyte2>: + appendU1(m_code, 0x13); + appendU2(m_code, index); + } +} + +void ClassFile::Code::accessLocal( + sal_uInt16 index, sal_uInt8 fastOp, sal_uInt8 normalOp) +{ + if (index <= 3) { + // ...load/store_<n>: + appendU1(m_code, static_cast< sal_uInt8 >(fastOp + index)); + } else if (index <= 0xFF) { + // ...load/store <index>: + appendU1(m_code, normalOp); + appendU1(m_code, static_cast< sal_uInt8 >(index)); + } else { + // wide ...load/store <indexbyte1> <indexbyte2>: + appendU1(m_code, 0xC4); + appendU1(m_code, normalOp); + appendU2(m_code, index); + } +} + +ClassFile::ClassFile( + AccessFlags accessFlags, OString const & thisClass, + OString const & superClass, OString const & signature): + m_constantPoolCount(1), m_accessFlags(accessFlags), m_interfacesCount(0), + m_fieldsCount(0), m_methodsCount(0), m_attributesCount(0) +{ + m_thisClass = addClassInfo(thisClass); + m_superClass = addClassInfo(superClass); + if (!signature.isEmpty()) { + ++m_attributesCount; + appendU2(m_attributes, addUtf8Info("Signature")); + appendU4(m_attributes, 2); + appendU2(m_attributes, addUtf8Info(signature)); + } +} + +ClassFile::~ClassFile() {} + +std::unique_ptr<ClassFile::Code> ClassFile::newCode() { + return std::unique_ptr<Code>(new Code(*this)); +} + +sal_uInt16 ClassFile::addIntegerInfo(sal_Int32 value) { + std::map< sal_Int32, sal_uInt16 >::iterator i(m_integerInfos.find(value)); + if (i != m_integerInfos.end()) { + return i->second; + } + sal_uInt16 index = nextConstantPoolIndex(1); + appendU1(m_constantPool, 3); + appendU4(m_constantPool, static_cast< sal_uInt32 >(value)); + if (!m_integerInfos.emplace(value, index).second) + { + OSL_ASSERT(false); + } + return index; +} + +sal_uInt16 ClassFile::addFloatInfo(float value) { + std::map< float, sal_uInt16 >::iterator i(m_floatInfos.find(value)); + if (i != m_floatInfos.end()) { + return i->second; + } + sal_uInt16 index = nextConstantPoolIndex(1); + appendU1(m_constantPool, 4); + union { float floatBytes; sal_uInt32 uint32Bytes; } bytes; + bytes.floatBytes = value; + appendU4(m_constantPool, bytes.uint32Bytes); + if (!m_floatInfos.emplace(value, index).second) + { + OSL_ASSERT(false); + } + return index; +} + +sal_uInt16 ClassFile::addLongInfo(sal_Int64 value) { + std::map< sal_Int64, sal_uInt16 >::iterator i(m_longInfos.find(value)); + if (i != m_longInfos.end()) { + return i->second; + } + sal_uInt16 index = nextConstantPoolIndex(2); + appendU1(m_constantPool, 5); + appendU8(m_constantPool, static_cast< sal_uInt64 >(value)); + if (!m_longInfos.emplace(value, index).second) + { + OSL_ASSERT(false); + } + return index; +} + +sal_uInt16 ClassFile::addDoubleInfo(double value) { + std::map< double, sal_uInt16 >::iterator i(m_doubleInfos.find(value)); + if (i != m_doubleInfos.end()) { + return i->second; + } + sal_uInt16 index = nextConstantPoolIndex(2); + appendU1(m_constantPool, 6); + union { double doubleBytes; sal_uInt64 uint64Bytes; } bytes; + bytes.doubleBytes = value; + appendU8(m_constantPool, bytes.uint64Bytes); + if (!m_doubleInfos.emplace(value, index).second) + { + OSL_ASSERT(false); + } + return index; +} + +void ClassFile::addInterface(OString const & interface) { + if (m_interfacesCount == SAL_MAX_UINT16) { + throw CannotDumpException("Too many interfaces for Java class file format"); + } + ++m_interfacesCount; + appendU2(m_interfaces, addClassInfo(interface)); +} + +void ClassFile::addField( + AccessFlags accessFlags, OString const & name, + OString const & descriptor, sal_uInt16 constantValueIndex, + OString const & signature) +{ + if (m_fieldsCount == SAL_MAX_UINT16) { + throw CannotDumpException("Too many fields for Java class file format"); + } + ++m_fieldsCount; + appendU2(m_fields, static_cast< sal_uInt16 >(accessFlags)); + appendU2(m_fields, addUtf8Info(name)); + appendU2(m_fields, addUtf8Info(descriptor)); + appendU2( + m_fields, + ((constantValueIndex == 0 ? 0 : 1) + + (signature.isEmpty() ? 0 : 1))); + if (constantValueIndex != 0) { + appendU2(m_fields, addUtf8Info("ConstantValue")); + appendU4(m_fields, 2); + appendU2(m_fields, constantValueIndex); + } + appendSignatureAttribute(m_fields, signature); +} + +void ClassFile::addMethod( + AccessFlags accessFlags, OString const & name, + OString const & descriptor, Code const * code, + std::vector< OString > const & exceptions, + OString const & signature) +{ + if (m_methodsCount == SAL_MAX_UINT16) { + throw CannotDumpException("Too many methods for Java class file format"); + } + ++m_methodsCount; + appendU2(m_methods, static_cast< sal_uInt16 >(accessFlags)); + appendU2(m_methods, addUtf8Info(name)); + appendU2(m_methods, addUtf8Info(descriptor)); + std::vector< OString >::size_type excs = exceptions.size(); + if (excs > SAL_MAX_UINT16) { + throw CannotDumpException("Too many exception specifications for Java class file format"); + } + appendU2( + m_methods, + ((code == nullptr ? 0 : 1) + (exceptions.empty() ? 0 : 1) + + (signature.isEmpty() ? 0 : 1))); + if (code != nullptr) { + std::vector< unsigned char >::size_type codeSize = code->m_code.size(); + std::vector< unsigned char >::size_type exceptionTableSize + = code->m_exceptionTable.size(); + if (codeSize > SAL_MAX_UINT32 - (2 + 2 + 4 + 2 + 2) + || (exceptionTableSize + > (SAL_MAX_UINT32 - (2 + 2 + 4 + 2 + 2) + - static_cast< sal_uInt32 >(codeSize)))) + { + throw CannotDumpException("Code block is too big for Java class file format"); + } + appendU2(m_methods, addUtf8Info("Code")); + appendU4( + m_methods, + (2 + 2 + 4 + static_cast< sal_uInt32 >(codeSize) + 2 + + static_cast< sal_uInt32 >(exceptionTableSize) + 2)); + appendU2(m_methods, code->m_maxStack); + appendU2(m_methods, code->m_maxLocals); + appendU4(m_methods, static_cast< sal_uInt32 >(codeSize)); + appendStream(m_methods, code->m_code); + appendU2(m_methods, code->m_exceptionTableLength); + appendStream(m_methods, code->m_exceptionTable); + appendU2(m_methods, 0); + } + if (!exceptions.empty()) { + appendU2(m_methods, addUtf8Info("Exceptions")); + appendU4( + m_methods, + static_cast< sal_uInt32 >(2 + 2 * static_cast< sal_uInt32 >(excs))); + appendU2(m_methods, static_cast< sal_uInt16 >(excs)); + for (const OString& ex : exceptions) + { + appendU2(m_methods, addClassInfo(ex)); + } + } + appendSignatureAttribute(m_methods, signature); +} + +void ClassFile::write(FileStream & file) const { + writeU4(file, 0xCAFEBABE); + writeU2(file, 0); + writeU2(file, 49); // class file version of JRE 1.5 + writeU2(file, m_constantPoolCount); + writeStream(file, m_constantPool); + writeU2(file, static_cast< sal_uInt16 >(m_accessFlags)); + writeU2(file, m_thisClass); + writeU2(file, m_superClass); + writeU2(file, m_interfacesCount); + writeStream(file, m_interfaces); + writeU2(file, m_fieldsCount); + writeStream(file, m_fields); + writeU2(file, m_methodsCount); + writeStream(file, m_methods); + writeU2(file, m_attributesCount); + writeStream(file, m_attributes); +} + +sal_uInt16 ClassFile::nextConstantPoolIndex(sal_uInt16 width) { + OSL_ASSERT(width == 1 || width == 2); + if (m_constantPoolCount > SAL_MAX_UINT16 - width) { + throw CannotDumpException("Too many constant pool items for Java class file format"); + } + sal_uInt16 index = m_constantPoolCount; + m_constantPoolCount = m_constantPoolCount + width; + return index; +} + +sal_uInt16 ClassFile::addUtf8Info(OString const & value) { + std::map< OString, sal_uInt16 >::iterator i(m_utf8Infos.find(value)); + if (i != m_utf8Infos.end()) { + return i->second; + } + if (value.getLength() > SAL_MAX_UINT16) { + throw CannotDumpException("UTF-8 string too long for Java class file format"); + } + sal_uInt16 index = nextConstantPoolIndex(1); + appendU1(m_constantPool, 1); + appendU2(m_constantPool, static_cast< sal_uInt16 >(value.getLength())); + for (sal_Int32 j = 0; j < value.getLength(); ++j) { + appendU1(m_constantPool, static_cast< sal_uInt8 >(value[j])); + } + if (!m_utf8Infos.emplace(value, index).second) + { + OSL_ASSERT(false); + } + return index; +} + +sal_uInt16 ClassFile::addClassInfo(OString const & type) { + sal_uInt16 nameIndex = addUtf8Info(type); + std::map< sal_uInt16, sal_uInt16 >::iterator i( + m_classInfos.find(nameIndex)); + if (i != m_classInfos.end()) { + return i->second; + } + sal_uInt16 index = nextConstantPoolIndex(1); + appendU1(m_constantPool, 7); + appendU2(m_constantPool, nameIndex); + if (!m_classInfos.emplace(nameIndex, index).second) + { + OSL_ASSERT(false); + } + return index; +} + +sal_uInt16 ClassFile::addStringInfo(OString const & value) { + sal_uInt16 stringIndex = addUtf8Info(value); + std::map< sal_uInt16, sal_uInt16 >::iterator i( + m_stringInfos.find(stringIndex)); + if (i != m_stringInfos.end()) { + return i->second; + } + sal_uInt16 index = nextConstantPoolIndex(1); + appendU1(m_constantPool, 8); + appendU2(m_constantPool, stringIndex); + if (!m_stringInfos.emplace(stringIndex, index).second) + { + OSL_ASSERT(false); + } + return index; +} + +sal_uInt16 ClassFile::addFieldrefInfo( + OString const & type, OString const & name, + OString const & descriptor) +{ + sal_uInt16 classIndex = addClassInfo(type); + sal_uInt16 nameAndTypeIndex = addNameAndTypeInfo(name, descriptor); + sal_uInt32 key = (static_cast< sal_uInt32 >(classIndex) << 16) + | nameAndTypeIndex; + std::map< sal_uInt32, sal_uInt16 >::iterator i(m_fieldrefInfos.find(key)); + if (i != m_fieldrefInfos.end()) { + return i->second; + } + sal_uInt16 index = nextConstantPoolIndex(1); + appendU1(m_constantPool, 9); + appendU2(m_constantPool, classIndex); + appendU2(m_constantPool, nameAndTypeIndex); + if (!m_fieldrefInfos.emplace(key, index).second) + { + OSL_ASSERT(false); + } + return index; +} + +sal_uInt16 ClassFile::addMethodrefInfo( + OString const & type, OString const & name, + OString const & descriptor) +{ + sal_uInt16 classIndex = addClassInfo(type); + sal_uInt16 nameAndTypeIndex = addNameAndTypeInfo(name, descriptor); + sal_uInt32 key = (static_cast< sal_uInt32 >(classIndex) << 16) + | nameAndTypeIndex; + std::map< sal_uInt32, sal_uInt16 >::iterator i(m_methodrefInfos.find(key)); + if (i != m_methodrefInfos.end()) { + return i->second; + } + sal_uInt16 index = nextConstantPoolIndex(1); + appendU1(m_constantPool, 10); + appendU2(m_constantPool, classIndex); + appendU2(m_constantPool, nameAndTypeIndex); + if (!m_methodrefInfos.emplace(key, index).second) + { + OSL_ASSERT(false); + } + return index; +} + +sal_uInt16 ClassFile::addInterfaceMethodrefInfo( + OString const & type, OString const & name, + OString const & descriptor) +{ + sal_uInt16 classIndex = addClassInfo(type); + sal_uInt16 nameAndTypeIndex = addNameAndTypeInfo(name, descriptor); + sal_uInt32 key = (static_cast< sal_uInt32 >(classIndex) << 16) + | nameAndTypeIndex; + std::map< sal_uInt32, sal_uInt16 >::iterator i( + m_interfaceMethodrefInfos.find(key)); + if (i != m_interfaceMethodrefInfos.end()) { + return i->second; + } + sal_uInt16 index = nextConstantPoolIndex(1); + appendU1(m_constantPool, 11); + appendU2(m_constantPool, classIndex); + appendU2(m_constantPool, nameAndTypeIndex); + if (!m_interfaceMethodrefInfos.emplace(key, index).second) + { + OSL_ASSERT(false); + } + return index; +} + +sal_uInt16 ClassFile::addNameAndTypeInfo( + OString const & name, OString const & descriptor) +{ + sal_uInt16 nameIndex = addUtf8Info(name); + sal_uInt16 descriptorIndex = addUtf8Info(descriptor); + sal_uInt32 key = (static_cast< sal_uInt32 >(nameIndex) << 16) + | descriptorIndex; + std::map< sal_uInt32, sal_uInt16 >::iterator i( + m_nameAndTypeInfos.find(key)); + if (i != m_nameAndTypeInfos.end()) { + return i->second; + } + sal_uInt16 index = nextConstantPoolIndex(1); + appendU1(m_constantPool, 12); + appendU2(m_constantPool, nameIndex); + appendU2(m_constantPool, descriptorIndex); + if (!m_nameAndTypeInfos.emplace(key, index).second) + { + OSL_ASSERT(false); + } + return index; +} + +void ClassFile::appendSignatureAttribute( + std::vector< unsigned char > & stream, OString const & signature) +{ + if (!signature.isEmpty()) { + appendU2(stream, addUtf8Info("Signature")); + appendU4(stream, 2); + appendU2(stream, addUtf8Info(signature)); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/codemaker/source/javamaker/classfile.hxx b/codemaker/source/javamaker/classfile.hxx new file mode 100644 index 000000000..ebfef0782 --- /dev/null +++ b/codemaker/source/javamaker/classfile.hxx @@ -0,0 +1,239 @@ +/* -*- 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 . + */ + +#pragma once + +#include <codemaker/unotype.hxx> +#include <sal/types.h> + +#include <map> +#include <memory> +#include <utility> +#include <vector> + +class FileStream; + +namespace codemaker::javamaker { + +class ClassFile { +public: + enum AccessFlags { + ACC_PUBLIC = 0x0001, + ACC_PRIVATE = 0x0002, + ACC_STATIC = 0x0008, + ACC_FINAL = 0x0010, + ACC_SUPER = 0x0020, + ACC_VARARGS = 0x0080, + ACC_INTERFACE = 0x0200, + ACC_ABSTRACT = 0x0400, + ACC_SYNTHETIC = 0x1000 + }; + + class Code { + public: + typedef std::vector< unsigned char >::size_type Branch; + typedef std::vector< unsigned char >::size_type Position; + + ~Code(); + + void instrAastore(); + void instrAconstNull(); + void instrAnewarray(rtl::OString const & type); + void instrAreturn(); + void instrAthrow(); + void instrCheckcast(rtl::OString const & type); + void instrDup(); + + void instrGetstatic( + rtl::OString const & type, rtl::OString const & name, + rtl::OString const & descriptor); + + Branch instrIfAcmpne(); + Branch instrIfeq(); + Branch instrIfnull(); + + void instrInstanceof(rtl::OString const & type); + + void instrInvokeinterface( + rtl::OString const & type, rtl::OString const & name, + rtl::OString const & descriptor, sal_uInt8 args); + + void instrInvokespecial( + rtl::OString const & type, rtl::OString const & name, + rtl::OString const & descriptor); + + void instrInvokestatic( + rtl::OString const & type, rtl::OString const & name, + rtl::OString const & descriptor); + + void instrInvokevirtual( + rtl::OString const & type, rtl::OString const & name, + rtl::OString const & descriptor); + + void instrLookupswitch( + Code const * defaultBlock, + std::vector< std::pair< sal_Int32, Code * > > const & blocks); + + void instrNew(rtl::OString const & type); + void instrNewarray(codemaker::UnoType::Sort sort); + void instrPop(); + + void instrPutfield( + rtl::OString const & type, rtl::OString const & name, + rtl::OString const & descriptor); + + void instrPutstatic( + rtl::OString const & type, rtl::OString const & name, + rtl::OString const & descriptor); + + void instrReturn(); + void instrSwap(); + + void instrTableswitch( + Code const * defaultBlock, sal_Int32 low, + std::vector< std::unique_ptr<Code> > const & blocks); + + void loadIntegerConstant(sal_Int32 value); + void loadStringConstant(rtl::OString const & value); + void loadLocalInteger(sal_uInt16 index); + void loadLocalLong(sal_uInt16 index); + void loadLocalFloat(sal_uInt16 index); + void loadLocalDouble(sal_uInt16 index); + void loadLocalReference(sal_uInt16 index); + void storeLocalReference(sal_uInt16 index); + void branchHere(Branch branch); + + void addException( + Position start, Position end, Position handler, + rtl::OString const & type); + + void setMaxStackAndLocals(sal_uInt16 maxStack, sal_uInt16 maxLocals) + { m_maxStack = maxStack; m_maxLocals = maxLocals; } + + Position getPosition() const; + + private: + Code(Code const &) = delete; + Code& operator =(const Code&) = delete; + + explicit Code(ClassFile & classFile); + + void ldc(sal_uInt16 index); + + void accessLocal( + sal_uInt16 index, sal_uInt8 fastOp, sal_uInt8 normalOp); + + ClassFile & m_classFile; + sal_uInt16 m_maxStack; + sal_uInt16 m_maxLocals; + std::vector< unsigned char > m_code; + sal_uInt16 m_exceptionTableLength; + std::vector< unsigned char > m_exceptionTable; + + friend class ClassFile; + }; + + ClassFile( + AccessFlags accessFlags, rtl::OString const & thisClass, + rtl::OString const & superClass, rtl::OString const & signature); + + ~ClassFile(); + + std::unique_ptr<Code> newCode(); + + sal_uInt16 addIntegerInfo(sal_Int32 value); + sal_uInt16 addFloatInfo(float value); + sal_uInt16 addLongInfo(sal_Int64 value); + sal_uInt16 addDoubleInfo(double value); + + void addInterface(rtl::OString const & interface); + + void addField( + AccessFlags accessFlags, rtl::OString const & name, + rtl::OString const & descriptor, sal_uInt16 constantValueIndex, + rtl::OString const & signature); + + void addMethod( + AccessFlags accessFlags, rtl::OString const & name, + rtl::OString const & descriptor, Code const * code, + std::vector< rtl::OString > const & exceptions, + rtl::OString const & signature); + + void write(FileStream & file) const; //TODO + +private: + typedef std::map< rtl::OString, sal_uInt16 > Map; + + ClassFile(ClassFile const &) = delete; + ClassFile& operator =(const ClassFile&) = delete; + + sal_uInt16 nextConstantPoolIndex(sal_uInt16 width); + sal_uInt16 addUtf8Info(rtl::OString const & value); + sal_uInt16 addClassInfo(rtl::OString const & type); + sal_uInt16 addStringInfo(rtl::OString const & value); + + sal_uInt16 addFieldrefInfo( + rtl::OString const & type, rtl::OString const & name, + rtl::OString const & descriptor); + + sal_uInt16 addMethodrefInfo( + rtl::OString const & type, rtl::OString const & name, + rtl::OString const & descriptor); + + sal_uInt16 addInterfaceMethodrefInfo( + rtl::OString const & type, rtl::OString const & name, + rtl::OString const & descriptor); + + sal_uInt16 addNameAndTypeInfo( + rtl::OString const & name, rtl::OString const & descriptor); + + void appendSignatureAttribute( + std::vector< unsigned char > & stream, rtl::OString const & signature); + + sal_uInt16 m_constantPoolCount; + std::vector< unsigned char > m_constantPool; + std::map< rtl::OString, sal_uInt16 > m_utf8Infos; + std::map< sal_Int32, sal_uInt16 > m_integerInfos; + std::map< sal_Int64, sal_uInt16 > m_longInfos; + std::map< float, sal_uInt16 > m_floatInfos; + std::map< double, sal_uInt16 > m_doubleInfos; + std::map< sal_uInt16, sal_uInt16 > m_classInfos; + std::map< sal_uInt16, sal_uInt16 > m_stringInfos; + std::map< sal_uInt32, sal_uInt16 > m_fieldrefInfos; + std::map< sal_uInt32, sal_uInt16 > m_methodrefInfos; + std::map< sal_uInt32, sal_uInt16 > m_interfaceMethodrefInfos; + std::map< sal_uInt32, sal_uInt16 > m_nameAndTypeInfos; + AccessFlags m_accessFlags; + sal_uInt16 m_thisClass; + sal_uInt16 m_superClass; + sal_uInt16 m_interfacesCount; + std::vector< unsigned char > m_interfaces; + sal_uInt16 m_fieldsCount; + std::vector< unsigned char > m_fields; + sal_uInt16 m_methodsCount; + std::vector< unsigned char > m_methods; + sal_uInt16 m_attributesCount; + std::vector< unsigned char > m_attributes; + + friend class Code; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/codemaker/source/javamaker/javamaker.cxx b/codemaker/source/javamaker/javamaker.cxx new file mode 100644 index 000000000..ee376c8cc --- /dev/null +++ b/codemaker/source/javamaker/javamaker.cxx @@ -0,0 +1,98 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <cstdlib> +#include <cstring> +#include <iostream> +#include <vector> + +#include <codemaker/generatedtypeset.hxx> +#include <codemaker/typemanager.hxx> +#include <rtl/ref.hxx> +#include <rtl/string.hxx> +#include <rtl/ustring.hxx> +#include <sal/main.h> +#include <sal/types.h> +#include <unoidl/unoidl.hxx> +#include <o3tl/string_view.hxx> + +#include "javaoptions.hxx" +#include "javatype.hxx" + +// coverity[tainted_data] - this is a build time tool +SAL_IMPLEMENT_MAIN_WITH_ARGS(argc, argv) { + JavaOptions options; + try { + if (!options.initOptions(argc, argv)) { + return EXIT_FAILURE; + } + + rtl::Reference typeMgr(new TypeManager); + for (const OString& i : options.getExtraInputFiles()) + { + typeMgr->loadProvider(convertToFileUrl(i), false); + } + for (const OString& i : options.getInputFiles()) + { + typeMgr->loadProvider(convertToFileUrl(i), true); + } + codemaker::GeneratedTypeSet generated; + if (options.isValid("-T")) { + OUString names(b2u(options.getOption("-T"))); + for (sal_Int32 i = 0; i != -1;) { + std::u16string_view name(o3tl::getToken(names, 0, ';', i)); + if (!name.empty()) { + produce( + OUString(name == u"*" + ? u"" + : o3tl::ends_with(name, u".*") + ? name.substr(0, name.size() - std::strlen(".*")) + : name), + typeMgr, generated, options); + } + } + } else { + produce("", typeMgr, generated, options); + } + } + catch (CannotDumpException & e) { + std::cerr << "ERROR: " << e.getMessage() << '\n'; + return EXIT_FAILURE; + } catch (unoidl::NoSuchFileException & e) { + std::cerr << "ERROR: No such file <" << e.getUri() << ">\n"; + return EXIT_FAILURE; + } catch (unoidl::FileFormatException & e) { + std::cerr + << "ERROR: Bad format of <" << e.getUri() << ">, \"" + << e.getDetail() << "\"\n"; + return EXIT_FAILURE; + } catch (IllegalArgument & e) { + std::cerr << "Illegal option " << e.m_message << '\n'; + return EXIT_FAILURE; + } catch (std::exception & e) { + std::cerr << "Failure " << e.what() << '\n'; + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/codemaker/source/javamaker/javaoptions.cxx b/codemaker/source/javamaker/javaoptions.cxx new file mode 100644 index 000000000..e0e51c973 --- /dev/null +++ b/codemaker/source/javamaker/javaoptions.cxx @@ -0,0 +1,249 @@ +/* -*- 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 <stdio.h> +#include <string.h> +#include "javaoptions.hxx" +#include <osl/process.h> +#include <osl/thread.h> + + +#ifdef SAL_UNX +#define SEPARATOR '/' +#else +#define SEPARATOR '\\' +#endif + +bool JavaOptions::initOptions(int ac, char* av[], bool bCmdFile) +{ + bool ret = true; + int i=0; + + if (!bCmdFile) + { + bCmdFile = true; + + OString name(av[0]); + sal_Int32 index = name.lastIndexOf(SEPARATOR); + m_program = name.copy(index > 0 ? index+1 : 0); + + if (ac < 2) + { + fprintf(stderr, "%s", prepareHelp().getStr()); + ret = false; + } + + i = 1; + } + + char *s=nullptr; + for( ; i < ac; i++) + { + if (av[i][0] == '-') + { + switch (av[i][1]) + { + case 'O': + if (av[i][2] == '\0') + { + if (i < ac - 1 && av[i+1][0] != '-') + { + i++; + s = av[i]; + } else + { + OString tmp("'-O', please check"); + if (i <= ac - 1) + { + tmp += OString::Concat(" your input '") + av[i+1] + "'"; + } + + throw IllegalArgument(tmp); + } + } else + { + s = av[i] + 2; + } + + m_options["-O"] = OString(s); + break; + case 'n': + if (av[i][2] != 'D' || av[i][3] != '\0') + { + OString tmp(OString::Concat("'-nD', please check your input '") + av[i] + "'"); + throw IllegalArgument(tmp); + } + + m_options["-nD"] = OString(); + break; + case 'T': + if (av[i][2] == '\0') + { + if (i < ac - 1 && av[i+1][0] != '-') + { + i++; + s = av[i]; + } else + { + OString tmp("'-T', please check"); + if (i <= ac - 1) + { + tmp += OString::Concat(" your input '") + av[i+1] + "'"; + } + + throw IllegalArgument(tmp); + } + } else + { + s = av[i] + 2; + } + + if (m_options.count("-T") > 0) + { + OString tmp = m_options["-T"] + ";" + s; + m_options["-T"] = tmp; + } else + { + m_options["-T"] = OString(s); + } + break; + case 'G': + if (av[i][2] == 'c') + { + if (av[i][3] != '\0') + { + OString tmp("'-Gc', please check"); + if (i <= ac - 1) + { + tmp += OString::Concat(" your input '") + av[i] + "'"; + } + + throw IllegalArgument(tmp); + } + + m_options["-Gc"] = OString(); + break; + } else if (av[i][2] != '\0') + { + OString tmp("'-G', please check"); + if (i <= ac - 1) + { + tmp += OString::Concat(" your input '") + av[i] + "'"; + } + + throw IllegalArgument(tmp); + } + + m_options["-G"] = OString(); + break; + case 'X': // support for eXtra type rdbs + { + if (av[i][2] == '\0') + { + if (i < ac - 1 && av[i+1][0] != '-') + { + i++; + s = av[i]; + } else + { + OString tmp("'-X', please check"); + if (i <= ac - 1) + { + tmp += OString::Concat(" your input '") + av[i+1] + "'"; + } + + throw IllegalArgument(tmp); + } + } else + { + s = av[i] + 2; + } + + m_extra_input_files.emplace_back(s ); + break; + } + + default: + throw IllegalArgument(OString::Concat("the option is unknown") + av[i]); + } + } else + { + if (av[i][0] == '@') + { + FILE* cmdFile = fopen(av[i]+1, "r"); + if( cmdFile == nullptr ) + { + fprintf(stderr, "%s", prepareHelp().getStr()); + ret = false; + } else + { + int rargc=0; + char* rargv[512]; + char buffer[512]; + + while (fscanf(cmdFile, "%511s", buffer) != EOF && rargc < 512) + { + rargv[rargc]= strdup(buffer); + rargc++; + } + fclose(cmdFile); + + ret = initOptions(rargc, rargv, bCmdFile); + + for (int j=0; j < rargc; j++) + { + free(rargv[j]); + } + } + } else + { + m_inputFiles.emplace_back(av[i]); + } + } + } + + return ret; +} + +OString JavaOptions::prepareHelp() +{ + OString help = "\nusing: " + + m_program + " [-options] file_1 ... file_n -Xfile_n+1 -Xfile_n+2\nOptions:\n" + " -O<path> = path describes the root directory for the generated output.\n" + " The output directory tree is generated under this directory.\n" + " -T<name> = name specifies a type or a list of types. The output for this\n" + " [t1;...] type and all dependent types are generated. If no '-T' option is\n" + " specified, then output for all types is generated.\n" + " Example: 'com.sun.star.uno.XInterface' is a valid type.\n" + " -nD = no dependent types are generated.\n" + " -G = generate only target files which does not exists.\n" + " -Gc = generate only target files which content will be changed.\n" + " -X<file> = extra types which will not be taken into account for generation.\n\n" + + prepareVersion(); + + return help; +} + +OString JavaOptions::prepareVersion() const +{ + return m_program + " Version 2.0\n\n"; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/codemaker/source/javamaker/javaoptions.hxx b/codemaker/source/javamaker/javaoptions.hxx new file mode 100644 index 000000000..5e6161c04 --- /dev/null +++ b/codemaker/source/javamaker/javaoptions.hxx @@ -0,0 +1,41 @@ +/* -*- 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 . + */ + +#pragma once + +#include <codemaker/options.hxx> + +class JavaOptions : public Options +{ +public: + JavaOptions() + : Options() + { + } + + bool initOptions(int ac, char* av[], bool bCmdFile = false) override; + + OString prepareHelp() override; + + OString prepareVersion() const; + +protected: +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/codemaker/source/javamaker/javatype.cxx b/codemaker/source/javamaker/javatype.cxx new file mode 100644 index 000000000..3db9572e5 --- /dev/null +++ b/codemaker/source/javamaker/javatype.cxx @@ -0,0 +1,2539 @@ +/* -*- 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 <string_view> +#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 <o3tl/string_view.hxx> + +#include "classfile.hxx" +#include "javaoptions.hxx" +#include "javatype.hxx" + +using codemaker::javamaker::ClassFile; + +namespace { + +void appendUnoName( + rtl::Reference< TypeManager > const & manager, std::u16string_view 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()) + return; + + 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, std::u16string_view nucleus, + sal_Int32 rank, std::vector< OUString > const & arguments) +{ + OUStringBuffer buf(256); + 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( + std::u16string_view name, std::string_view prefix) +{ + assert(!o3tl::starts_with(name, u"[]")); + assert(name.find('<') == std::string_view::npos); + size_t i = name.rfind('.'); + if (i == std::string_view::npos) + i = 0; + else + ++i; + return codemaker::convertString(OUString(name.substr(0, i))).replace('.', '/') + + codemaker::java::translateUnoToJavaIdentifier( + codemaker::convertString(OUString(name.substr(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, std::u16string_view 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; + } + [[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, std::u16string_view 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, + std::u16string_view type, OString * descriptor, OString * signature, + PolymorphicUnoType * polymorphicUnoType) +{ + assert(descriptor != nullptr); + OStringBuffer desc(64); + OStringBuffer sig(64); + 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 > manager, + std::set<OUString> * dependencies, std::u16string_view returnType, + SpecialType * specialReturnType, + PolymorphicUnoType * polymorphicUnoType); + + SpecialType addParameter( + std::u16string_view type, bool array, bool dependency, + PolymorphicUnoType * polymorphicUnoType); + + void addTypeParameter(OUString const & name); + + OString getDescriptor() const; + + OString getSignature() const { return m_needsSignature ? m_signatureStart + m_signatureEnd : OString();} + +private: + rtl::Reference< TypeManager > m_manager; + std::set<OUString> * m_dependencies; + OStringBuffer m_descriptorStart{16*1024}; + OString m_descriptorEnd; + OStringBuffer m_signatureStart{16*1024}; + OString m_signatureEnd; + bool m_needsSignature; +}; + +MethodDescriptor::MethodDescriptor( + rtl::Reference< TypeManager > manager, std::set<OUString> * dependencies, + std::u16string_view returnType, SpecialType * specialReturnType, + PolymorphicUnoType * polymorphicUnoType): + m_manager(std::move(manager)), m_dependencies(dependencies), m_needsSignature(false) +{ + assert(dependencies != nullptr); + m_descriptorStart.append('('); + m_signatureStart.append('('); + OStringBuffer descEnd(128); + descEnd.append(')'); + OStringBuffer sigEnd(128); + 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( + std::u16string_view 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 { + return m_descriptorStart + m_descriptorEnd; +} + + +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 name, SpecialType specialType, sal_Int32 index, + PolymorphicUnoType const & polymorphicUnoType, + sal_Int32 typeParameterIndex); + + // KIND_ATTRIBUTE/METHOD: + TypeInfo( + Kind kind, OString name, SpecialType specialType, Flags flags, + sal_Int32 index, PolymorphicUnoType polymorphicUnoType); + + // KIND_PARAMETER: + TypeInfo( + OString parameterName, SpecialType specialType, + bool inParameter, bool outParameter, OString methodName, + sal_Int32 index, PolymorphicUnoType 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 name, SpecialType specialType, sal_Int32 index, + PolymorphicUnoType const & polymorphicUnoType, + sal_Int32 typeParameterIndex): + m_kind(KIND_MEMBER), m_name(std::move(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 name, SpecialType specialType, Flags flags, + sal_Int32 index, PolymorphicUnoType polymorphicUnoType): + m_kind(kind), m_name(std::move(name)), + m_flags(flags | translateSpecialTypeFlags(specialType, false, false)), + m_index(index), m_polymorphicUnoType(std::move(polymorphicUnoType)), + m_typeParameterIndex(0) +{ + assert(kind == KIND_ATTRIBUTE || kind == KIND_METHOD); +} + +TypeInfo::TypeInfo( + OString parameterName, SpecialType specialType, bool inParameter, + bool outParameter, OString methodName, sal_Int32 index, + PolymorphicUnoType polymorphicUnoType): + m_kind(KIND_PARAMETER), m_name(std::move(parameterName)), + m_flags(translateSpecialTypeFlags(specialType, inParameter, outParameter)), + m_index(index), m_methodName(std::move(methodName)), + m_polymorphicUnoType(std::move(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) + return; + + 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 = 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 = 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< std::unique_ptr<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(std::move(blockCode)); + } + code->instrTableswitch(defCode.get(), min, blocks); + } 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.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 = 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); +} + +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, std::u16string_view 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< 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(128); + 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(128); + 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( + OUString::Concat("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(128); + translateUnoTypeToDescriptor( + manager, sort, nucleus, rank - 1, std::vector< OUString >(), false, + false, dependencies, &desc, nullptr, nullptr, nullptr); + code->instrAnewarray(desc.makeStringAndClear()); + } + OStringBuffer desc(128); + 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, std::u16string_view 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( + OUString::Concat("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( + OUString::Concat("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)); + 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, u"void", nullptr, nullptr); + code = 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); +} + +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(128); + sig.append("<"); + 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, u"void", nullptr, nullptr); + code = 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); +} + +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)); + 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, + u"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 = 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, + u"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 = 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, + u"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 = 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, + u"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, u"void", nullptr, nullptr); + code = cf->newCode(); + code->loadLocalReference(0); + sal_uInt16 index2 = 1; + code->loadLocalReference(index2++); + desc1.addParameter(u"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, u"void", nullptr, nullptr); + code = 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(u"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>", OString::Concat("(Ljava/lang/Throwable;") + desc2.getDescriptor().subView(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>", OString::Concat("(Ljava/lang/Throwable;") + desc2.getDescriptor().subView(1), code.get(), + std::vector< OString >(), desc2.getSignature()); + + addTypeInfo(className, typeInfo, dependencies, cf.get()); + writeClassFile(options, className, *cf); +} + +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, u"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); +} + +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); +} + +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, + std::string_view 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(u"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(u"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); +} + +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(u"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); +} + +} + +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: */ diff --git a/codemaker/source/javamaker/javatype.hxx b/codemaker/source/javamaker/javatype.hxx new file mode 100644 index 000000000..9df1d94b5 --- /dev/null +++ b/codemaker/source/javamaker/javatype.hxx @@ -0,0 +1,35 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/config.h> + +#include <rtl/ref.hxx> + +namespace codemaker { class GeneratedTypeSet; } +namespace rtl { class OUString; } +class JavaOptions; +class TypeManager; + +void produce( + OUString const & name, rtl::Reference< TypeManager > const & manager, + codemaker::GeneratedTypeSet & generated, JavaOptions const & options); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |