#include "stdafx.h"

#include <Compiler.h>

#include "common.h"

#ifdef _AMD64_
#include "../compiler_x64/opcode.h"
#else
#include "../compiler_x86/opcode.h"
#endif

//デバッグ用
#include "debug.h"

using namespace ActiveBasic::Compiler;

int Debugging_GetArray( const Subscripts &subscripts,char *array,const Type &type,LONG_PTR *plpOffset);

ULONG_PTR Debugging_GetVarPtr(RELATIVE_VAR *pRelativeVar){
	extern DWORD ImageBase;
	extern int MemPos_RWSection;
	int i2;

	if(pRelativeVar->dwKind==VAR_GLOBAL){
		return ImageBase+MemPos_RWSection+pRelativeVar->offset;
	}
	else if( pRelativeVar->dwKind == VAR_REFGLOBAL ){
		extern HANDLE hDebugProcess;
		LONG_PTR lpData;
		SIZE_T accessBytes;
		ReadProcessMemory(hDebugProcess,
			(void *)(ImageBase+MemPos_RWSection+pRelativeVar->offset),
			&lpData,
			sizeof(LONG_PTR),
			&accessBytes);

		return lpData;
	}
	else if(pRelativeVar->dwKind==VAR_LOCAL){
		extern HWND hDebugWnd;
		i2=(int)SendDlgItemMessage(hDebugWnd,IDC_PROCCOMBO,CB_GETCURSEL,0,0);
		i2=pobj_dti->iProcLevel-i2;

		if(pobj_dti->lplpSpBase[i2]==0) return 0;

		return pobj_dti->lplpSpBase[i2]+pRelativeVar->offset;
	}
	else if( pRelativeVar->dwKind == VAR_REFLOCAL ){
		extern HWND hDebugWnd;
		i2=(int)SendDlgItemMessage(hDebugWnd,IDC_PROCCOMBO,CB_GETCURSEL,0,0);
		i2=pobj_dti->iProcLevel-i2;

		if(pobj_dti->lplpSpBase[i2]==0) return 0;

		extern HANDLE hDebugProcess;
		LONG_PTR lpData;
		SIZE_T accessBytes;
		ReadProcessMemory(hDebugProcess,
			(void *)(pobj_dti->lplpSpBase[i2]+(int)pRelativeVar->offset),
			&lpData,
			sizeof(LONG_PTR),
			&accessBytes);

		return lpData;
	}
	else if(pRelativeVar->dwKind==VAR_DIRECTMEM){
		return pRelativeVar->offset;
	}

	return 0;
}

bool Debugging_SetRelativeOffset( Type &type,RELATIVE_VAR *pRelativeVar,char *lpPtrOffset){
	int array_num;

	_int64 i64data;
	if( !StaticCalculation( true, lpPtrOffset, 0, &i64data, Type(), 1 ) ){
		return false;
	}
	if( type.IsReal() ){
		double dbl;
		memcpy(&dbl,&i64data,sizeof(double));
		i64data=(_int64)dbl;
	}

	array_num=(int)i64data;

	if( type.PtrLevel() ){
		type.PtrLevelDown();
		array_num *= type.GetSize();
	}
	else{
		//エラー
		return false;
	}

	extern HANDLE hDebugProcess;
	LONG_PTR lpData;
	SIZE_T accessBytes;
	lpData=Debugging_GetVarPtr(pRelativeVar);
	if(!ReadProcessMemory(hDebugProcess,(void *)lpData,&pRelativeVar->offset,sizeof(LONG_PTR),&accessBytes)){
		return false;
	}
	pRelativeVar->dwKind=VAR_DIRECTMEM;

	pRelativeVar->offset+=array_num;
	return true;
}

int Debugging_GetMember( const CClass &objClass,char *member,RELATIVE_VAR *pRelativeVar, Type &resultType, BOOL bPrivateAccess){
	int i2;

	//直接参照に切り替え
	pRelativeVar->offset=(LONG_PTR)Debugging_GetVarPtr(pRelativeVar);
	pRelativeVar->dwKind=VAR_DIRECTMEM;

	//クラス、配列の構成要素を解析する
	char VarName[VN_SIZE];		//変数名
	char array[VN_SIZE];		//第1次配列
	char lpPtrOffset[VN_SIZE];	//第2次配列
	char NestMember[VN_SIZE];	//入れ子メンバ
	ReferenceKind refType;
	lstrcpy(VarName,member);
	if(!GetVarFormatString(VarName,array,lpPtrOffset,NestMember, refType ) ) return 0;


	////////////////////////////
	// メンバオフセットを取得
	////////////////////////////

	const Member *pMember = objClass.FindDynamicMember( VarName );
	if( !pMember )
	{
		return 0;
	}

	int offset = objClass.GetMemberOffset( VarName );


	//アクセシビリティをチェック
	if(( bPrivateAccess==0 && pMember->IsPrivate() )||
		pMember->IsNoneAccess() ){
		return 0;
	}
	else if(bPrivateAccess==0&&pMember->IsProtected())
		return 0;

	resultType = pMember->GetType();

	//ポインタ変数の場合
	if( resultType.IsPointer() ){
		if( pMember->GetSubscripts().size() == 0 ){
			lstrcpy(lpPtrOffset,array);
			array[0]=0;
		}
	}
	else{
		if(lpPtrOffset[0]) return 0;
	}

	pRelativeVar->offset+=offset;

	if(array[0]){
		//配列オフセット
		i2=Debugging_GetArray(
			pMember->GetSubscripts(),
			array,
			resultType,
			&pRelativeVar->offset);
		if(i2==0){
			//式エラー
			return 0;
		}
		if(i2==-1){
			//アクセスエラー
			return -1;
		}
	}
	else if( pMember->GetSubscripts().size() > 0 ){
		resultType.SetBasicType( resultType.GetBasicType() | FLAG_PTR );
	}

	if(NestMember[0]){
		//入れ子構造の場合

		if( resultType.IsObject() || resultType.IsStruct() ){
			if( refType != RefDot ) return 0;

			if( resultType.IsObject() ){
				extern HANDLE hDebugProcess;
				LONG_PTR lpData;
				SIZE_T accessBytes;
				lpData=Debugging_GetVarPtr(pRelativeVar);
				if(!ReadProcessMemory(hDebugProcess,(void *)lpData,&pRelativeVar->offset,sizeof(LONG_PTR),&accessBytes)) return -1;
				pRelativeVar->dwKind=VAR_DIRECTMEM;
			}
		}
		else if( resultType.IsObjectPtr() || resultType.IsStructPtr() ){
			//構造体ポインタ型メンバ変数

			if(lpPtrOffset[0]){
				if( refType != RefDot ) return 0;

				//直接参照に切り替え
				Debugging_SetRelativeOffset(resultType,pRelativeVar,lpPtrOffset);

				lpPtrOffset[0]=0;
			}
			else{
				if( refType != RefPointer ) return 0;

				extern HANDLE hDebugProcess;
				LONG_PTR lpData;
				SIZE_T accessBytes;
				lpData=Debugging_GetVarPtr(pRelativeVar);
				if(!ReadProcessMemory(hDebugProcess,(void *)lpData,&pRelativeVar->offset,sizeof(LONG_PTR),&accessBytes)) return -1;
				pRelativeVar->dwKind=VAR_DIRECTMEM;
			}
		}

		i2=Debugging_GetMember(pMember->GetType().GetClass(),
			NestMember,
			pRelativeVar,
			resultType,
			0);
		if(i2==0){
			//式エラー
			return 0;
		}
		if(i2==-1){
			//アクセスエラー
			return -1;
		}
	}

	if(lpPtrOffset[0]){
		Debugging_SetRelativeOffset(resultType,pRelativeVar,lpPtrOffset);
	}

	return 1;
}
int Debugging_GetArray( const Subscripts &subscripts,char *array,const Type &type,LONG_PTR *plpOffset){
	extern HANDLE hHeap;
	int i,i2,i3,i4,i5,array_offset;
	char temporary[VN_SIZE],*pParm[MAX_PARMS];

	for(i=0,i2=0,i3=0;;i++,i2++){
		if(array[i]=='('){
			i4=GetStringInPare(temporary+i2,array+i);
			i+=i4-1;
			i2+=i4-1;
			continue;
		}
		if(array[i]=='['){
			i4=GetStringInBracket(temporary+i2,array+i);
			i+=i4-1;
			i2+=i4-1;
			continue;
		}
		if(array[i]==','||array[i]=='\0'){
			if( i3 >= (int)subscripts.size() )
			{
				for(i3--;i3>=0;i3--) HeapDefaultFree(pParm[i3]);
				return 0;
			}

			temporary[i2]=0;

			pParm[i3]=(char *)HeapAlloc(hHeap,0,lstrlen(temporary)+1);
			lstrcpy(pParm[i3],temporary);

			i3++;

			if(array[i]=='\0'){
				if( i3 < (int)subscripts.size() )
				{
					for(i3--;i3>=0;i3--) HeapDefaultFree(pParm[i3]);
					return 0;
				}
				break;
			}

			i2=-1;
			continue;
		}
		temporary[i2]=array[i];
	}

	array_offset=0;

	for(i=i3-1;i>=0;i--){
		_int64 i64data;
		Type resultType;
		bool isMemoryAccessError;
		if( !StaticCalculation(true, pParm[i],0,&i64data,resultType,1, &isMemoryAccessError ) ){
			//式エラー
			return 0;
		}
		if(isMemoryAccessError){
			//アクセスエラー
			return -1;
		}

		if(resultType.IsReal()){
			double dbl;
			memcpy(&dbl,&i64data,sizeof(double));
			i64data=(_int64)dbl;
		}
		i5=(int)i64data;

		for(i2=i+1,i4=1;i2<i3;i2++) i4*=subscripts[i2]+1;

		array_offset+=i5*i4;

		HeapDefaultFree(pParm[i]);
	}

	array_offset *= type.GetSize();

	*plpOffset+=array_offset;

	return 1;
}
ULONG_PTR Debugging_GetThisPtrOffset(LONG_PTR obp_Rip){
	UserProc *pUserProc = GetSubFromObp(obp_Rip);

	BOOST_FOREACH( Variable *pVar, pUserProc->GetLocalVars() ){
		if( pVar->GetName() == "_System_LocalThis" ){
			return pVar->GetOffsetAddress();
		}
	}
	return 0;
}
int Debugging_GetVarOffset( char *variable,RELATIVE_VAR *pRelativeVar, Type &resultType, Subscripts *pResultSubscripts){
	extern HANDLE hDebugProcess;
	int i2,i3;
	char member[VN_SIZE],VarName[VN_SIZE],array[VN_SIZE],lpPtrOffset[VN_SIZE];
	LONG_PTR lpData;
	SIZE_T accessBytes;

	lstrcpy(VarName,variable);
	ReferenceKind refType;
	GetVarFormatString(VarName,array,lpPtrOffset,member,refType);

	const Subscripts *pSubscripts;
	bool isArray;


	/////////////////
	// ローカル変数
	/////////////////
	if( compiler.IsLocalAreaCompiling() ){
		const Variable *pVar = compiler.GetCompilingUserProc().GetLocalVars().Find( LexicalAnalyzer::FullNameToSymbol( VarName ) );

		if( pVar ){
			//ポインタ変数の場合
			if( pVar->GetType().IsPointer() ){
				if( !pVar->IsArray() ){
					lstrcpy(lpPtrOffset,array);
					array[0]=0;
				}
			}
			else{
				if(lpPtrOffset[0]) return 0;
			}

			pRelativeVar->offset = pVar->GetOffsetAddress();
			if( pVar->IsRef() ){
				pRelativeVar->dwKind=VAR_REFLOCAL;
			}
			else{
				pRelativeVar->dwKind=VAR_LOCAL;
			}
			resultType = pVar->GetType();
			isArray = pVar->IsArray();
			pSubscripts = &pVar->GetSubscripts();
		}
	}

	if( compiler.IsCompilingClass() )
	{
		///////////////////////
		// クラスメンバの参照
		///////////////////////

		if(memicmp(variable,"This.",5)==0){
			//Thisオブジェクトのメンバを参照するとき
			SlideString(variable+5,-5);
			lstrcpy(VarName,variable);
		}
		else{
			//クラス内の動的メンバを参照するとき（通常）

			if( !compiler.GetCompilingClass().HasDynamicMember( VarName ) )
			{
				goto NonClassMember;
			}
		}

		/////////////////////////////
		// thisポインタを取得

		extern HWND hDebugWnd;
		i2=(int)SendDlgItemMessage(hDebugWnd,IDC_PROCCOMBO,CB_GETCURSEL,0,0);
		i2=pobj_dti->iProcLevel-i2;

		lpData=Debugging_GetThisPtrOffset(pobj_dti->lplpObp[i2]);
		if(!lpData){
			//式エラー
			return 0;
		}
		lpData+=pobj_dti->lplpSpBase[i2];
		if(!ReadProcessMemory(hDebugProcess,(void *)lpData,&pRelativeVar->offset,sizeof(LONG_PTR),&accessBytes)){
			//メモリにアクセスできないとき
			return -1;
		}

		pRelativeVar->dwKind=VAR_DIRECTMEM;

		i3=Debugging_GetMember( compiler.GetCompilingClass(),variable,pRelativeVar,resultType,1);
		if(i3==0){
			//式エラー
			return 0;
		}
		if(i3==-1){
			//アクセスエラー
			return -1;
		}

		return 1;
	}

NonClassMember:

	{
		///////////////////
		// グローバル変数
		///////////////////

		const Variable *pVar = compiler.GetObjectModule().meta.GetGlobalVars().Find( LexicalAnalyzer::FullNameToSymbol( VarName ) );
		if( !pVar ){
			//一致しないとき
			return 0;
		}

		//ポインタ変数の場合
		if( pVar->GetType().IsPointer() ){
			if( !pVar->IsArray() ){
				lstrcpy(lpPtrOffset,array);
				array[0]=0;
			}
		}
		else{
			if(lpPtrOffset[0]) return 0;
		}

		pRelativeVar->offset=pVar->GetOffsetAddress();
		if(pVar->IsRef()) pRelativeVar->dwKind=VAR_REFGLOBAL;
		else pRelativeVar->dwKind=VAR_GLOBAL;
		resultType = pVar->GetType();
		isArray = pVar->IsArray();
		pSubscripts = &pVar->GetSubscripts();
	}


	if(array[0]==0&&isArray){
		//配列の先頭ポインタを示す場合
		resultType.SetBasicType( resultType.GetBasicType() | FLAG_PTR );
		if( pResultSubscripts )
		{
			(*pResultSubscripts) = *pSubscripts;
		}
		return 1;
	}

	if(array[0]){
		i3=Debugging_GetArray( *pSubscripts, array,resultType,&pRelativeVar->offset);
		if(i3==0){
			//式エラー
			return 0;
		}
		if(i3==-1){
			//アクセスエラー
			return -1;
		}
	}
	if(member[0]){
		if( resultType.IsObject() || resultType.IsStruct() ){
			//実態オブジェクトのメンバを参照（obj.member）
			if( refType != RefDot ){
				return 0;
			}

			i3=Debugging_GetMember(resultType.GetClass(),member,pRelativeVar,resultType,0);
			if(i3==0){
				//式エラー
				return 0;
			}
			if(i3==-1){
				//アクセスエラー
				return -1;
			}
		}
		else if( resultType.IsObjectPtr() || resultType.IsStructPtr() ){
			//ポインタオブジェクトが示すメンバを参照
			if(lpPtrOffset[0]){
				//pObj[n].member
				if( refType != RefDot ) return 0;
				Debugging_SetRelativeOffset(resultType,pRelativeVar,lpPtrOffset);

				i3=Debugging_GetMember(resultType.GetClass(),member,pRelativeVar,resultType,0);
				if(i3==0){
					//式エラー
					return 0;
				}
				if(i3==-1){
					//アクセスエラー
					return -1;
				}
			}
			else{
				//pObj->member
				if( refType != RefPointer ) return 0;

				pRelativeVar->offset=Debugging_GetVarPtr(pRelativeVar);
				pRelativeVar->dwKind=VAR_DIRECTMEM;

				if(!ReadProcessMemory(hDebugProcess,(void *)pRelativeVar->offset,&lpData,sizeof(LONG_PTR),&accessBytes)) return -1;
				pRelativeVar->offset=lpData;

				i3=Debugging_GetMember(resultType.GetClass(),member,pRelativeVar,resultType,0);
				if(i3==0){
					//式エラー
					return 0;
				}
				if(i3==-1){
					//アクセスエラー
					return -1;
				}
			}
		}
		else{
			return 0;
		}
		return 1;
	}

	if(lpPtrOffset[0]){
		if(!Debugging_SetRelativeOffset(resultType,pRelativeVar,lpPtrOffset)) return 0;
	}

	return 1;
}
