// primValue.cc -- #value (and #blockCopy) primitives
// 
// Author: Ian.Piumarta@INRIA.Fr
// 
// Last edited: Mon Sep 18 14:01:35 2000 by piumarta (Ian Piumarta) on emilia



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

#include "Frame.h"


extern "C" { void primitiveBlockCopy(void); }

//  instructionPointer
extern void j_primitiveBlockCopy(NativeMethod *nMeth, Frame *sender)
{
  instructionPointer= nMeth->n2vPC(sender->pc);
  primitiveBlockCopy();
}



//  activeContext argumentCount instructionPointer method receiver theHomeContext
extern insn *j_primitiveValue(NativeMethod *nMeth, Frame *sender)
{
  // NOTE: if we were passed the NEW frame instead of the sender then
  // we could perform all of the argument copying, convert the receiver
  // into a PseudoContext for the new frame (to preserve the Squeak
  // non-reentrant block semantics), etc...

  int nArgs= nMeth->argumentCount;
  oop rcvr= sender->stackValue(nArgs);

  PRINTF(("primitiveValue "));
  PRINTLN(rcvr);

  if ((!rcvr->isBlockContext())
      || (rcvr->asBlockContext()->nargs->integerValue() != nArgs))
    return 0;

  BlockContext *closure= rcvr->asBlockContext();
  oop home= closure->home;
  NativeMethod *homeMethod= 0;

# if defined(USE_CLOSURE_CACHE)
  if (closure->stack[ClosureCacheIndex] != 0)
    {
      assert(closure->stackp == Object::integer(0));
      // answer entry point
      PRINTF(("enter block at %p\n", entry));
      return (insn *)closure->stack[ClosureCacheIndex];
    }
# endif // USE_CLOSURE_CACHE

  if (home->isPseudoMethodContext())
    {
      homeMethod= home->asPseudoContext()->frame()->nativeMethod();
    }
  else if (home->isMethodContext())
    {
      assert(activeFrame == sender);
      home->pushRemappable();
      homeMethod= NativeMethod::find(home->asMethodContext());
      home= popRemappableOop();
      home->beRoot();
    }
  else
    {
      home->print();
      putchar('\n');
      fatal("this block has no home?");
    }

# ifndef BLOCK_CLOSURES
  // pcx->frame= calleeFrame (calleeFrame->pcx= pcx set in blockActivate)
  closure->asContext()->bePseudoContext(sender->calleeFrame());
# endif

  // answer entry point
  insn *entry=
    homeMethod->v2nBlockPC(closure->asBlockContext()->startpc->integerValue());

# if defined(USE_CLOSURE_CACHE)
  assert(closure->stackp == Object::integer(0));
# if 0
  closure->stack[ClosureHomeIndex]= (oop)homeMethod;
# endif
  closure->stack[ClosureCacheIndex]= (oop)entry;
# endif

  PRINTF(("enter block at %p\n", entry));
  return entry;
}



//  activeContext instructionPointer method receiver theHomeContext
extern insn *j_primitiveValueWithArgs(NativeMethod *nMeth, Frame *sender)
{
  Array *argumentArray= sender->stackValue(0)->asArray();
  if (!argumentArray->isArray())
    return 0;

  oop rcvr= sender->stackValue(1);

  if (!rcvr->isBlockContext())
    return 0;

  PRINTF(("primitiveValue "));
  PRINTLN(rcvr);

  BlockContext *closure= rcvr->asBlockContext();

  int blockArgumentCount= closure->nargs->integerValue();
  int arrayArgumentCount= argumentArray->wordLength();

  if (arrayArgumentCount != blockArgumentCount)
    return 0;

  oop home= closure->home;
  NativeMethod *homeMethod= 0;

  if (home->isPseudoMethodContext())
    {
      homeMethod= home->asPseudoContext()->frame()->nativeMethod();
    }
  else if (home->isMethodContext())
    {
      assert(activeFrame == sender);
      home->pushRemappable();
      homeMethod= NativeMethod::find(home->asMethodContext());
      home= popRemappableOop();
      home->beRoot();
    }
  else
    {
      home->print();
      putchar('\n');
      fatal("this block has no home?");
    }

  sender->drop(1);	// argumentArray
  for (int i= 0; i < blockArgumentCount; ++i)
    sender->push(argumentArray->at(i));

# ifndef BLOCK_CLOSURES
  // pcx->frame= calleeFrame (calleeFrame->pcx= pcx set in blockActivate)
  closure->asContext()->bePseudoContext(sender->calleeFrame());
# endif

  // answer entry point
  insn *entry=
    homeMethod->v2nBlockPC(closure->asBlockContext()->startpc->integerValue());
  PRINTF(("enter block at %p\n", entry));
  return entry;
}
