/****************************************************************************
/                                                                           
/                     Copyright 1997 Macromedia, Inc.                       
/                                                                          
/      This material is the confidential trade secret and proprietary       
/      information of Macromedia, Inc.  It may not be reproduced, used,     
/      sold or transferred to any third party without the prior written     
/      consent of Macromedia, Inc.  All rights reserved.                    
/                                                                          
/ Filename
/   BehaviorEventPane.cpp
/
/ Purpose
/   This file contains all calls from the NscpJavaScript directory to other
/   directories in the tree.  Currently, that list is limited to the memory
/   allocation functions, which use the MemPool class.
/
/   The prtypes.h file does a #define so that all calls to malloc are
/   made to call glue_malloc instead.  Same for free and realloc.  That's
/   less error-prone than changing each call to malloc by hand.  The
/   only danger is that someone might call malloc before including prtypes.h.
/
****************************************************************************/


#define incDebugUtils

#ifdef _MAC
#pragma cplusplus on
//#define __AFXWIN_H__
//#include "mfc2x.h"
#include "mfc2x_pch.h"
#else
#include <afxwin.h>
#endif

// jschang 4/20/04 - as part of moving to shared_code, I'm going to remove 
// the dependency on MemPool until we figure out where MemPool is going... 
// TODO - run some tests to see if this adversely 
// affects the performance of the javascript engine
//#ifndef _UTILITY_MEMPOOL_H
//#include "mempool.h"
//#endif // _UTILITY_MEMPOOL_H

#ifdef USE_CRASH_LOGGER
#include "WindowsCrashLogger.h"
#endif

#include <stdio.h>
#include <string.h>
#include <math.h>

extern "C"
{
#include "jstypes.h"
#include "jsapi.h"
#include "jsarray.h"
#include "jsatom.h"
#include "jsbool.h"
#include "jscntxt.h"
#include "jsconfig.h"
#include "jsdbgapi.h"
#include "jsfun.h"
#include "jsgc.h"
#include "jsinterp.h"
#include "jslock.h"
#include "jsnum.h"
#include "jsobj.h"
#include "jsopcode.h"
#include "jsscope.h"
#include "jsscript.h"
#include "jsstr.h"
}

extern "C" JSBool JSCallNativeSafe (JSNative native, JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
		      jsval *rval);


// Defined in JSInterp.cpp.
extern void AssertHaveJSErrorString();

#include <typeinfo>

// Returns whether the native call should be wrapped in a try/catch block.
static BOOL UseTryCatch()
{
	BOOL useTryCatch = TRUE;

#if defined(_DEBUG) && defined(_WINDOWS) && defined(USE_CRASH_LOGGER)
	// [mmorearty 2/27/03] If we're running under the debugger, then don't
	// catch the exception -- that way the debugger can catch it and drop into
	// the source code at the actual location of the exception.
	//
	// Note, this means that we have slightly different behavior when running
	// under the debugger than when running without the debugger.  That can be
	// potentially confusing, but still it is much better than not being able
	// to debug exceptions.
	if (IsDebuggerPresent() || WindowsCrashLogger::IsJitDebuggerInstalled())
		useTryCatch = FALSE;
#endif

	return useTryCatch;
}

#ifdef _WINDOWS

// If an exception happens when we call native code, then this exception
// handler will be called; we will write a crashlog to the user's disk.
LONG WINAPI JSNativeCallExceptionHandler(EXCEPTION_POINTERS* pExceptionPointers)
{
#ifdef USE_CRASH_LOGGER
	// If the native call was wrapped in a try-catch block, then we should write
	// out a dump file, since the debugger is not going to be given the chance
	// to deal with it.
	if (UseTryCatch())
		WindowsCrashLogger::SaveCrashToDumpFile(pExceptionPointers, FALSE);
#endif

	// Whether or not we saved the crashlog, we do NOT want to enter the __except block of the caller
	// -- we want the operating system to continue looking up the exception chain for an
	// exception hander that will catch this (which will turn out to be JSCallNativeSafe)
	return EXCEPTION_CONTINUE_SEARCH;
}

#endif // _WINDOWS


static JSBool JSCallNative(JSNative native, JSContext *cx, JSObject *obj,
						   uintN argc, jsval *argv, jsval *rval)
{
	JSBool ok = JS_FALSE;

#ifdef _WINDOWS
	__try
	{
#endif

		ok = native (cx, obj, argc, argv, rval);

#ifdef _WINDOWS
	}
	// If any exception gets thrown, we call JSNativeCallExceptionHandler,
	// which will write a crashlog to disk but will *NOT* actually 'catch'
	// the exception -- it returns EXCEPTION_CONTINUE_SEARCH, which means
	// "I don't want to catch this one; keep looking up the chain of
	// exception handlers to find someone who does want to."  Since we
	// were called by JSCallNativeSafe, that function will (in most cases)
	// catch this exception and display a message to the user.
	__except( JSNativeCallExceptionHandler(GetExceptionInformation()) )
	{
		// Even if an exception is thrown, we shouldn't end up
		// here, because JSNativeCallExceptionHandler is
		// supposed to always return EXCEPTION_CONTINUE_SEARCH
		ASSERT(FALSE);
	}
#endif

	// If this assertion fires, it means a native function returned
	// false (error) without recording an error message.  This is
	// bad behavior, because it makes the bug hard to track down.
	// 
	// To get an idea of what native function we just called:
	// - Visual Studio .NET: hover the mouse over the word "native"
	//   in the function call above.  The tooltip will say the name
	//   of the native function.
	// - CodeWarrior: Right-click on "native" in the function call
	//   above, and pick "View Memory".  Change the "Display:" to
	//   "*native" (with an asterisk) and hit Enter.  Change
	//   "View:" to "Source".  You should now see the source code
	//   of the function that was called.
	// - Other: Look up the stack one level to js_Invoke.  js_Invoke
	//   has a local variable named "fun".  The expression
	//   *(void**)fun->atom->entry.key gives the address of the
	//   function name (in Unicode format).
	// You should then find the C++ implementation of that function,
	// and update it to include a call to JS_ReportError when it
	// returns false.  Search for JS_ReportError in Source/Titan/JavaScript
	// for usage examples.
	// 
	// snewman 5/23/01
	if (!ok)
		AssertHaveJSErrorString();

	return ok;
}

extern "C" JSBool JSCallNativeSafe (JSNative native, JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
								    jsval *rval)
{
	JSBool	ok = JS_FALSE;

	if (UseTryCatch())
	{
		try
		{
			ok = JSCallNative(native, cx, obj, argc, argv, rval);
		}
#ifdef _DEBUG
		catch (CException *mfcEx) {
			char buf[1024];
			const char *name = typeid(*mfcEx).name();
			strcpy(buf, "exception thrown in native function. \"");
			strncat(buf, name, sizeof(buf));
			strncat(buf, "\": ", sizeof(buf));
			size_t len = strlen(buf);
			#ifndef _UNICODE
				mfcEx->GetErrorMessage(buf + len, sizeof(buf) - len);
			#endif
			buf[sizeof(buf) - 1] = '\0';
			JS_ReportError(cx, buf);
			mfcEx->Delete();
		}
		catch (const std::exception &stdEx) {
			char buf[1024];
			const char *what = stdEx.what();
			const char *name = typeid(stdEx).name();

			strcpy(buf, "exception thrown in native function. \"");
			strncat(buf, name, sizeof(buf));
			strncat(buf, "\": ", sizeof(buf));
			strncat(buf, what, sizeof(buf));
			buf[sizeof(buf) - 1] = '\0';
			JS_ReportError(cx, buf);
		}
		catch (char *strEx) {
			char buf[1024];

			strcpy(buf, "exception thrown in native function. (char *): ");
			strncat(buf, strEx, sizeof(buf));
			buf[sizeof(buf) - 1] = '\0';
			JS_ReportError(cx, buf);
		}
#endif
		catch (...)
		{
			JS_ReportError(cx, "Exception thrown in native function.");
		}
	}
	else // else don't use try-catch
	{
		ok = JSCallNative(native, cx, obj, argc, argv, rval);
	}

	return ok;
}


// snewman 3/28/01: prototypes to suppress a Metrowerks compiler warning
extern "C" void* glue_malloc(size_t nbytes);
extern "C" void* glue_realloc(void *p, size_t nbytes);
extern "C" void glue_free(void *p);


#ifdef OVERRIDE_JS_ALLOCATOR

// snewman 3/28/01: turning this off because it clutters up the build messages
// #pragma message( "OVERRIDE_JS_ALLOCATOR defined " __FILE__ ) 

#ifdef USE_MULTIPOOLS
#pragma message( "USE_MULTIPOOLS defined " __FILE__ ) 
#endif

#if defined(NETSCAPEJS_USES_MEMPOOL)
/* Initialize the memory pool to use 64K puddles.  If we ever attempt to 
 * allocate a single block that's larger than 48K, automatically create
 * a new puddle. */
#define	TAGITEM_MEMPOOL_PUDDLE_SIZE		(64 * 1024)
#define	TAGITEM_MEMPOOL_THRESHOLD_SIZE	(48 * 1024)

#if (_DO_STATS)
static ULONG allocSizes[N_ALLOC_TRACKING_SIZES] = STANDARD_ALLOC_SIZES;
#define STATS_SEP ,
#define STATS_PARAMETER(x) x
#else
#define STATS_SEP
#define STATS_PARAMETER(x)
#endif

static MemPool memPool( _T("_NscpJavaScript")
	, (_MEMPOOLF_USE_APPLICATION_HEAP)
	, TAGITEM_MEMPOOL_PUDDLE_SIZE
	, TAGITEM_MEMPOOL_THRESHOLD_SIZE
	,	singlePool
	STATS_SEP	STATS_PARAMETER(allocSizes)
	);


extern "C" void* glue_malloc(size_t nbytes)
{
	return memPool.Malloc(nbytes);
}


extern "C" void* glue_realloc(void *p, size_t nbytes)
{
    return memPool.Realloc(p, nbytes);
}

extern "C" void glue_free(void *p)
{
	memPool.Free(p);
}

#elif defined(NETSCAPEJS_USES_MULTIPOOL)
#pragma message( "NETSCAPEJS_USES_MULTIPOOL defined " __FILE__ ) 
#ifndef _DO_STATS
#define _DO_STATS
#endif


#if (_DO_STATS)
static ULONG allocSizes[N_ALLOC_TRACKING_SIZES] = STANDARD_ALLOC_SIZES;
#define STATS_SEP ,
#define STATS_PARAMETER(x) x
#else
#define STATS_SEP
#define STATS_PARAMETER(x)
#endif

#define	JS_MULTIPOOL_PUDDLE_SIZE		(64 * 1024)
#define	JS_MULTIPOOL_THRESHOLD_SIZE		(56 * 1024)


MULTI_POOL_DATA jsPoolSizes[MAX_JS_POOLS] = { 
	{JS_MULTIPOOL_PUDDLE_SIZE, JS_MULTIPOOL_THRESHOLD_SIZE, STANDARD_SIZE_2,   "js-0020"},
	{JS_MULTIPOOL_PUDDLE_SIZE, JS_MULTIPOOL_THRESHOLD_SIZE, STANDARD_SIZE_3,   "js-0042"},
	{JS_MULTIPOOL_PUDDLE_SIZE, JS_MULTIPOOL_THRESHOLD_SIZE, STANDARD_SIZE_4,   "js-0056"},
	{JS_MULTIPOOL_PUDDLE_SIZE, JS_MULTIPOOL_THRESHOLD_SIZE, STANDARD_SIZE_5,   "js-0128"},
	{JS_MULTIPOOL_PUDDLE_SIZE, JS_MULTIPOOL_THRESHOLD_SIZE, STANDARD_SIZE_7,   "js-0512"},
	{JS_MULTIPOOL_PUDDLE_SIZE, JS_MULTIPOOL_THRESHOLD_SIZE, STANDARD_SIZE_9,   "js-1060"},
	{JS_MULTIPOOL_PUDDLE_SIZE, JS_MULTIPOOL_THRESHOLD_SIZE, STANDARD_SIZE_10,  "js-2048"},
	{JS_MULTIPOOL_PUDDLE_SIZE, JS_SPLITPOOL_THRESHOLD_SIZE, STANDARD_SIZE_12,  "js-4096"}
	
};



static MultiPool memPool( (_MEMPOOLF_USE_APPLICATION_HEAP)
	, jsPoolSizes
	STATS_SEP STATS_PARAMETER(allocSizes)
	);

//	Uncomment this out if you want to turn on the malloc callback routines
//#define VERIFY_ALLOCATOR_OVERRIDES


#ifdef VERIFY_ALLOCATOR_OVERRIDES
typedef void (*JSMallocCB)(char *);
static JSMallocCB jsMallocCallBack	= NULL;


void
some_ErrorReporter( const char *message)
{

    fputs("js: ", stderr);
    
	fprintf(stderr, "%s\n", message);
	return;
    
}


extern "C" void * 
nsjs_GetPoolPointer()
{
	return &memPool;
}

extern "C" void  
nsjs_SetMallocCallBack(JSMallocCB cb)
{
	jsMallocCallBack = cb;
}
#endif // VERIFY_ALLOCATOR_OVERRIDES

extern "C" void *
glue_malloc(size_t nbytes)
{
#ifdef VERIFY_ALLOCATOR_OVERRIDES
	#pragma message("Verifying allocator overrides in " __FILE__ )
	some_ErrorReporter(  " fake malloc called" );
	if ( jsMallocCallBack != NULL )
		jsMallocCallBack( "glue_malloc" );
	/*if ( !registered )
	{
		ASSERT(FALSE);
		Lifeguard::RegisterPool( (MemPool*) &memPool );
		registered = TRUE;
	}
	else
	{
		registered = FALSE;
	}*/
#endif	// VERIFY_ALLOCATOR_OVERRIDES
	return memPool.Malloc(nbytes);
}



extern "C" void * 
glue_realloc(void *p, size_t nbytes)
{
#ifdef VERIFY_ALLOCATOR_OVERRIDES
    if ( jsMallocCallBack != NULL )
		jsMallocCallBack( "glue_realloc" );
#endif // VERIFY_ALLOCATOR_OVERRIDES
    return memPool.Realloc(p, nbytes);
}



extern "C" void
glue_free(void *p)
{
#ifdef VERIFY_ALLOCATOR_OVERRIDES
	if ( jsMallocCallBack != NULL )
		jsMallocCallBack( "glue_free" );
#endif VERIFY_ALLOCATOR_OVERRIDES
	memPool.Free(p);
}

#endif // NETSCAPEJS_USES_MULTIPOOL


#else // #ifdef OVERRIDE_JS_ALLOCATOR


extern "C" void* glue_malloc(size_t nbytes)
{
	return malloc(nbytes);
}


extern "C" void* glue_realloc(void *p, size_t nbytes)
{
    return realloc(p, nbytes);
}

extern "C" void glue_free(void *p)
{
	free(p);
}

#endif // #ifdef OVERRIDE_JS_ALLOCATOR
