summaryrefslogtreecommitdiffstats
path: root/basic/source/comp/codegen.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'basic/source/comp/codegen.cxx')
-rw-r--r--basic/source/comp/codegen.cxx585
1 files changed, 585 insertions, 0 deletions
diff --git a/basic/source/comp/codegen.cxx b/basic/source/comp/codegen.cxx
new file mode 100644
index 0000000000..9f2f4960bf
--- /dev/null
+++ b/basic/source/comp/codegen.cxx
@@ -0,0 +1,585 @@
+/* -*- 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 <basic/sberrors.hxx>
+#include <basic/sbx.hxx>
+#include <basic/sbmeth.hxx>
+#include <basic/sbmod.hxx>
+#include <image.hxx>
+#include <codegen.hxx>
+#include <parser.hxx>
+#include <sbintern.hxx>
+#include <cstddef>
+#include <limits>
+#include <algorithm>
+#include <osl/diagnose.h>
+#include <rtl/ustrbuf.hxx>
+#include <o3tl/string_view.hxx>
+#include <com/sun/star/script/ModuleType.hpp>
+
+// nInc is the increment size of the buffers
+
+SbiCodeGen::SbiCodeGen(SbModule& r, SbiParser* p)
+ : pParser(p)
+ , rMod(r)
+ , nLine(0)
+ , nCol(0)
+ , nForLevel(0)
+ , bStmnt(false)
+{
+}
+
+sal_uInt32 SbiCodeGen::GetPC() const
+{
+ return aCode.GetSize();
+}
+
+// memorize the statement
+
+void SbiCodeGen::Statement()
+{
+ if( pParser->IsCodeCompleting() )
+ return;
+
+ bStmnt = true;
+
+ nLine = pParser->GetLine();
+ nCol = pParser->GetCol1();
+
+ // #29955 Store the information of the for-loop-layer
+ // in the upper Byte of the column
+ nCol = (nCol & 0xff) + 0x100 * nForLevel;
+}
+
+// Mark the beginning of a statement
+
+void SbiCodeGen::GenStmnt()
+{
+ if( pParser->IsCodeCompleting() )
+ return;
+
+ if( bStmnt )
+ {
+ bStmnt = false;
+ Gen( SbiOpcode::STMNT_, nLine, nCol );
+ }
+}
+
+// The Gen-Routines return the offset of the 1. operand,
+// so that jumps can sink their backchain there.
+
+sal_uInt32 SbiCodeGen::Gen( SbiOpcode eOpcode )
+{
+ if( pParser->IsCodeCompleting() )
+ return 0;
+
+#ifdef DBG_UTIL
+ if( eOpcode < SbiOpcode::SbOP0_START || eOpcode > SbiOpcode::SbOP0_END )
+ pParser->Error( ERRCODE_BASIC_INTERNAL_ERROR, "OPCODE1" );
+#endif
+ GenStmnt();
+ aCode += static_cast<sal_uInt8>(eOpcode);
+ return GetPC();
+}
+
+sal_uInt32 SbiCodeGen::Gen( SbiOpcode eOpcode, sal_uInt32 nOpnd )
+{
+ if( pParser->IsCodeCompleting() )
+ return 0;
+
+#ifdef DBG_UTIL
+ if( eOpcode < SbiOpcode::SbOP1_START || eOpcode > SbiOpcode::SbOP1_END )
+ pParser->Error( ERRCODE_BASIC_INTERNAL_ERROR, "OPCODE2" );
+#endif
+ GenStmnt();
+ aCode += static_cast<sal_uInt8>(eOpcode);
+ sal_uInt32 n = GetPC();
+ aCode += nOpnd;
+ return n;
+}
+
+sal_uInt32 SbiCodeGen::Gen( SbiOpcode eOpcode, sal_uInt32 nOpnd1, sal_uInt32 nOpnd2 )
+{
+ if( pParser->IsCodeCompleting() )
+ return 0;
+
+#ifdef DBG_UTIL
+ if( eOpcode < SbiOpcode::SbOP2_START || eOpcode > SbiOpcode::SbOP2_END )
+ pParser->Error( ERRCODE_BASIC_INTERNAL_ERROR, "OPCODE3" );
+#endif
+ GenStmnt();
+ aCode += static_cast<sal_uInt8>(eOpcode);
+ sal_uInt32 n = GetPC();
+ aCode += nOpnd1;
+ aCode += nOpnd2;
+ return n;
+}
+
+// Storing of the created image in the module
+
+void SbiCodeGen::Save()
+{
+ if( pParser->IsCodeCompleting() )
+ return;
+
+ std::unique_ptr<SbiImage> p(new SbiImage);
+ rMod.StartDefinitions();
+ // OPTION BASE-Value:
+ p->nDimBase = pParser->nBase;
+ // OPTION take over the EXPLICIT-Flag
+ if( pParser->bExplicit )
+ p->SetFlag( SbiImageFlags::EXPLICIT );
+
+ int nIfaceCount = 0;
+ if( rMod.mnType == css::script::ModuleType::CLASS )
+ {
+ rMod.bIsProxyModule = true;
+ p->SetFlag( SbiImageFlags::CLASSMODULE );
+ GetSbData()->pClassFac->AddClassModule( &rMod );
+
+ nIfaceCount = pParser->aIfaceVector.size();
+ if( !rMod.pClassData )
+ rMod.pClassData.reset(new SbClassData);
+ if( nIfaceCount )
+ {
+ for( int i = 0 ; i < nIfaceCount ; i++ )
+ {
+ const OUString& rIfaceName = pParser->aIfaceVector[i];
+ SbxVariable* pIfaceVar = new SbxVariable( SbxVARIANT );
+ pIfaceVar->SetName( rIfaceName );
+ SbxArray* pIfaces = rMod.pClassData->mxIfaces.get();
+ pIfaces->Insert( pIfaceVar, pIfaces->Count() );
+ }
+ }
+
+ rMod.pClassData->maRequiredTypes = pParser->aRequiredTypes;
+ }
+ else
+ {
+ GetSbData()->pClassFac->RemoveClassModule( &rMod );
+ // Only a ClassModule can revert to Normal
+ if ( rMod.mnType == css::script::ModuleType::CLASS )
+ {
+ rMod.mnType = css::script::ModuleType::NORMAL;
+ }
+ rMod.bIsProxyModule = false;
+ }
+
+ // GlobalCode-Flag
+ if( pParser->HasGlobalCode() )
+ {
+ p->SetFlag( SbiImageFlags::INITCODE );
+ }
+ // The entry points:
+ for( SbiSymDef* pDef = pParser->aPublics.First(); pDef;
+ pDef = pParser->aPublics.Next() )
+ {
+ SbiProcDef* pProc = pDef->GetProcDef();
+ if( pProc && pProc->IsDefined() )
+ {
+ OUString aProcName = pProc->GetName();
+ OUStringBuffer aIfaceProcName;
+ OUString aIfaceName;
+ sal_uInt16 nPassCount = 1;
+ if( nIfaceCount )
+ {
+ int nPropPrefixFound = aProcName.indexOf("Property ");
+ std::u16string_view aPureProcName = aProcName;
+ std::u16string_view aPropPrefix;
+ if( nPropPrefixFound == 0 )
+ {
+ aPropPrefix = aProcName.subView( 0, 13 ); // 13 == Len( "Property ?et " )
+ aPureProcName = aProcName.subView( 13 );
+ }
+ for( int i = 0 ; i < nIfaceCount ; i++ )
+ {
+ const OUString& rIfaceName = pParser->aIfaceVector[i];
+ bool bFound = o3tl::starts_with(aPureProcName, rIfaceName );
+ if( bFound && aPureProcName[rIfaceName.getLength()] == '_' )
+ {
+ if( nPropPrefixFound == 0 )
+ {
+ aIfaceProcName.append(aPropPrefix);
+ }
+ aIfaceProcName.append(aPureProcName.substr(rIfaceName.getLength() + 1) );
+ aIfaceName = rIfaceName;
+ nPassCount = 2;
+ break;
+ }
+ }
+ }
+ SbMethod* pMeth = nullptr;
+ for( sal_uInt16 nPass = 0 ; nPass < nPassCount ; nPass++ )
+ {
+ if( nPass == 1 )
+ {
+ aProcName = aIfaceProcName.toString();
+ }
+ PropertyMode ePropMode = pProc->getPropertyMode();
+ if( ePropMode != PropertyMode::NONE )
+ {
+ SbxDataType ePropType = SbxEMPTY;
+ switch( ePropMode )
+ {
+ case PropertyMode::Get:
+ ePropType = pProc->GetType();
+ break;
+ case PropertyMode::Let:
+ {
+ // type == type of first parameter
+ ePropType = SbxVARIANT; // Default
+ SbiSymPool* pPool = &pProc->GetParams();
+ if( pPool->GetSize() > 1 )
+ {
+ SbiSymDef* pPar = pPool->Get( 1 );
+ if( pPar )
+ {
+ ePropType = pPar->GetType();
+ }
+ }
+ break;
+ }
+ case PropertyMode::Set:
+ ePropType = SbxOBJECT;
+ break;
+ default:
+ OSL_FAIL("Illegal PropertyMode");
+ break;
+ }
+ OUString aPropName = pProc->GetPropName();
+ if( nPass == 1 )
+ {
+ aPropName = aPropName.copy( aIfaceName.getLength() + 1 );
+ }
+ rMod.GetProcedureProperty( aPropName, ePropType );
+ }
+ if( nPass == 1 )
+ {
+ rMod.GetIfaceMapperMethod( aProcName, pMeth );
+ }
+ else
+ {
+ pMeth = rMod.GetMethod( aProcName, pProc->GetType() );
+
+ if( !pProc->IsPublic() )
+ {
+ pMeth->SetFlag( SbxFlagBits::Private );
+ }
+ // Declare? -> Hidden
+ if( !pProc->GetLib().isEmpty())
+ {
+ pMeth->SetFlag( SbxFlagBits::Hidden );
+ }
+ pMeth->nStart = pProc->GetAddr();
+ pMeth->nLine1 = pProc->GetLine1();
+ pMeth->nLine2 = pProc->GetLine2();
+ // The parameter:
+ SbxInfo* pInfo = pMeth->GetInfo();
+ OUString aHelpFile, aComment;
+ sal_uInt32 nHelpId = 0;
+ if( pInfo )
+ {
+ // Rescue the additional data
+ aHelpFile = pInfo->GetHelpFile();
+ aComment = pInfo->GetComment();
+ nHelpId = pInfo->GetHelpId();
+ }
+ // And reestablish the parameter list
+ pInfo = new SbxInfo( aHelpFile, nHelpId );
+ pInfo->SetComment( aComment );
+ SbiSymPool* pPool = &pProc->GetParams();
+ // The first element is always the value of the function!
+ for( sal_uInt16 i = 1; i < pPool->GetSize(); i++ )
+ {
+ SbiSymDef* pPar = pPool->Get( i );
+ SbxDataType t = pPar->GetType();
+ if( !pPar->IsByVal() )
+ {
+ t = static_cast<SbxDataType>( t | SbxBYREF );
+ }
+ if( pPar->GetDims() )
+ {
+ t = static_cast<SbxDataType>( t | SbxARRAY );
+ }
+ // #33677 hand-over an Optional-Info
+ SbxFlagBits nFlags = SbxFlagBits::Read;
+ if( pPar->IsOptional() )
+ {
+ nFlags |= SbxFlagBits::Optional;
+ }
+ pInfo->AddParam( pPar->GetName(), t, nFlags );
+
+ sal_uInt32 nUserData = 0;
+ sal_uInt16 nDefaultId = pPar->GetDefaultId();
+ if( nDefaultId )
+ {
+ nUserData |= nDefaultId;
+ }
+ if( pPar->IsParamArray() )
+ {
+ nUserData |= PARAM_INFO_PARAMARRAY;
+ }
+ if( pPar->IsWithBrackets() )
+ {
+ nUserData |= PARAM_INFO_WITHBRACKETS;
+ }
+ SbxParamInfo* pParam = nullptr;
+ if( nUserData )
+ {
+ pParam = const_cast<SbxParamInfo*>(pInfo->GetParam( i ));
+ }
+ if( pParam )
+ {
+ pParam->nUserData = nUserData;
+ }
+ }
+ pMeth->SetInfo( pInfo );
+ }
+ } // for( iPass...
+ }
+ }
+ if (aCode.GetErrCode())
+ {
+ pParser->Error(aCode.GetErrCode(), aCode.GetErrMessage());
+ }
+ // The code
+ p->AddCode(aCode.GetBuffer());
+
+ // The global StringPool. 0 is not occupied.
+ SbiStringPool* pPool = &pParser->aGblStrings;
+ sal_uInt16 nSize = pPool->GetSize();
+ p->MakeStrings( nSize );
+ sal_uInt32 i;
+ for( i = 1; i <= nSize; i++ )
+ {
+ p->AddString( pPool->Find( i ) );
+ }
+ // Insert types
+ sal_uInt32 nCount = pParser->rTypeArray->Count();
+ for (i = 0; i < nCount; i++)
+ {
+ p->AddType(static_cast<SbxObject *>(pParser->rTypeArray->Get(i)));
+ }
+ // Insert enum objects
+ nCount = pParser->rEnumArray->Count();
+ for (i = 0; i < nCount; i++)
+ {
+ p->AddEnum(static_cast<SbxObject *>(pParser->rEnumArray->Get(i)));
+ }
+ if( !p->IsError() )
+ {
+ rMod.pImage = std::move(p);
+ }
+ rMod.EndDefinitions();
+}
+
+namespace {
+
+template < class T >
+class PCodeVisitor
+{
+public:
+ virtual ~PCodeVisitor();
+
+ virtual void start( const sal_uInt8* pStart ) = 0;
+ virtual void processOpCode0( SbiOpcode eOp ) = 0;
+ virtual void processOpCode1( SbiOpcode eOp, T nOp1 ) = 0;
+ virtual void processOpCode2( SbiOpcode eOp, T nOp1, T nOp2 ) = 0;
+ virtual bool processParams() = 0;
+};
+
+}
+
+template <class T> PCodeVisitor< T >::~PCodeVisitor()
+{}
+
+namespace {
+
+template <class T>
+class PCodeBufferWalker
+{
+private:
+ T m_nBytes;
+ const sal_uInt8* m_pCode;
+ static T readParam( sal_uInt8 const *& pCode )
+ {
+ T nOp1=0;
+ for ( std::size_t i=0; i<sizeof( T ); ++i )
+ nOp1 |= *pCode++ << ( i * 8);
+ return nOp1;
+ }
+public:
+ PCodeBufferWalker( const sal_uInt8* pCode, T nBytes ): m_nBytes( nBytes ), m_pCode( pCode )
+ {
+ }
+ void visitBuffer( PCodeVisitor< T >& visitor )
+ {
+ const sal_uInt8* pCode = m_pCode;
+ if ( !pCode )
+ return;
+ const sal_uInt8* pEnd = pCode + m_nBytes;
+ visitor.start( m_pCode );
+ T nOp1 = 0, nOp2 = 0;
+ for( ; pCode < pEnd; )
+ {
+ SbiOpcode eOp = static_cast<SbiOpcode>(*pCode++);
+
+ if ( eOp <= SbiOpcode::SbOP0_END )
+ visitor.processOpCode0( eOp );
+ else if( eOp >= SbiOpcode::SbOP1_START && eOp <= SbiOpcode::SbOP1_END )
+ {
+ if ( visitor.processParams() )
+ nOp1 = readParam( pCode );
+ else
+ pCode += sizeof( T );
+ visitor.processOpCode1( eOp, nOp1 );
+ }
+ else if( eOp >= SbiOpcode::SbOP2_START && eOp <= SbiOpcode::SbOP2_END )
+ {
+ if ( visitor.processParams() )
+ {
+ nOp1 = readParam( pCode );
+ nOp2 = readParam( pCode );
+ }
+ else
+ pCode += ( sizeof( T ) * 2 );
+ visitor.processOpCode2( eOp, nOp1, nOp2 );
+ }
+ }
+ }
+};
+
+template < class T, class S >
+class OffSetAccumulator : public PCodeVisitor< T >
+{
+ T m_nNumOp0;
+ T m_nNumSingleParams;
+ T m_nNumDoubleParams;
+public:
+
+ OffSetAccumulator() : m_nNumOp0(0), m_nNumSingleParams(0), m_nNumDoubleParams(0){}
+ virtual void start( const sal_uInt8* /*pStart*/ ) override {}
+ virtual void processOpCode0( SbiOpcode /*eOp*/ ) override { ++m_nNumOp0; }
+ virtual void processOpCode1( SbiOpcode /*eOp*/, T /*nOp1*/ ) override { ++m_nNumSingleParams; }
+ virtual void processOpCode2( SbiOpcode /*eOp*/, T /*nOp1*/, T /*nOp2*/ ) override { ++m_nNumDoubleParams; }
+ S offset()
+ {
+ typedef decltype(T(1) + S(1)) larger_t; // type capable to hold both value ranges of T and S
+ T result = 0 ;
+ static const S max = std::numeric_limits< S >::max();
+ result = m_nNumOp0 + ( ( sizeof(S) + 1 ) * m_nNumSingleParams ) + ( (( sizeof(S) * 2 )+ 1 ) * m_nNumDoubleParams );
+ return std::min<larger_t>(max, result);
+ }
+ virtual bool processParams() override { return false; }
+};
+
+
+template < class T, class S >
+class BufferTransformer : public PCodeVisitor< T >
+{
+ const sal_uInt8* m_pStart;
+ SbiBuffer m_ConvertedBuf;
+public:
+ BufferTransformer():m_pStart(nullptr) {}
+ virtual void start( const sal_uInt8* pStart ) override { m_pStart = pStart; }
+ virtual void processOpCode0( SbiOpcode eOp ) override
+ {
+ m_ConvertedBuf += static_cast<sal_uInt8>(eOp);
+ }
+ virtual void processOpCode1( SbiOpcode eOp, T nOp1 ) override
+ {
+ m_ConvertedBuf += static_cast<sal_uInt8>(eOp);
+ switch( eOp )
+ {
+ case SbiOpcode::JUMP_:
+ case SbiOpcode::JUMPT_:
+ case SbiOpcode::JUMPF_:
+ case SbiOpcode::GOSUB_:
+ case SbiOpcode::CASEIS_:
+ case SbiOpcode::RETURN_:
+ case SbiOpcode::ERRHDL_:
+ case SbiOpcode::TESTFOR_:
+ nOp1 = static_cast<T>( convertBufferOffSet(m_pStart, nOp1) );
+ break;
+ case SbiOpcode::RESUME_:
+ if ( nOp1 > 1 )
+ nOp1 = static_cast<T>( convertBufferOffSet(m_pStart, nOp1) );
+ break;
+ default:
+ break;
+
+ }
+ m_ConvertedBuf += static_cast<S>(nOp1);
+ }
+ virtual void processOpCode2( SbiOpcode eOp, T nOp1, T nOp2 ) override
+ {
+ m_ConvertedBuf += static_cast<sal_uInt8>(eOp);
+ if ( eOp == SbiOpcode::CASEIS_ && nOp1 )
+ nOp1 = static_cast<T>( convertBufferOffSet(m_pStart, nOp1) );
+ m_ConvertedBuf += static_cast<S>(nOp1);
+ m_ConvertedBuf += static_cast<S>(nOp2);
+
+ }
+ virtual bool processParams() override { return true; }
+ // yeuch, careful here, you can only call
+ // GetBuffer on the returned SbiBuffer once, also
+ // you (as the caller) get to own the memory
+ SbiBuffer& buffer()
+ {
+ return m_ConvertedBuf;
+ }
+ static S convertBufferOffSet( const sal_uInt8* pStart, T nOp1 )
+ {
+ PCodeBufferWalker< T > aBuff( pStart, nOp1);
+ OffSetAccumulator< T, S > aVisitor;
+ aBuff.visitBuffer( aVisitor );
+ return aVisitor.offset();
+ }
+};
+
+}
+
+sal_uInt32
+SbiCodeGen::calcNewOffSet( sal_uInt8 const * pCode, sal_uInt16 nOffset )
+{
+ return BufferTransformer< sal_uInt16, sal_uInt32 >::convertBufferOffSet( pCode, nOffset );
+}
+
+sal_uInt16
+SbiCodeGen::calcLegacyOffSet( sal_uInt8 const * pCode, sal_uInt32 nOffset )
+{
+ return BufferTransformer< sal_uInt32, sal_uInt16 >::convertBufferOffSet( pCode, nOffset );
+}
+
+template <class T, class S>
+void
+PCodeBuffConvertor<T,S>::convert()
+{
+ PCodeBufferWalker< T > aBuf( m_pStart, m_nSize );
+ BufferTransformer< T, S > aTrnsfrmer;
+ aBuf.visitBuffer( aTrnsfrmer );
+ // TODO: handle buffer errors
+ m_aCnvtdBuf = aTrnsfrmer.buffer().GetBuffer();
+}
+
+template class PCodeBuffConvertor< sal_uInt16, sal_uInt32 >;
+template class PCodeBuffConvertor< sal_uInt32, sal_uInt16 >;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */