// Frame.cc -- architecture-independent interface to native stack frames
// 
// Author: Ian.Piumarta@INRIA.Fr
//
// Last edited: 2000-11-30 12:01:44 by piumarta on emilia.rd.wdi.disney.com


#include "Frame.h"
#include "NativeProcess.h"
#include "machine.h"


void Frame::resume(void)
{
# ifndef NDEBUG
  // have to clear activeFrame from reload
  extern Frame *activeFrame;
  activeFrame= 0;
# endif
  tramp_resume((Frame *)this);
}





/// 
/// primitive support
/// 


// NOTE: idx is in C coordinates

oop Frame::instVarAt(int idx)
{
  assert(hasPseudoContext());
  assert(activeFrame != 0);	// we're GC-safe

  switch (idx)
    {
    case 0: // sender
      {
	if (senderFrame()->isBaseFrame())
	  {
	    PRINTF(("Frame::instVarAt(sender) => nil\n"));
	    return nilObj;
	  }
	Frame *cxSenderFrame= senderFrame();
	if (!cxSenderFrame->hasPseudoContext())
	  {
	    cxSenderFrame->allocatePseudoContext();
	    assert(cxSenderFrame->hasPseudoContext());
	  }
	PRINTF(("Frame::instVarAt(sender) => "));
	PRINTLN(cxSenderFrame->pseudoContext());

	return cxSenderFrame->pseudoContext();
      }
    case 1: // pc
      {
	return Object::integer(vPC());
      }
    case 2: // stackp
      {
	return Object::integer(stackIndex());
      }
    case 3: // method | nArgs (init in PseudoContext creation)
      {
	return pseudoContext()->nArgsOrMethod;
      }
    case 4: // startPC | receiverMap (init in PseudoContext creation)
      {
	return pseudoContext()->startPcOrReceiverMap;
      }
    case 5: // homeOrReceiver (init in PseudoContext creation)
      {
	assert(pseudoContext()->homeOrReceiver->okayFields());
	return pseudoContext()->homeOrReceiver;
      }
    }

  fprintf(stderr, "Frame::instVarAt: index %d out of range", idx);
  return 0;
}



// NOTE: idx is in C coordinates; answer 0 to "fail"

oop Frame::instVarAtPut(int idx, oop val)
{
  assert(hasPseudoContext());
  assert(activeFrame != 0);	// we're GC-safe

  switch (idx)
    {
    case 0: // sender
      {
	PRINTF(("STORING CONTEXT %p [FRAME %p] SENDER WITH ", pseudoContext(), this));
	PRINTLN(val);

	if (val->isNil())
	  {
	    // sender <- nil
	    if (senderFrame()->isBaseFrame())
	      return val;		// nothing to do...
	    senderFrame()->stabiliseAll();
	    senderFrame()->beBaseFrame();
	    return val;
	  }

	// assume storing into our own stack...

	if ((!val->isMethodContext())
	    && (!val->isBlockContext())
	    && (!val->isPseudoContext()))
	  {
	    return 0;
	  }

	Context *topCtx= 0;

	val->pushRemappable();
	topCtx= activeFrame->stabiliseAll();
	val= popRemappableOop();

	if (val->isPseudoContext())
	  {
	    fprintf(stderr, 
		    "attempt to store Context sender with another Process's context");
	    return 0;
	  }

	// this check will FAIL when we reinstate multiple native processes
	if (pseudoContext()->isPseudoContext())
	  {
	    fprintf(stderr, "attempt to store Context sender of another Process");
	    return 0;
	  }

	assert(pseudoContext()->isBlockContext() || pseudoContext()->isMethodContext());

	pseudoContext()->checkStore(pseudoContext()->sender= (val->asContext()));

	// resume with modified stack

	tramp_callOnCStack((void *)NativeProcess::reloadAndResumeContext, (int)topCtx);

	return 0;		// NOT REACHED!!!
      }
    case 1: // pc
      {
	if (!val->isInteger())
	  {
	    fprintf(stderr, "attempt to store non-Integer pc into Context");
	    return 0;
	  }
	int newPC= val->integerValue();
	NativeMethod *nm= nativeMethod();
	if (!nm->includesVPC(newPC))
	  {
	    fprintf(stderr, "attempt to store illegal pc %d into Context", newPC);
	    return 0;
	  }
	insn *nPC= nm->v2nPC(newPC);
	setPC(nPC);
	return val;
      }
    case 2: // stackp
      {
	int index= 0;
	if (val->isInteger())
	  {
	    index= val->integerValue();
	    if ((index < 0) || (index > LargeFrame))	// FIX THIS
	      {
		fprintf(stderr,
			"attempt to store illegal stackp %d into Context", index);
		return 0;
	      }
	  }
	else
	  {
	    if (val->notNil())
	      {
		fprintf(stderr, "attempt to store non-Integer stackp into Context");
		return 0;
	      }
	  }
	while (index > (int)stackIndex())
	  push(nilObj);
	stackIndex(index);
	return val;
      }
    case 3: // BlockContext.nargs | MethodContext.method (init in PseudoContext creation)
      {
	if (pseudoContext()->isPseudoBlockContext())
	  {
	    if (!val->isInteger())
	      {
		fprintf(stderr, "attempt to store non-Integer nargs into BlockContext");
		return 0;
	      }
	    pseudoContext()->nArgsOrMethod= val;
	  }
	else
	  {
	    assert(pseudoContext()->isPseudoMethodContext());
	    assert(pseudoContext()->nArgsOrMethod->isCompiledMethod());
	    if (!val->isCompiledMethod())
	      {
		fprintf(stderr,
			"attempt to store non-CompiledMethod into MethodContext.method");
		return 0;
	      }
	    pseudoContext()->checkStore(pseudoContext()->nArgsOrMethod= val);

	    // need to recompile method and then figure out what the
	    // fuck to do about finding a new PC for the frame...
	    NativeMethod *nm= nativeMethod();
	    messageSelector= nm->selector();
	    argumentCount=   nm->argumentCount;
	    receiverClass=   lkupClass= nm->receiverClass();
	    methodClass=     nm->methodClass();
	    newMethod=       val->asCompiledMethod();
	    primitiveIndex=  nm->primitiveIndex;

	    val->pushRemappable();
	    gen_compile();
	    val= popRemappableOop();
	    nm= newNativeMethod;

	    int oldPC= vPC();
	    setNativeMethod(nm);
	    
	    assert(nm->compiledMethod() == val);
	    insn *nPC= 0;
	    if (nm->includesVPC(oldPC))
	      nPC= nm->v2nPC(oldPC);
	    else
	      nPC= t_mxFault;	// trap if not reinitialised before resumed
	    setPC(nPC);
	  }
	return val;
      }
    case 4: // BlockContext.startPC | MethodContext.receiverMap (init in PCtx creation)
      {
	if (pseudoContext()->isPseudoBlockContext())
	  {
	    if (!val->isInteger())
	      {
		fprintf(stderr,
			"attempt to store non-Integer startpc into BlockContext");
		return 0;
	      }
	    int newPC= val->integerValue();
	    if (!nativeMethod()->includesBlockVPC(newPC))
	      {
		fprintf(stderr, 
			"attempt to store illegal startpc %d into BlockContext", newPC);
		return 0;
	      }
	    pseudoContext()->startPcOrReceiverMap= val;
	  }
	else
	  {
	    pseudoContext()->checkStore(pseudoContext()->startPcOrReceiverMap= val);
	  }
	return val;
      }
    case 5: // BlockContext.home | MethodContext.receiver (init in PCtx creation)
      {
	if (pseudoContext()->isPseudoBlockContext())
	  {
	    if (!val->isMethodContext() || !val->isPseudoMethodContext())
	      {
		fprintf(stderr,
			"attempt to store non-MethodContext home into BlockContext");
		return 0;
	      }
	    pseudoContext()->checkStore(pseudoContext()->homeOrReceiver= val);
	    // block frame's receiver is it's home context
	    receiver= val;
	  }
	else
	  {
	    assert(pseudoContext()->isPseudoMethodContext());
	    pseudoContext()->checkStore(pseudoContext()->homeOrReceiver= val);
	    receiver= val;
	  }
	return val;
      }
    }
  fprintf(stderr, "Frame::instVarAtPut: index %d out of range", idx);
  return 0;
}



// NOTE: idx is in C coordinates

oop Frame::at(int idx)
{
  if ((idx < 0) || (idx >= (int)stackIndex()))
    return 0;	// FAIL

#ifdef STACK_GROWS_DOWN
  return stackFirst()[-idx];
#else
  return stack[idx];
#endif
}


// NOTE: idx is in C coordinates

oop Frame::atPut(int idx, oop obj)
{
  if ((idx < 0) || (idx >= (int)stackIndex()))
    return 0;	// FAIL

#ifdef STACK_GROWS_DOWN
  return stackFirst()[-idx]= obj;
#else
  return stack[idx]= obj;
#endif
}


// NOTE: idx is in **Smalltalk** coordinates

bool Frame::storeStackp(int idx)
{
  if ((idx < 0) || (idx > LargeFrame))	// FIX THIS
    return false;	// FAIL

  while ((int)stackIndex() < idx)
    push(nilObj);

  stackIndex(idx);

  return true;
}


Context *Frame::clone(void)
{
  assert(hasPseudoContext());

  assert(activeFrame != 0);	// we're GC-safe

  if (!senderFrame()->hasPseudoContext())
    {
      senderFrame()->allocatePseudoContext();
    }

  Context *nx= 0;

  if (pseudoContext()->isPseudoBlockContext())
    {
      nx= (new BlockContext())->asContext();
    }
  else
    {
      assert(pseudoContext()->isPseudoMethodContext());
      nx= (new MethodContext())->asContext();
    }

  nx->sender=               senderFrame()->pseudoContext();
  nx->pc=                   Object::integer(vPC());
  nx->stackp=               Object::integer(stackIndex());
  nx->nArgsOrMethod=        pseudoContext()->nArgsOrMethod;
  nx->startPcOrReceiverMap= pseudoContext()->startPcOrReceiverMap;
  nx->homeOrReceiver=       pseudoContext()->homeOrReceiver;

  size_t i= stackIndex();
  while (i-- != 0)
    {
#    ifdef STACK_GROWS_DOWN
      nx->stack[i]= stackFirst()[-i];
#    else
      nx->stack[i]= stack[i];
#    endif
    }
  
  nx->beRoot();

  return nx;
}
