blob: f611b7db89d340a0bc8be90a51dd049d0be4fead [file] [log] [blame]
/**************************************************************
*
* 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
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_basic.hxx"
#include <stdio.h>
#include <string.h>
#include <tools/stream.hxx>
#include <basic/sbx.hxx>
#include "sb.hxx"
#include "iosys.hxx"
#include "disas.hxx"
#include "sbtrace.hxx"
static const char* pOp1[] = {
"NOP",
// Operators
// the following operators have the same order as in
// enum SbxVarOp
"EXP", "MUL", "DIV", "MOD", "PLUS", "MINUS", "NEG",
"EQ", "NE", "LT", "GT", "LE", "GE",
"IDIV", "AND", "OR", "XOR", "EQV", "IMP", "NOT",
"CAT",
// End enum SbxVarOp
"LIKE", "IS",
// Load/Store
"ARGC", // Create new Argv
"ARGV", // TOS ==> current Argv
"INPUT", // Input ==> TOS
"LINPUT", // Line Input ==> TOS
"GET", // get TOS
"SET", // Save Object TOS ==> TOS-1
"PUT", // TOS ==> TOS-1
"CONST", // TOS ==> TOS-1, then ReadOnly
"DIM", // DIM
"REDIM", // REDIM
"REDIMP", // REDIM PRESERVE
"ERASE", // delete TOS
// Branch
"STOP", // End of program
"INITFOR", // FOR-Variable init
"NEXT", // FOR-Variable increment
"CASE", // Begin CASE
"ENDCASE", // End CASE
"STDERR", // Default error handling
"NOERROR", // No error handling
"LEAVE", // leave UP
// I/O
"CHANNEL", // TOS = Channelnumber
"PRINT", // print TOS
"PRINTF", // print TOS in field
"WRITE", // write TOS
"RENAME", // Rename Tos+1 to Tos
"PROMPT", // TOS = Prompt for Input
"RESTART", // Define restart point
"STDIO", // Switch to I/O channel 0
// Misc
"EMPTY", // Empty statement to stack
"ERROR", // TOS = error code
"LSET", // Save object TOS ==> TOS-1
"RSET", // Save object TOS ==> TOS-1 (TODO: Same as above?)
"REDIMP_ERASE",
"INITFOREACH",
"VBASET",
"ERASE_CLEAR",
"ARRAYACCESS",
"BYVAL"
};
static const char* pOp2[] = {
"NUMBER", // Load a numeric constant (+ID)
"STRING", // Load a string constant (+ID)
"CONSTANT", // Immediate Load (+value)
"ARGN", // Save named args in argv (+StringID)
"PAD", // Pad String to defined length (+length)
// Branches
"JUMP", // Jump to target (+Target)
"JUMP.T", // evaluate TOS, conditional jump (+Target)
"JUMP.F", // evaluate TOS, conditional jump (+Target)
"ONJUMP", // evaluate TOS, jump into JUMP-table (+MaxVal)
"GOSUB", // UP-Call (+Target)
"RETURN", // UP-Return (+0 or Target)
"TESTFOR", // Test FOR-Variable, increment (+Endlabel)
"CASETO", // Tos+1 <= Case <= Tos, 2xremove (+Target)
"ERRHDL", // Error-Handler (+Offset)
"RESUME", // Resume after errors (+0 or 1 or Label)
// I/O
"CLOSE", // (+channel/0)
"PRCHAR", // (+char)
// Objects
"SETCLASS", // Test Set + Classname (+StringId)
"TESTCLASS", // Check TOS class (+StringId)
"LIB", // Set Libname for Declare-Procs (+StringId)
// New since Beta 3 (TODO: Which Beta3?)
"BASED", // TOS is incremted about BASE, push BASE before
"ARGTYP", // Convert last parameter in argv (+Type)
"VBASETCLASS",
};
static const char* pOp3[] = {
// All opcodes with two operands
"RTL", // Load from RTL (+StringID+Typ)
"FIND", // Load (+StringID+Typ)
"ELEM", // Load element (+StringID+Typ)
"PARAM", // Parameter (+Offset+Typ)
// Branching
"CALL", // Call DECLARE method (+StringID+Typ)
"CALL.C", // Call Cdecl-DECLARE method (+StringID+Typ)
"CASEIS", // Case-Test (+Test-Opcode+False-Target)
"STMNT", // Start of a statement (+Line+Col)
// I/O
"OPEN", // (+SvStreamFlags+Flags)
// Objects and variables
"LOCAL", // Local variables (+StringID+Typ)
"PUBLIC", // Modul global var (+StringID+Typ)
"GLOBAL", // Global var (+StringID+Typ)
"CREATE", // Create object (+StringId+StringId)
"STATIC", // Create static object (+StringId+StringId)
"TCREATE", // Create User defined Object (+StringId+StringId)
"DCREATE", // Create User defined Object-Array kreieren (+StringId+StringId)
"GLOBAL_P", // Define persistent global var (existing after basic restart)
// P=PERSIST (+StringID+Typ)
"FIND_G", // Searches for global var with special handling due to _GLOBAL_P
"DCREATE_REDIMP", // Change dimensions of a user defined Object-Array (+StringId+StringId)
"FIND_CM", // Search inside a class module (CM) to enable global search in time
"PUBLIC_P", // Module global Variable (persisted between calls)(+StringID+Typ)
"FIND_STATIC", // local static var lookup (+StringID+Typ)
};
static const char** pOps[3] = { pOp1, pOp2, pOp3 };
typedef void( SbiDisas::*Func )( String& ); // Processing routines
static const Func pOperand2[] = {
&SbiDisas::StrOp, // Load a numeric constant (+ID)
&SbiDisas::StrOp, // Load a string constant (+ID)
&SbiDisas::ImmOp, // Immediate Load (+Wert)
&SbiDisas::StrOp, // Save a named argument (+ID)
&SbiDisas::ImmOp, // Strip String to fixed size (+length)
// Branches
&SbiDisas::LblOp, // Jump (+Target)
&SbiDisas::LblOp, // eval TOS, conditional jump (+Target)
&SbiDisas::LblOp, // eval TOS, conditional jump (+Target)
&SbiDisas::OnOp, // eval TOS, jump in JUMP table (+MaxVal)
&SbiDisas::LblOp, // UP call (+Target)
&SbiDisas::ReturnOp, // UP Return (+0 or Target)
&SbiDisas::LblOp, // test FOR-Variable, increment (+Endlabel)
&SbiDisas::LblOp, // Tos+1 <= Case <= Tos), 2xremove (+Target)
&SbiDisas::LblOp, // Error handler (+Offset)
&SbiDisas::ResumeOp, // Resume after errors (+0 or 1 or Label)
// I/O
&SbiDisas::CloseOp, // (+channel/0)
&SbiDisas::CharOp, // (+char)
// Objects
&SbiDisas::StrOp, // Test classname (+StringId)
&SbiDisas::StrOp, // TESTCLASS, Check TOS class (+StringId)
&SbiDisas::StrOp, // Set libname for declare procs (+StringId)
&SbiDisas::ImmOp, // TOS is incremented about BASE erhoeht, BASE pushed before
&SbiDisas::TypeOp, // Convert last parameter to/in(?) argv (+Typ)
&SbiDisas::StrOp, // VBASETCLASS (+StringId)
};
static const Func pOperand3[] = {
// All opcodes with two operands
&SbiDisas::VarOp, // Load from RTL (+StringID+Typ)
&SbiDisas::VarOp, // Load (+StringID+Typ)
&SbiDisas::VarOp, // Load Element (+StringID+Typ)
&SbiDisas::OffOp, // Parameter (+Offset+Typ)
// Branch
&SbiDisas::VarOp, // Call DECLARE-Method (+StringID+Typ)
&SbiDisas::VarOp, // Call CDecl-DECLARE-Methode (+StringID+Typ)
&SbiDisas::CaseOp, // Case-Test (+Test-Opcode+False-Target)
&SbiDisas::StmntOp, // Statement (+Row+Column)
// I/O
&SbiDisas::StrmOp, // (+SvStreamFlags+Flags)
// Objects
&SbiDisas::VarDefOp, // Define local var (+StringID+Typ)
&SbiDisas::VarDefOp, // Define Module global var (+StringID+Typ)
&SbiDisas::VarDefOp, // Define global var (+StringID+Typ)
&SbiDisas::Str2Op, // Create object (+StringId+StringId)
&SbiDisas::VarDefOp, // Define static object (+StringID+Typ)
&SbiDisas::Str2Op, // Create User defined Object (+StringId+StringId)
&SbiDisas::Str2Op, // Create User defined Object-Array (+StringId+StringId)
&SbiDisas::VarDefOp, // Define persistent global var P=PERSIST (+StringID+Typ)
&SbiDisas::VarOp, // Searches for global var with special handling due to _GLOBAL_P
&SbiDisas::Str2Op, // Redimensionate User defined Object-Array (+StringId+StringId)
&SbiDisas::VarOp, // FIND_CM
&SbiDisas::VarDefOp, // PUBLIC_P
&SbiDisas::VarOp, // FIND_STATIC
};
// TODO: Why as method? Isn't a simple define sufficient?
static const char* _crlf()
{
#if defined (UNX) || defined( PM2 )
return "\n";
#else
return "\r\n";
#endif
}
// This method exists because we want to load the file as own segment
sal_Bool SbModule::Disassemble( String& rText )
{
rText.Erase();
if( pImage )
{
SbiDisas aDisas( this, pImage );
aDisas.Disas( rText );
}
return sal_Bool( rText.Len() != 0 );
}
SbiDisas::SbiDisas( SbModule* p, const SbiImage* q ) : rImg( *q ), pMod( p )
{
memset( cLabels, 0, 8192 );
nLine = 0;
nOff = 0;
nPC = 0;
nOp1 = nOp2 = nParts = 0;
eOp = _NOP;
// Set Label-Bits
nOff = 0;
while( Fetch() )
{
switch( eOp )
{
case _RESUME: if( nOp1 <= 1 ) break;
case _RETURN: if( !nOp1 ) break;
case _JUMP:
case _JUMPT:
case _JUMPF:
case _GOSUB:
case _TESTFOR:
case _CASEIS:
case _CASETO:
case _ERRHDL:
cLabels[ (nOp1 & 0xffff) >> 3 ] |= ( 1 << ( nOp1 & 7 ) );
break;
default: break;
}
}
nOff = 0;
// Add the publics
for( sal_uInt16 i = 0; i < pMod->GetMethods()->Count(); i++ )
{
SbMethod* pMeth = PTR_CAST(SbMethod,pMod->GetMethods()->Get( i ));
if( pMeth )
{
sal_uInt16 nPos = (sal_uInt16) (pMeth->GetId());
cLabels[ nPos >> 3 ] |= ( 1 << ( nPos & 7 ) );
}
}
}
// Read current opcode
sal_Bool SbiDisas::Fetch()
{
nPC = nOff;
if( nOff >= rImg.GetCodeSize() )
return sal_False;
const unsigned char* p = (const unsigned char*)( rImg.GetCode() + nOff );
eOp = (SbiOpcode) ( *p++ & 0xFF );
if( eOp <= SbOP0_END )
{
nOp1 = nOp2 = 0;
nParts = 1;
nOff++;
return sal_True;
}
else if( eOp <= SbOP1_END )
{
nOff += 5;
if( nOff > rImg.GetCodeSize() )
return sal_False;
nOp1 = *p++; nOp1 |= *p++ << 8; nOp1 |= *p++ << 16; nOp1 |= *p++ << 24;
nParts = 2;
return sal_True;
}
else if( eOp <= SbOP2_END )
{
nOff += 9;
if( nOff > rImg.GetCodeSize() )
return sal_False;
nOp1 = *p++; nOp1 |= *p++ << 8; nOp1 |= *p++ << 16; nOp1 |= *p++ << 24;
nOp2 = *p++; nOp2 |= *p++ << 8; nOp2 |= *p++ << 16; nOp2 |= *p++ << 24;
nParts = 3;
return sal_True;
}
else
return sal_False;
}
void SbiDisas::Disas( SvStream& r )
{
String aText;
nOff = 0;
while( DisasLine( aText ) )
{
ByteString aByteText( aText, gsl_getSystemTextEncoding() );
r.WriteLine( aByteText );
}
}
void SbiDisas::Disas( String& r )
{
r.Erase();
String aText;
nOff = 0;
while( DisasLine( aText ) )
{
r += aText;
r.AppendAscii( _crlf() );
}
aText.ConvertLineEnd();
}
sal_Bool SbiDisas::DisasLine( String& rText )
{
char cBuf[ 100 ];
const char* pMask[] = {
"%08" SAL_PRIXUINT32 " ",
"%08" SAL_PRIXUINT32 " %02X ",
"%08" SAL_PRIXUINT32 " %02X %08X ",
"%08" SAL_PRIXUINT32 " %02X %08X %08X " };
rText.Erase();
if( !Fetch() )
return sal_False;
#ifdef DBG_TRACE_BASIC
String aTraceStr_STMNT;
#endif
// New line?
if( eOp == _STMNT && nOp1 != nLine )
{
// Find line
String aSource = rImg.aOUSource;
nLine = nOp1;
sal_uInt16 n = 0;
sal_uInt16 l = (sal_uInt16)nLine;
while( --l ) {
n = aSource.SearchAscii( "\n", n );
if( n == STRING_NOTFOUND ) break;
else n++;
}
// Show position
if( n != STRING_NOTFOUND )
{
sal_uInt16 n2 = aSource.SearchAscii( "\n", n );
if( n2 == STRING_NOTFOUND ) n2 = aSource.Len() - n;
String s( aSource.Copy( n, n2 - n + 1 ) );
sal_Bool bDone;
do {
bDone = sal_True;
n = s.Search( '\r' );
if( n != STRING_NOTFOUND ) bDone = sal_False, s.Erase( n, 1 );
n = s.Search( '\n' );
if( n != STRING_NOTFOUND ) bDone = sal_False, s.Erase( n, 1 );
} while( !bDone );
// snprintf( cBuf, sizeof(cBuf), pMask[ 0 ], nPC );
// rText += cBuf;
rText.AppendAscii( "; " );
rText += s;
rText.AppendAscii( _crlf() );
#ifdef DBG_TRACE_BASIC
aTraceStr_STMNT = s;
#endif
}
}
// Label?
const char* p = "";
if( cLabels[ nPC >> 3 ] & ( 1 << ( nPC & 7 ) ) )
{
// Public?
ByteString aByteMethName;
for( sal_uInt16 i = 0; i < pMod->GetMethods()->Count(); i++ )
{
SbMethod* pMeth = PTR_CAST(SbMethod,pMod->GetMethods()->Get( i ));
if( pMeth )
{
aByteMethName = ByteString( pMeth->GetName(), gsl_getSystemTextEncoding() );
if( pMeth->GetId() == nPC )
{
p = aByteMethName.GetBuffer();
break;
}
if( pMeth->GetId() >= nPC )
break;
}
}
snprintf( cBuf, sizeof(cBuf), pMask[ 0 ], nPC );
rText.AppendAscii( cBuf );
if( p && *p )
{
rText.AppendAscii( p );
}
else
{
// fix warning (now error) for "Lbl%04lX" format
snprintf( cBuf, sizeof(cBuf), "Lbl%08" SAL_PRIXUINT32, nPC );
rText.AppendAscii( cBuf );
}
rText += ':';
rText.AppendAscii( _crlf() );
}
snprintf( cBuf, sizeof(cBuf), pMask[ nParts ], nPC, (sal_uInt16) eOp, nOp1, nOp2 );
String aPCodeStr;
aPCodeStr.AppendAscii( cBuf );
int n = eOp;
if( eOp >= SbOP2_START )
n -= SbOP2_START;
else if( eOp >= SbOP1_START )
n -= SbOP1_START;
aPCodeStr += '\t';
aPCodeStr.AppendAscii( pOps[ nParts-1 ][ n ] );
aPCodeStr += '\t';
switch( nParts )
{
case 2: (this->*( pOperand2[ n ] ) )( aPCodeStr ); break;
case 3: (this->*( pOperand3[ n ] ) )( aPCodeStr ); break;
}
rText += aPCodeStr;
#ifdef DBG_TRACE_BASIC
dbg_RegisterTraceTextForPC( pMod, nPC, aTraceStr_STMNT, aPCodeStr );
#endif
return sal_True;
}
// Read from StringPool
void SbiDisas::StrOp( String& rText )
{
String aStr = rImg.GetString( (sal_uInt16)nOp1 );
ByteString aByteString( aStr, RTL_TEXTENCODING_ASCII_US );
const char* p = aByteString.GetBuffer();
if( p )
{
rText += '"';
rText.AppendAscii( p );
rText += '"';
}
else
{
rText.AppendAscii( "?String? " );
rText += (sal_uInt16)nOp1;
}
}
void SbiDisas::Str2Op( String& rText )
{
StrOp( rText );
rText += ',';
String s;
nOp1 = nOp2;
StrOp( s );
rText += s;
}
// Immediate Operand
void SbiDisas::ImmOp( String& rText )
{
rText += String::CreateFromInt32(nOp1);
}
// OnGoto Operand
void SbiDisas::OnOp( String& rText )
{
rText += String::CreateFromInt32(nOp1 & 0x7FFF);
if( nOp1 & 0x800 )
rText.AppendAscii( "\t; Gosub" );
}
// Label
void SbiDisas::LblOp( String& rText )
{
char cBuf[ 10 ];
snprintf( cBuf, sizeof(cBuf), "Lbl%04" SAL_PRIXUINT32, nOp1 );
rText.AppendAscii( cBuf );
}
// 0 or Label
void SbiDisas::ReturnOp( String& rText )
{
if( nOp1 )
LblOp( rText );
}
// 0, 1 or Label
void SbiDisas::ResumeOp( String& rText )
{
switch( nOp1 )
{
case 1: rText.AppendAscii( "NEXT" ); break;
case 2: LblOp( rText );
}
}
// print Prompt
// sal_False/TRUE
void SbiDisas::PromptOp( String& rText )
{
if( nOp1 )
rText.AppendAscii( "\"? \"" );
}
// 0 or 1
void SbiDisas::CloseOp( String& rText )
{
rText.AppendAscii( nOp1 ? "Channel" : "All" );
}
// Print character
void SbiDisas::CharOp( String& rText )
{
const char* p = NULL;
switch( nOp1 )
{
case 7: p = "'\\a'"; break;
case 9: p = "'\\t'"; break;
case 10: p = "'\\n'"; break;
case 12: p = "'\\f'"; break;
case 13: p = "'\\r'"; break;
}
if( p ) rText.AppendAscii( p );
else if( nOp1 >= ' ' )
rText += '\'',
rText += (char) nOp1,
rText += '\'';
else
rText.AppendAscii( "char " ),
rText += (sal_uInt16)nOp1;
}
// Print var: String-ID and type
void SbiDisas::VarOp( String& rText )
{
rText += rImg.GetString( (sal_uInt16)(nOp1 & 0x7FFF) );
rText.AppendAscii( "\t; " );
// The type
sal_uInt32 n = nOp1;
nOp1 = nOp2;
TypeOp( rText );
if( n & 0x8000 )
rText.AppendAscii( ", Args" );
}
// Define variable: String-ID and type
void SbiDisas::VarDefOp( String& rText )
{
rText += rImg.GetString( (sal_uInt16)(nOp1 & 0x7FFF) );
rText.AppendAscii( "\t; " );
// The Typ
nOp1 = nOp2;
TypeOp( rText );
}
// Print variable: Offset and Typ
void SbiDisas::OffOp( String& rText )
{
rText += String::CreateFromInt32( nOp1 & 0x7FFF );
rText.AppendAscii( "\t; " );
// The type
sal_uInt32 n = nOp1;
nOp1 = nOp2;
TypeOp( rText );
if( n & 0x8000 )
rText.AppendAscii( ", Args" );
}
// Data type
#ifdef HP9000 // TODO: remove this!
static char* SbiDisas_TypeOp_pTypes[13] = {
"Empty","Null","Integer","Long","Single","Double",
"Currency","Date","String","Object","Error","Boolean",
"Variant" };
#define pTypes SbiDisas_TypeOp_pTypes
#endif
void SbiDisas::TypeOp( String& rText )
{
// AB 19.1.96: Typ kann Flag für BYVAL enthalten (StepARGTYP)
if( nOp1 & 0x8000 )
{
nOp1 &= 0x7FFF; // Flag wegfiltern
rText.AppendAscii( "BYVAL " );
}
if( nOp1 < 13 )
{
#ifndef HP9000
static char pTypes[][13] = {
"Empty","Null","Integer","Long","Single","Double",
"Currency","Date","String","Object","Error","Boolean",
"Variant" };
#endif
rText.AppendAscii( pTypes[ nOp1 ] );
}
else
{
rText.AppendAscii( "type " );
rText += (sal_uInt16)nOp1;
}
}
#ifdef HP9000
#undef pTypes
#endif
// sal_True-Label, condition Opcode
void SbiDisas::CaseOp( String& rText )
{
LblOp( rText );
rText += ',';
rText.AppendAscii( pOp1[ nOp2 - SbxEQ + _EQ ] );
}
// Row, column
void SbiDisas::StmntOp( String& rText )
{
rText += String::CreateFromInt32( nOp1 );
rText += ',';
sal_uInt32 nCol = nOp2 & 0xFF;
sal_uInt32 nFor = nOp2 / 0x100;
rText += String::CreateFromInt32( nCol );
rText.AppendAscii( " (For-Level: " );
rText += String::CreateFromInt32( nFor );
rText += ')';
}
// open mode, flags
void SbiDisas::StrmOp( String& rText )
{
char cBuf[ 10 ];
snprintf( cBuf, sizeof(cBuf), "%04" SAL_PRIXUINT32, nOp1 );
rText.AppendAscii( cBuf );
if( nOp2 & SBSTRM_INPUT )
rText.AppendAscii( ", Input" );
if( nOp2 & SBSTRM_OUTPUT )
rText.AppendAscii( ", Output" );
if( nOp2 & SBSTRM_APPEND )
rText.AppendAscii( ", Append" );
if( nOp2 & SBSTRM_RANDOM )
rText.AppendAscii( ", Random" );
if( nOp2 & SBSTRM_BINARY )
rText.AppendAscii( ", Binary" );
}