/* Metallurgy - Higher Level Interface to Chordata
 * Copyright (C) 2001-2002  Jay Freeman (saurik)
*/

/*
 *        Redistribution and use in source and binary
 * forms, with or without modification, are permitted
 * provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the
 *    above copyright notice, this list of conditions
 *    and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the
 *    above copyright notice, this list of conditions
 *    and the following disclaimer in the documentation
 *    and/or other materials provided with the
 *    distribution.
 * 3. The name of the author may not be used to endorse
 *    or promote products derived from this software
 *    without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "stdafx.h"

#include "metallurgy/Method.h"
#include "metallurgy/ILMethodImpl.h"

#include "chordata/ILMethod.h"
using namespace Chordata::ILMethod;

#include "filet/Utils.h"

namespace Metallurgy {

ILMethodImpl::ILMethodImpl(const Method *method, uint32_t coderva, uint32_t flags) :
	MethodImpl(method, coderva, flags),
	locals(NULL)
{
	if (getCodeRVA() != 0) {
		offset = (uint8_t *) module->FindRVA(getCodeRVA());
		switch (*offset & Flags::FormatMask) {
			case Flags::TinyFormat:
			case Flags::TinyFormat1:
				length = ((Tiny *) offset++)->size;
			break;
			case Flags::FatFormat: {
				// Oh come now... don't tell me you don't know Fat Tony!
				Fat *tony = (Fat *) offset;
				length = tony->CodeSize;
				locals = dynamic_cast<StandAlone *>(module->ResolveToken(tony->LocalVarSigTok));

				uint32_t distance(length);
				uint8_t *section = offset += tony->Size * 4;
				if (tony->Flags & Flags::MoreSects) {
					do {
						section = Filet::Utils::AlignPointer(section + distance);

						bool isFat;
						if (*section & Section::Flags::FatFormat) {
							distance = ((Section::Fat *) section)->DataSize;
							isFat = true;
						} else {
							distance = ((Section::Small *) section)->DataSize;
							isFat = false;
						}

						switch (*section & (uint8_t) Section::Flags::KindMask) {
							case Section::Flags::EHTable:
								uint8_t *end = distance + (uint8_t *) section;
								if (isFat) {
									Section::Exception::FatHandler *sect = (Section::Exception::FatHandler *) section;
									for (Section::Exception::FatHandlerClause *clause = sect->Clauses; (uint8_t *) clause < end; clause++)
										m_Exceptions.push_back(Exception(offset, clause));
								} else {
									Section::Exception::SmallHandler *sect = (Section::Exception::SmallHandler *) section;
									for (Section::Exception::SmallHandlerClause *clause = sect->Clauses; (uint8_t *) clause < end; clause++)
										m_Exceptions.push_back(Exception(offset, clause));
								}
							break;
						}
					} while (*section & (uint8_t) Section::Flags::MoreSects);
				}
			} break;
			default:
				std::cerr << "Unsupported method format." << std::endl ;
			break;
		}

        data.assign(reinterpret_cast<char *>(offset), length);
	}
}

uint8_t *ILMethodImpl::getOffset() const {
	return offset;
}

uint32_t ILMethodImpl::getLength() const {
	return length;
}

StandAlone *ILMethodImpl::getLocals() const {
	return locals;
}

menes::bytebuf ILMethodImpl::getData() const {
    return data;
}

const ILMethodImpl::ExceptionVector &ILMethodImpl::getExceptions() const {
	return m_Exceptions;
}

}