// primPerform.cc -- #perform (and #doPrimitive) primitives
// 
// Author: Ian.Piumarta@INRIA.Fr
// 
// Last edited: 2000-11-29 15:27:22 by piumarta on emilia.rd.wdi.disney.com



#include "debug.h"
#include "archdep.h"
#include "generate.h"
#include "machine.h"
#include "primitive.h"
#include "cases.h"

#include "Frame.h"



//  activeContext argumentCount instructionPointer lkupClass method primitiveIndex receiver theHomeContext
extern insn *j_primitiveDoPrimitiveWithArgs(NativeMethod *nMeth, Frame *sender)
{
  oop argumentArrayOop= sender->stackValue(0);
  if (!(argumentArrayOop->isArray()))
    return 0;
  Array *argumentArray= argumentArrayOop->asArray();
  int argCount= argumentArrayOop->wordLength();

  oop primIndexOop= sender->stackValue(1);
  // FIX THIS RANGE CHECK
  if ((!primIndexOop->isInteger()) || ((sender->stackIndex() + argCount) > LargeFrame))
    return 0;	// FAIL
  int primIndex= primIndexOop->integerValue();

  oop rcvr= sender->stackValue(2);

  if ((!rcvr->isInteger()) && (rcvr->isPseudoContext()))
    {
      // only let through those primitives that we know to be safe
      switch (primIndex)
	{
	  // Array and stream primitives
	case  60: // primitiveAt
	case  61: // primitiveAtPut
	case  62: // primitiveSize
	  // StorageManagement Primitives (68-79)"
	case  68: // primitiveObjectAt
	case  69: // primitiveObjectAtPut
	case  73: // primitiveInstVarAt
	case  74: // primitiveInstVarAtPut
	case  76: // primitiveStoreStackp
	  // Control Primitives (80-89)
	case  81: // primitiveValue
	case  82: // primitiveValueWithArgs
	case  83: // primitivePerform
	case  84: // primitivePerformWithArgs
	case 100: // primitivePerformInSuperclass
	case 110: // primitiveEquivalent
	case 118: // primitiveDoPrimitiveWithArgs
	  // Miscellaneous primitives
	case 148: // primitiveClone
	case 197: // primitiveFindHandlerContext

	  // "Quick Push Const Methods (256-263)
	case 256: // primitivePushSelf
	case 257: // primitivePushTrue
	case 258: // primitivePushFalse
	case 259: // primitivePushNil
	case 260: // primitivePushMinusOne
	case 261: // primitivePushZero
	case 262: // primitivePushOne
	case 263: // primitivePushTwo
      //case 264 ... 519: // primitiveLoadInstVar
	cases256(264): // primitiveLoadInstVar
	  break;
	default:
	  fatal("unimplemented: doPrimitive %d in PseudoContext receiver", primIndex);
	  break;
	}
    }

  sender->drop(2);
  {
    for (int i= 0; i < argCount; ++i)
      sender->push(argumentArray->at(i));
  }

  switch (primIndex)
    {
      // Control Primitives (80-89)
    case  80: // primitiveBlockCopy
    case  81: // primitiveValue
    case  83: // primitivePerform
    case  84: // primitivePerformWithArgs
      fatal("unsupported doPrimitive: %d (see ContextPart.doPrimitive:receiver:args:)");
    case  82: // primitiveValueWithArgs
    case 100: // primitivePerformInSuperclass
    case 118: // primitiveDoPrimitiveWithArgs
      fatal("unimplemented doPrimitive: %d", primIndex);

      // all other primitives
    default:
      successFlag= true;
#    ifdef NEW_PRIMITIVES
      stackPointer= primitiveArguments - 1;
      {
	for (int i= argCount; i >= 0; --i)
	  *++stackPointer= sender->stackValue(i);
      }
#    else
      stackPointer= sender->stackp;
#    endif
      argumentCount= argCount;
      primitiveIndex= primIndex;
      receiverClass= lkupClass= nilObj->asClass();
      // NOTE: cannot set messageSelector (this is a BUG in the Interpreter!!!)

      PRINTF(("doPrimitive: %d\n", primIndex));

#    ifndef NDEBUG
      assert(activeFrame == sender);
      activeFrame= 0;
#    endif

      GC_PROTECT(sender, {
	argumentArray->pushRemappable();
	((j_primitive)primitiveTable[primIndex])(nMeth, sender);
	argumentArray= popRemappable(Array);
      });

#    ifndef NDEBUG
      activeFrame= sender;
#    endif

      if (successFlag)
	{
#	 ifdef NEW_PRIMITIVES
	  sender->popThenPush(argCount+1, primitiveArguments[0]);
#	 else
	  sender->stackp= stackPointer;
#	 endif
	  return g_primitiveEpilogue;	// escape from control primitive response
	}

      // restore original state for failure code

      sender->drop(argCount);
      sender->push(primIndexOop);
      sender->push(argumentArray);
    }
  return 0;				// run failure code
}



//  activeContext argumentCount instructionPointer lkupClass messageSelector method newMethod primitiveIndex receiver theHomeContext
insn *j_primitivePerform(NativeMethod *nMeth, Frame *sender)
{
  // stackValue(0) --->	N arguments...
  // stackValue(N) --->	selector
  // stackValue(N+1) ->	receiver

  int nArgs= nMeth->argumentCount;	// arg count of #perform:[with:[with:[...]]]
  assert(nArgs > 0);

  Symbol *selectorPerform= messageSelector;

  Symbol *newSelector= sender->stackValue(nArgs - 1)->asSymbol();
  assert(newSelector->isSymbol());
  oop newReceiver= sender->stackValue(nArgs);

  // remove selector so that #doesNotUnderstand: will work.
  sender->stackRemove(nArgs - 1);

  assert(activeFrame == sender);

  selectorPerform->pushRemappable();
  NativeMethod *destMethod=
    NativeMethod::find(newSelector, nArgs - 1, newReceiver);
  selectorPerform= popRemappable(Symbol);

  if (destMethod->argumentCount != (size_t)argumentCount)
    {
      // failure
      sender->stackInsert(selectorPerform, nArgs - 2);
      return 0;			// failure
    }

  // success: transfer to destination

  return destMethod->controlEntry;
}



static insn *j_primitivePerformAt(Frame *sender, Class *rCls, Class *lCls)
{
  Array *argumentArray= sender->stackValue(0)->asArray();

  if (!argumentArray->isArray())
    return 0;						// fail

  size_t arraySize= argumentArray->wordLength();

  if (sender->stackIndex() + arraySize > LargeFrame)	// FIX THIS
    return 0;						// fail

  sender->drop(1);					// pop argument array

  messageSelector= sender->pop()->asSymbol();		// pop selector

  // copy the arguments to the stack, and execute

  {
    for (size_t index= 0;  index < arraySize;  ++index)
      sender->push(argumentArray->at(index));
  }

  assert(activeFrame == sender);

  argumentArray->pushRemappable();
  messageSelector->pushRemappable();
  NativeMethod *destMethod=
    NativeMethod::find(messageSelector, arraySize, rCls, lCls);
  messageSelector= popRemappable(Symbol);
  argumentArray= popRemappable(Array);

  if (destMethod->argumentCount == arraySize)
    return destMethod->controlEntry;			// success

  // restore the state and fail

  sender->drop(argumentCount);
  sender->push(messageSelector);
  sender->push(argumentArray);

  return 0;						// fail
}



//  activeContext argumentCount instructionPointer lkupClass messageSelector method newMethod primitiveIndex receiver theHomeContext
extern insn *j_primitivePerformInSuperclass(NativeMethod *nMeth, Frame *sender)
{
  Class *lookupClass= sender->stackValue(0)->asClass();
  oop rcvr= sender->stackValue(nMeth->argumentCount);
  Class *rCls= rcvr->fetchClass();

  {
    for (Class *currentClass= rCls;
	 currentClass != lookupClass;
	 currentClass= currentClass->superclass)
      {
	if (currentClass->isNil())
	  return 0;	// failure
      }
  }

  sender->drop(1);

  insn *resume= j_primitivePerformAt(sender, rCls, lookupClass);

  if (resume == 0)	// failure
    sender->push(lookupClass);

  return resume;
}



//  activeContext argumentCount instructionPointer lkupClass messageSelector method newMethod primitiveIndex receiver theHomeContext
extern insn *j_primitivePerformWithArgs(NativeMethod *nMeth, Frame *sender)
{
  oop rcvr= sender->stackValue(nMeth->argumentCount);
  Class *rCls= rcvr->fetchClass();
  return j_primitivePerformAt(sender, rCls, rCls);
}
