
#include "interpreter/scopestack.h"

using namespace obj;

Scope::~Scope() {
	for (auto local : mLocals) {
		obj::NDO->destroy(local->val);
	}
	for (auto tmp : mTemps) {
		obj::NDO->destroy(tmp.data());
	}
}

obj::Object* ScopeStack::getLocalUtil(Scope& scope, tp::String* id) {

	auto idx = scope.mLocals.presents(*id);

	if (idx) {
		return scope.mLocals.getSlotVal(idx);
	} else {
		mIterIdx--;
		Scope& parent_scope = mBuff[mIterIdx];
		ASSERT(parent_scope.mChildReachable && "Undefined Local Reference");
		return getLocalUtil(parent_scope, id);
	}
}

ScopeStack::ScopeStack() {
	mBuff = (Scope*) tp::HeapAllocGlobal::allocate(sizeof(Scope) * MAX_STACK_SIZE);
	mIdx = 0;
	mIterIdx = 0;
}

void ScopeStack::enterScope(bool aChildReachable) {
	ASSERT(MAX_STACK_SIZE - 1 > mIdx && "stack overflow");
	new (&mBuff[mIdx]) Scope();
	mBuff[mIdx].mChildReachable = aChildReachable;
	mIdx++;
}

void ScopeStack::leaveScope() {
	ASSERT(mIdx != NULL && "stack overflow");
	mBuff[mIdx - 1].~Scope();
	mIdx--;
}

void ScopeStack::addTemp(obj::Object* tmp) {
	obj::NDO->refinc(tmp);
	mBuff[mIdx - 1].mTemps.pushBack(tmp);
}

void ScopeStack::popTemp() {
	obj::NDO->destroy(mBuff[mIdx - 1].mTemps.last()->data);
	mBuff[mIdx - 1].mTemps.popBack();
}

void ScopeStack::addTempReturn(obj::Object* ret) {
	if (mIdx >= 2) {
		obj::NDO->refinc(ret);
		mBuff[mIdx - 2].mTemps.pushBack(ret);
	}
}

void ScopeStack::addLocal(obj::Object* local, tp::String id) {
	DEBUG_ASSERT(mIdx != 0 && "No scope given");
	Scope::ObjDict& locals = mBuff[mIdx - 1].mLocals;
	auto idx = locals.presents(id);
	if (idx) {
		obj::NDO->destroy(locals.getSlotVal(idx));
	}
	obj::NDO->refinc(local);
	locals.put(id, local);
}

obj::Object* ScopeStack::getLocal(tp::String& str) {
	mIterIdx = mIdx - 1;
	return getLocalUtil(mBuff[mIdx - 1], &str);
}

Scope* ScopeStack::getCurrentScope() { return &mBuff[mIdx - 1]; }

ScopeStack::~ScopeStack() { tp::HeapAllocGlobal::deallocate(mBuff); }
