/*
 * The contents of this file are subject to the Netscape Public
 * License Version 1.1 (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.mozilla.org/NPL/

 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.

 * The Original Code is Rhino code, released
 * May 6, 1999.

 * The Initial Developer of the Original Code is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1997-1999 Netscape Communications Corporation. All
 * Rights Reserved.

 * Contributor(s): 

 * Alternatively, the contents of this file may be used under the
 * terms of the GNU Public License (the "GPL"), in which case the
 * provisions of the GPL are applicable instead of those above.
 * If you wish to allow use of your version of this file only
 * under the terms of the GPL and not to allow others to use your
 * version of this file under the NPL, indicate your decision by
 * deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL.  If you do not delete
 * the provisions above, a recipient may use your version of this
 * file under either the NPL or the GPL.
 */
/* Header file */
#include "js-interpretedfunction.h"
#include "js-runtimeexception.h"
#include "js-exceptionhandler.h"
#include "js-interpreterdata.h"
#include "ewb-sys.h"
#include "ewb-sys-if.h"
#ifdef PERSIST
# include "js-regexpproxy.h"
#endif

#ifdef PERSIST
/* Data */
static jsBool_t JSInterpreterData::* bools[] = {
     &JSInterpreterData::itsNeedsActivation,
     &JSInterpreterData::itsFromEvalCode,
     &JSInterpreterData::itsUseDynamicScope,
} ;
static int JSInterpreterData::* ints[] = {
     &JSInterpreterData::itsMaxLocals,
     &JSInterpreterData::itsMaxArgs,
     &JSInterpreterData::itsMaxStack,
     &JSInterpreterData::itsMaxTryDepth,
     &JSInterpreterData::itsMaxEnums,
} ;
#endif

/* Procedures */
void
JSInterpreterData::mark(JSContext *cx)
{
    JSGCHeap *gcHeap = &cx->runtime->gcHeap;

    if (gcHeap->isMarkObj(this)) return;

    gcHeap->markObj(this);
    if (itsVariableTable != NULL) {
	itsVariableTable->mark(cx);
    }
    if (itsName != NULL) {
	gcHeap->markObj(itsName);
    }
    if (itsSource != NULL) {
	gcHeap->markObj(itsSource);
    }
    if (itsSourceFile != NULL) {
	gcHeap->markObj(itsSourceFile);
    }
    if (itsStringTable != NULL) {
/* itsStringTable is allocated from runtime memory to avoid gc fragmentation
        gcHeap->markObj(itsStringTable);
*/
        for (int i = 0; i < itsStringTableIndex; ++i) {
	    if (itsStringTable[i] != NULL) {
	        gcHeap->markObj(itsStringTable[i]);
	    }
        }
    }
/* itsNumberTable is allocated from runtime memory to avoid gc fragmentation
    if (itsNumberTable != NULL) {
	gcHeap->markObj(itsNumberTable);
    }
*/
    if (itsNestedFunctions != NULL) {
	gcHeap->markObj(itsNestedFunctions);
	for (int i = 0; i < itsNestedFunctionsLength; ++i) {
	    itsNestedFunctions[i]->mark(cx);
	}
    }
    if (itsRegExpLiterals != NULL) {
	gcHeap->markObj(itsRegExpLiterals);
	for (int i = 0; i < itsRegExpLiteralsLength; ++i) {
	    JS_MarkVal(cx, itsRegExpLiterals[i]);
	}
    }
/* itsICode is allocated from runtime memory to avoid gc fragmentation
    if (itsICode != NULL) {
	gcHeap->markObj(itsICode);
    }
*/
}
/* itsICode is allocated from runtime memory to avoid gc fragmentation */
void
JSInterpreterData::finalize(JSContext *cx)
{
    if (itsICode != NULL) {
	JS_DeleteRuntimeObj(cx->runtime, itsICode);
    }
    if (itsStringTable != NULL) {
	JS_DeleteRuntimeObj(cx->runtime, itsStringTable);
    }
    if (itsNumberTable != NULL) {
	JS_DeleteRuntimeObj(cx->runtime, itsNumberTable);
    }
}
#ifdef PERSIST
void
JSInterpreterData::presave( JSContext *cx, JSScriptable *scope,
		JSPersist::StringConstants& stringConstants,
		JSPersist::NumberConstants& numberConstants )
{
    itsVariableTable->presave( cx, scope, stringConstants, numberConstants );
    if ( itsName != NULL ) {
	( void ) stringConstants.addString( itsName );
    }
    if ( itsSourceFile != NULL ) {
	( void ) stringConstants.addString( itsSourceFile );
    }
    for ( int i = 0; i < itsStringTableIndex; ++i ) {
	( void ) stringConstants.addString( itsStringTable[ i ] );
    }
    for ( int i = 0; i < itsNumberTableIndex; ++i ) {
	( void ) numberConstants.addNumber( itsNumberTable[ i ] );
    }
    for ( int i = 0; i < itsNestedFunctionsLength; ++i ) {
	itsNestedFunctions[ i ]->presave( cx, scope, stringConstants,
			numberConstants );
    }
    JSRegExpProxy *rep = cx->getRegExpProxy();

    for ( int i = 0; i < itsRegExpLiteralsLength; ++i ) {
	rep->presave( cx, scope, itsRegExpLiterals[ i ], stringConstants,
			numberConstants );
    }
}
void
JSInterpreterData::save( JSContext *cx, JSScriptable *scope,
		JSRandomWriter* out,
		JSPersist::StringConstants& stringConstants,
		JSPersist::NumberConstants& numberConstants )
{
    itsVariableTable->save( cx, scope, out, stringConstants,
			numberConstants );

    uint16_t index16 = 0;

    if ( itsName != NULL ) {
	index16 = stringConstants.getString( itsName );
    }
    index16 = JSPersist::normalize( index16 );

    int n = out->write( &index16, 0, sizeof ( index16 ) );

    if ( n < ( int ) sizeof ( index16 ) ) {
	printf( "can't write itsName index\n" );
	return;
    }
    uint16_t count16 = itsSourceLength;

    count16 = JSPersist::normalize( count16 );
    n = out->write( &count16, 0, sizeof ( count16 ) );
    if ( n < ( int ) sizeof ( count16 ) ) {
	printf( "can't write itsSourceLength\n" );
	return;
    }
    if ( itsSourceLength != 0 ) {
	n = out->write( itsSource, 0, sizeof ( *itsSource ) * itsSourceLength );
	if ( n < ( int ) ( sizeof ( *itsSource ) * itsSourceLength ) ) {
	    printf( "can't write source\n" );
	    return;
	}
    }
    index16 = 0;
    if ( itsSourceFile != NULL ) {
	index16 = stringConstants.getString( itsSourceFile );
    }
    index16 = JSPersist::normalize( index16 );
    n = out->write( &index16, 0, sizeof ( index16 ) );
    if ( n < ( int ) sizeof ( index16 ) ) {
	printf( "can't write itsSourceFile index\n" );
	return;
    }
    uint8_t val8;
    
    for ( int i = 0; i < ( int ) TableSize( bools ); ++i ) {
    	val8 = this->*bools[ i ];
    	n = out->write( &val8, 0, sizeof ( val8 ) );
    	if ( n < ( int ) sizeof ( val8 ) ) {
	    printf( "can't write bools %d\n", i );
	    return;
	}
    }
    val8 = itsFunctionType;
    n = out->write( &val8, 0, sizeof ( val8 ) );
    if ( n < ( int ) sizeof ( val8 ) ) {
	printf( "can't write itsFunctionType index\n" );
	return;
    }
    count16 = JSPersist::normalize( ( uint16_t ) itsStringTableIndex );
    n = out->write( &count16, 0, sizeof ( count16 ) );
    if ( n < ( int ) sizeof ( count16 ) ) {
	printf( "can't write itsStringTableIndex\n" );
	return;
    }
    for ( int i = 0; i < itsStringTableIndex; ++i ) {
	index16 = stringConstants.getString( itsStringTable[ i ] );
	index16 = JSPersist::normalize( index16 );
	n = out->write( &index16, 0, sizeof ( index16 ) );
	if ( n < ( int ) sizeof ( index16 ) ) {
	    printf( "can't write itsStringTable\n" );
	    return;
	}
    }
    count16 = JSPersist::normalize( ( uint16_t ) itsNumberTableIndex );
    n = out->write( &count16, 0, sizeof ( count16 ) );
    if ( n < ( int ) sizeof ( count16 ) ) {
	printf( "can't write itsNumberTableIndex\n" );
	return;
    }
    for ( int i = 0; i < itsNumberTableIndex; ++i ) {
	index16 = numberConstants.getNumber( itsNumberTable[ i ] );
	index16 = JSPersist::normalize( index16 );
	n = out->write( &index16, 0, sizeof ( index16 ) );
	if ( n < ( int ) sizeof ( index16 ) ) {
	    printf( "can't write itsNumberTable\n" );
	    return;
	}
    }
    count16 = JSPersist::normalize( ( uint16_t ) itsNestedFunctionsLength );
    n = out->write( &count16, 0, sizeof ( count16 ) );
    if ( n < ( int ) sizeof ( count16 ) ) {
	printf( "can't write itsNestedFunctionsLength\n" );
	return;
    }
    for ( int i = 0; i < itsNestedFunctionsLength; ++i ) {
	itsNestedFunctions[ i ]->save( cx, scope, out, stringConstants,
			numberConstants );
    }
    count16 = JSPersist::normalize( ( uint16_t ) itsRegExpLiteralsLength );
    n = out->write( &count16, 0, sizeof ( count16 ) );
    if ( n < ( int ) sizeof ( count16 ) ) {
	printf( "can't write itsNestedFunctionsLength\n" );
	return;
    }
    JSRegExpProxy *rep = cx->getRegExpProxy();

    for ( int i = 0; i < itsRegExpLiteralsLength; ++i ) {
	rep->save( cx, scope, itsRegExpLiterals[ i ], out, stringConstants,
			numberConstants );
    }
    uint32_t count32 = JSPersist::normalize( ( uint32_t ) itsICodeTop );

    n = out->write( &count32, 0, sizeof ( count32 ) );
    if ( n < ( int ) sizeof ( count32 ) ) {
	printf( "can't write itsICodeTop\n" );
	return;
    }
    if ( itsICodeTop > 0 ) {
	n = out->write( itsICode, 0, itsICodeTop );
	if ( n < itsICodeTop ) {
	    printf( "cant't write code table\n" );
	    return;
	}
    }
    for ( int i = 0; i < ( int ) TableSize( ints ); ++i ) {
    	uint16_t val16
                = JSPersist::normalize( ( uint16_t ) ( this->*ints[ i ] ) );

    	n = out->write( &val16, 0, sizeof ( val16 ) );
    	if ( n < ( int ) sizeof ( val16 ) ) {
	    printf( "can't write ints %d\n", i );
	    return;
	}
    }
}
void
JSInterpreterData::load( JSContext *cx, JSScriptable *scope,
		JSRandomReader* in, jsString_t* strings, double* numbers )
{
    itsVariableTable->load( cx, scope, in, strings, numbers );

    uint16_t index16;
    int n = in->read( &index16, 0, sizeof ( index16 ) );

    if ( n < ( int ) sizeof ( index16 ) ) {
	printf( "can't read itsName\n" );
	return;
    }
    index16 = JSPersist::localize( index16 );
    if ( index16 != 0 ) {
    	itsName = strings[ index16 - 1 ];
    }
    uint16_t count16;

    n = in->read( &count16, 0, sizeof ( count16 ) );
    if ( n < ( int ) sizeof ( count16 ) ) {
	printf( "can't read itsSourceLength\n" );
	return;
    }
    itsSourceLength = JSPersist::localize( count16 );
    if ( itsSourceLength != 0 ) {
	itsSource = ( jsString_t ) JS_NewGCObj( cx,
			sizeof ( *itsSource ) * itsSourceLength );
	n = in->read( itsSource, 0, sizeof ( *itsSource ) * itsSourceLength );
	if ( n < ( int ) sizeof ( *itsSource ) * itsSourceLength ) {
	    printf( "cant't read itsSource\n" );
	    return;
	}
    }
    n = in->read( &index16, 0, sizeof ( index16 ) );
    if ( n < ( int ) sizeof ( index16 ) ) {
	printf( "can't read itsSourceFile\n" );
	return;
    }
    index16 = JSPersist::localize( index16 );
    if ( index16 != 0 ) {
    	itsSourceFile = strings[ index16 - 1 ];
    }
    uint8_t val8;

    for ( int i = 0; i < ( int ) TableSize( bools ); ++i ) {
	n = in->read( &val8, 0, sizeof ( val8 ) );
	if ( n < ( int ) sizeof ( val8 ) ) {
	    printf( "can't read bools %d\n", i );
	    return;
	}
	this->*bools[ i ] = val8;
    }
    n = in->read( &val8, 0, sizeof ( val8 ) );
    if ( n < ( int ) sizeof ( val8 ) ) {
	printf( "can't read itsFunctionType\n" );
	return;
    }
    itsFunctionType = val8;
    n = in->read( &count16, 0, sizeof ( count16 ) );
    if ( n < ( int ) sizeof ( count16 ) ) {
	printf( "can't read itsStringTableLength\n" );
	return;
    }
    if ( itsStringTable != NULL ) {
	JS_DeleteRuntimeObj( cx->runtime, itsStringTable );
	itsStringTable = NULL;
    }
    itsStringTableLength = itsStringTableIndex
	= JSPersist::localize( count16 );
    if ( itsStringTableLength != 0 ) {
	itsStringTable
		= ( jsString_t* ) JS_NewRuntimeObj( cx->runtime,
				sizeof ( *itsStringTable )
				* itsStringTableLength );
	memset( itsStringTable, 0,
			sizeof ( *itsStringTable ) * itsStringTableLength );
	for ( int i = 0; i < itsStringTableLength; ++i ) {
    	    n = in->read( &index16, 0, sizeof ( index16 ) );
    	    if ( n < ( int ) sizeof ( index16 ) ) {
	        printf( "can't read itsStringTable\n" );
	        return;
	    }
    	    index16 = JSPersist::localize( index16 );
	    if ( index16 != 0 ) {
	    	itsStringTable[ i ] = strings[ index16 - 1 ];
	    }
	}
    }
    n = in->read( &count16, 0, sizeof ( count16 ) );
    if ( n < ( int ) sizeof ( count16 ) ) {
	printf( "can't read itsNumberTableLength\n" );
	return;
    }
    if ( itsNumberTable != NULL ) {
	JS_DeleteRuntimeObj( cx->runtime, itsNumberTable );
	itsNumberTable = NULL;
    }
    itsNumberTableLength = itsNumberTableIndex = JSPersist::localize( count16 );
    if ( itsNumberTableLength != 0 ) {
/* itsNumberTable is allocated from runtime memory to avoid gc fragmentation
	itsNumberTable
		= ( double* ) JS_NewGCObj( cx,
				sizeof ( *itsNumberTable )
				* itsNumberTableLength );
*/
	itsNumberTable
		= ( double* ) JS_NewRuntimeObj( cx->runtime,
				sizeof ( *itsNumberTable )
				* itsNumberTableLength );
	for ( int i = 0; i < itsNumberTableLength; ++i ) {
    	    n = in->read( &index16, 0, sizeof ( index16 ) );
    	    if ( n < ( int ) sizeof ( index16 ) ) {
	        printf( "can't read itsNumberTable\n" );
	        return;
	    }
    	    index16 = JSPersist::localize( index16 );
	    if ( index16 != 0 ) {
	    	itsNumberTable[ i ] = numbers[ index16 - 1 ];
	    }
	}
    }
    n = in->read( &count16, 0, sizeof ( count16 ) );
    if ( n < ( int ) sizeof ( count16 ) ) {
	printf( "can't read itsNestedFunctionsLength\n" );
	return;
    }
    count16 = JSPersist::localize( count16 );
    if ( count16 != 0 ) {
	itsNestedFunctions
		= ( JSInterpretedFunction** ) JS_NewGCObj( cx,
				sizeof ( *itsNestedFunctions )
				* count16 );

	for ( int i = 0; i < count16; ++i ) {
	    JSInterpreterData *theData
		    = new (cx) JSInterpreterData( cx, 0, 0, 0,
				    cx->hasCompileFunctionsWithDynamicScope() );
	    JSVariableTable* varTable = new (cx) JSVariableTable(cx);

	    theData->itsVariableTable = varTable;
	    itsNestedFunctions[ i ]
		    = new (cx) JSInterpretedFunction( theData, cx );
	    itsNestedFunctionsLength = i + 1; // gc safe
	    itsNestedFunctions[ i ]->load( cx, scope, in, strings, numbers );
	}
    }
    n = in->read( &count16, 0, sizeof ( count16 ) );
    if ( n < ( int ) sizeof ( count16 ) ) {
	printf( "can't read itsRegExpLiteralsLength\n" );
	return;
    }
    count16 = JSPersist::localize( count16 );
    if ( count16 != 0 ) {
	itsRegExpLiterals
		= ( jsVal_t* ) JS_NewGCObj( cx,
				sizeof ( *itsRegExpLiterals )
				* count16 );
    	JSRegExpProxy *rep = cx->getRegExpProxy();

	for ( int i = 0; i < count16; ++i ) {
	    itsRegExpLiterals[ i ]
		    	= rep->newRegExp( cx, scope, "", "", FALSE );

	    itsRegExpLiteralsLength = i + 1;	// gc safe
	    rep->load(cx, scope, itsRegExpLiterals[ i ], in, strings, numbers );
	}
    }
    uint32_t count32;

    n = in->read( &count32, 0, sizeof ( count32 ) );
    if ( n < ( int ) sizeof ( count32 ) ) {
	printf( "can't read itsICodeLength\n" );
	return;
    }
    if ( itsICode != NULL ) {
	JS_DeleteRuntimeObj( cx->runtime, itsICode );
	itsICode = NULL;
    }
    itsICodeLength = itsICodeTop = JSPersist::localize( count32 );
    if ( itsICodeLength != 0 ) {
	itsICode = ( int8_t* )
		JS_NewRuntimeObj( cx->runtime, itsICodeLength );

	n = in->read( itsICode, 0, itsICodeLength );
	if ( n < itsICodeLength ) {
	    printf( "can't read code table\n" );
	    return;
	}
    }
    for ( int i = 0; i < ( int ) TableSize( ints ); ++i ) {
	uint16_t val16;

	n = in->read( &val16, 0, sizeof ( val16 ) );
	if ( n < ( int ) sizeof ( val16 ) ) {
	    printf( "can't read ints %d\n", i );
	    return;
	}
	this->*ints[ i ] = JSPersist::localize( val16 );
    }
}
#endif /* PERSIST */

JSInterpreterData::JSInterpreterData(JSContext *cx, int lastICodeTop,
				int lastStringTableIndex,
				int lastNumberTableIndex, 
				jsBool_t useDynamicScope)
{
    itsVariableTable = NULL;
    itsName = NULL;
    itsSource = NULL;
    itsSourceLength = 0;
    itsSourceFile = NULL;
    itsNeedsActivation = FALSE;
    itsFromEvalCode = FALSE;
    itsFunctionType = 0;

    itsStringTable = NULL;
    itsStringTableIndex = 0;
    itsNumberTable = NULL;
    itsNumberTableIndex = 0;
    itsNestedFunctionsLength = 0;
    itsNestedFunctions = NULL;
    itsRegExpLiterals = NULL;
    itsRegExpLiteralsLength = 0;
 
    itsICode = NULL;
    itsICodeTop = 0;
#ifdef REUSE_LOCAL
    itsLocalTop = 0;
#endif
    itsMaxLocals = 0;
    itsMaxArgs = 0;
    itsMaxStack = 0;
    itsMaxTryDepth = 0;
    itsMaxEnums = 0;

    itsICodeTop = lastICodeTop == 0
			? INITIAL_MAX_ICODE_LENGTH
			: lastICodeTop * 2;
    itsICodeLength = itsICodeTop;
/* itsICode is allocated from runtime memory to avoid gc fragmentation
    itsICode = (int8_t *) JS_NewGCObj(cx, sizeof (*itsICode) * itsICodeTop);
*/
    itsICode = (int8_t *) JS_NewRuntimeObj(cx->runtime,
					sizeof (*itsICode) * itsICodeTop);
 
    itsStringTableLength = lastStringTableIndex == 0
				? INITIAL_STRINGTABLE_SIZE
				: lastStringTableIndex * 2;
/* itsStringTable is allocated from runtime memory to avoid gc fragmentation
    itsStringTable = (jsString_t *) JS_NewGCObj(cx, sizeof (*itsStringTable)
							* itsStringTableLength);
*/
    itsStringTable = (jsString_t *) JS_NewRuntimeObj(cx->runtime,
						sizeof (*itsStringTable)
							* itsStringTableLength);

    memset(itsStringTable, 0, sizeof (*itsStringTable) * itsStringTableLength);
 
    itsNumberTableLength = lastNumberTableIndex == 0
				? INITIAL_NUMBERTABLE_SIZE
				: lastNumberTableIndex * 2;
/* itsNumberTable is allocated from runtime memory to avoid gc fragmentation
    itsNumberTable = (double *) JS_NewGCObj(cx, sizeof (*itsNumberTable)
						* itsNumberTableLength);
*/
    itsNumberTable = (double *) JS_NewRuntimeObj( cx->runtime, sizeof (*itsNumberTable)
						* itsNumberTableLength);

    itsUseDynamicScope = useDynamicScope;
}

