Throwable.java

Переключить прокрутку окна
Загрузить этот исходный код

/*
	Реализация спецификаций CLDC версии 1.1 (JSR-139), MIDP версии 2.1 (JSR-118)
	и других спецификаций для функционирования компактных приложений на языке
	Java (мидлетов) в среде программного обеспечения Малик Эмулятор.

	Copyright © 2016, 2019 Малик Разработчик

	Это свободная программа: вы можете перераспространять ее и/или изменять
	ее на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
	в каком она была опубликована Фондом свободного программного обеспечения;
	либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.

	Эта программа распространяется в надежде, что она будет полезной,
	но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
	или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
	общественной лицензии GNU.

	Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
	вместе с этой программой. Если это не так, см.
	<https://www.gnu.org/licenses/>.
*/


package java.lang;

import java.io.*;
import malik.emulator.io.cloud.*;
import malik.emulator.util.*;

public class Throwable extends Object
{
	private static Thread DISABLE_STACK_TRACE_FOR;

	static
	{
		MalikInterrupt.register(0x00, new ArithmeticExceptionHandler());
		MalikInterrupt.register(0x01, new NullPointerExceptionHandler());
		MalikInterrupt.register(0x02, new MemoryFaultExceptionHandler());
		MalikInterrupt.register(0x03, new ExecutionExceptionHandler());
		MalikInterrupt.register(0x04, new OperandExceptionHandler());
		MalikInterrupt.register(0x05, new StackFaultExceptionHandler());
		MalikInterrupt.register(0x06, new StackOverflowExceptionHandler());
		MalikInterrupt.register(0x07, new UnknownOperationExceptionHandler());
		MalikInterrupt.register(0x08, new ArrayIndexOutOfBoundsExceptionHandler());
		MalikInterrupt.register(0x09, new UnhandledExceptionHandler());
		MalikInterrupt.register(0x20, new ThrowStatementInterruptHandler());
		MalikInterrupt.register(0x21, new IsEnabledInterruptHandler());
		MalikInterrupt.register(0x22, new SetEnabledInterruptHandler());
	}

	static void disableStackTrace()
	{
		DISABLE_STACK_TRACE_FOR = Thread.currentThread();
	}

	static void enableStackTrace()
	{
		DISABLE_STACK_TRACE_FOR = null;
	}


	/* Нельзя менять порядок полей здесь, можно добавлять новые поля в конец списка. */
	private int address;
	private final Object monitor;

	/* Новые поля можно добавлять только после этого комментария. */
	private final int ignore;
	private final StackTraceElement[] trace;
	private final String message;

	public Throwable()
	{
		this(null, false);
	}

	public Throwable(String message)
	{
		this(message, false);
	}

	Throwable(String message, boolean implicit)
	{
		int ignore;
		StackTraceElement[] trace;
		Class errorType;
		Class thisType;
		Thread current;
		if((thisType = getClass()) != (errorType = MalikSystem.getClassInstance("Ljava/lang/Error;")) && !thisType.isInheritedFrom(errorType) && ThrowableStackTrace.enabled() && (current = Thread.currentThread()) != DISABLE_STACK_TRACE_FOR)
		{
			ignore = getIgnore(trace = StackTracer.trace(current), implicit);
		} else
		{
			ignore = 0;
			trace = null;
		}
		this.monitor = new Object();
		this.ignore = ignore;
		this.trace = trace;
		this.message = message;
	}

	public String toString()
	{
		String msg = getMessage();
		String cls = getClass().getName();
		return msg == null ? cls : (new StringBuilder()).append(cls).append(": ").append(msg).toString();
	}

	public void printStackTrace()
	{
		System.err.print(stackTraceToString());
	}

	public String getMessage()
	{
		return message;
	}

	public final void printRealStackTrace()
	{
		System.err.print(stackTraceToString());
	}

	public final String getRealMessage()
	{
		return message;
	}

	final int dummyThrowable()
	{
		return address ^ monitor.hashCode();
	}

	private int getIgnore(StackTraceElement[] trace, boolean implicit)
	{
		int result = 0;
		int limit = trace.length - 1;
		Class currType = getClass();
		Class thisType = MalikSystem.getClassInstance("Ljava/lang/Throwable;");
		do
		{
			for(StackTraceElement element1, element2; result < limit && (element1 = trace[result]) != null && (element2 = trace[result + 1]) != null && element1.getClassName().equals(element2.getClassName()) && "<init>".equals(element2.getMethodName()); result++);
			if(currType == thisType || result == limit) break;
			result++;
		} while((currType = currType.superType) != null);
		for(int i = implicit && result < limit ? (result += 2) : (++result); i-- > 0; trace[i] = null);
		return result;
	}

	private String stackTraceToString()
	{
		int thisIgnore = ignore;
		StackTraceElement[] thisTrace = trace;
		StringBuilder result = (new StringBuilder()).append(getClass().getName());
		String newline;
		String msg;
		if((msg = message) != null)
		{
			result.append(": ").append(msg);
		}
		if((newline = System.getProperty("line.separator")) == null)
		{
			newline = "\n";
		}
		result.append(newline);
		if(thisTrace != null)
		{
			int len = thisTrace.length;
			for(int i = thisIgnore; i < len; i++)
			{
				StackTraceElement element;
				if((element = thisTrace[i]) != null)
				{
					result.append("    в ").append(element.toString()).append(newline);
				}
			}
		}
		return result.toString();
	}
}

final class StackTracer extends Object
{
	private static final int EIP = 0x0000;
	private static final int ESP = 0x0100;

	private static boolean INITIALIZED;
	private static int ELEMENTS_COUNT;
	private static long ELEMENTS_OFFSET;
	private static long[] STRINGS_OFFSETS;
	private static String[] STRINGS;
	private static DataInputStream DATA_STREAM;
	private static FileInputStream FILE_STREAM;
	private static final Object MONITOR;

	static
	{
		INITIALIZED = false;
		MONITOR = new Object();
	}

	public static StackTraceElement[] trace(Thread thread)
	{
		StackTraceElement[] result;
		if(thread == null)
		{
			throw new NullPointerException("StackTracer.trace: аргумент thread равен нулевой ссылке.");
		}
		synchronized(MONITOR)
		{
			try
			{
				Throwable.disableStackTrace();
				try
				{
					if(!INITIALIZED)
					{
						initialize();
					}
					thread.enterStackTrace();
					try
					{
						boolean anotherThread = true;
						int index = 0;
						int len = 0;
						int id = thread.getID() & 0xff;
						int a = (int) MalikSystem.syscall((long) (ESP | id), 0x0007);
						int b = thread.getStackBottom();
						if(thread == Thread.currentThread())
						{
							anotherThread = false;
							len--;
							a += 0x10;
						}
						for(int address = a; address < b; address += 0x10)
						{
							int stackItemKind;
							if((stackItemKind = MalikSystem.getIntAt(address + 0x0c)) == MalikSystem.STACKITEM_RETURN || stackItemKind == MalikSystem.STACKITEM_IRETURN)
							{
								len++;
							}
						}
						result = new StackTraceElement[len];
						if(anotherThread && index < len)
						{
							result[index++] = getElement((int) MalikSystem.syscall((long) (EIP | id), 0x0007));
						}
						for(int address = a; address < b; address += 0x10)
						{
							int stackItemKind;
							if((stackItemKind = MalikSystem.getIntAt(address + 0x0c)) == MalikSystem.STACKITEM_RETURN || stackItemKind == MalikSystem.STACKITEM_IRETURN)
							{
								result[index++] = getElement(MalikSystem.getIntAt(address) - 1);
								if(index >= len) break;
							}
						}
					}
					finally
					{
						thread.leaveStackTrace();
					}
				}
				finally
				{
					Throwable.enableStackTrace();
				}
			}
			catch(IOException e)
			{
				result = null;
			}
		}
		return result;
	}

	private static void initialize() throws IOException
	{
		int i;
		int len;
		long offset;
		FILE_STREAM = new FileInputStream(System.getMalikAppProperty("Programme-Executable").concat(".bindbg"));
		DATA_STREAM = new DataInputStream(FILE_STREAM);
		FILE_STREAM.mark(Integer.MAX_VALUE);
		STRINGS = new String[len = DATA_STREAM.readInt()];
		STRINGS_OFFSETS = new long[len];
		for(offset = 4L, i = 0; i < len; i++)
		{
			STRINGS_OFFSETS[i] = offset;
			offset += FILE_STREAM.skip(DATA_STREAM.readUnsignedShort()) + 2L;
		}
		ELEMENTS_OFFSET = offset;
		ELEMENTS_COUNT = FILE_STREAM.available() / 18;
		INITIALIZED = true;
	}

	private static void seek(long offset) throws IOException
	{
		FILE_STREAM.reset();
		FILE_STREAM.skip(offset);
	}

	private static int getElementAddress(int index) throws IOException
	{
		if(index < 0) return Integer.MIN_VALUE;
		if(index >= ELEMENTS_COUNT) return Integer.MAX_VALUE;
		seek(ELEMENTS_OFFSET + (long) index * 18L);
		return DATA_STREAM.readInt();
	}

	private static String getStringAt(int index) throws IOException
	{
		String result;
		if(index < 0 || index >= STRINGS.length) return "";
		if((result = STRINGS[index]) == null)
		{
			seek(STRINGS_OFFSETS[index]);
			result = STRINGS[index] = DATA_STREAM.readUTF();
		}
		return result;
	}

	private static StackTraceElement getElement(int address) throws IOException
	{
		int limit1 = 0;
		int limitGuess = ELEMENTS_COUNT >> 1;
		int limit2 = ELEMENTS_COUNT;
		int address1;
		int addressGuess;
		int address2;
		int lineNumber;
		int classNameIndex;
		int methodNameIndex;
		int sourceNameIndex;
		for(; ; )
		{
			address1 = getElementAddress(limit1);
			address2 = getElementAddress(limit2);
			addressGuess = getElementAddress(limitGuess);
			if(limitGuess - limit1 <= 1 && limit2 - limitGuess <= 1)
			{
				break;
			}
			if(address >= address1 && address < addressGuess)
			{
				limit2 = limitGuess;
				limitGuess = (limit1 + limit2) >> 1;
				continue;
			}
			if(address >= addressGuess && address < address2)
			{
				limit1 = limitGuess;
				limitGuess = (limit1 + limit2) >> 1;
				continue;
			}
			return null;
		}
		if(address >= addressGuess)
		{
			if(limitGuess < 0 || limitGuess >= ELEMENTS_COUNT)
			{
				return null;
			}
			seek(ELEMENTS_OFFSET + (long) limitGuess * 18L + 4L);
		}
		else if(address >= address1)
		{
			if(limit1 < 0 || limit1 >= ELEMENTS_COUNT)
			{
				return null;
			}
			seek(ELEMENTS_OFFSET + (long) limit1 * 18L + 4L);
		}
		else
		{
			return null;
		}
		classNameIndex = DATA_STREAM.readInt();
		methodNameIndex = DATA_STREAM.readInt();
		sourceNameIndex = DATA_STREAM.readInt();
		lineNumber = DATA_STREAM.readUnsignedShort();
		return new StackTraceElement(getStringAt(classNameIndex), getStringAt(methodNameIndex), getStringAt(sourceNameIndex), lineNumber);
	}


	private StackTracer()
	{
	}
}

abstract class InterruptHandler extends Object implements Interrupt
{
	protected InterruptHandler()
	{
	}
}

abstract class ExceptionHandler extends InterruptHandler
{
	protected ExceptionHandler()
	{
	}
}

final class ArithmeticExceptionHandler extends ExceptionHandler implements InterruptVoid
{
	public ArithmeticExceptionHandler()
	{
	}

	public void interrupt() throws Throwable
	{
		Exception exception = new ArithmeticException("Ошибка деления целого числа на ноль.", true);
		MalikSystem.runExcept(MalikSystem.getReturnAddress(), exception);
	}
}

final class NullPointerExceptionHandler extends ExceptionHandler implements InterruptVoid
{
	public NullPointerExceptionHandler()
	{
	}

	public void interrupt() throws Throwable
	{
		Exception exception = new NullPointerException("Нулевая ссылка не может быть разыменована.", true);
		MalikSystem.runExcept(MalikSystem.getReturnAddress(), exception);
	}
}

final class MemoryFaultExceptionHandler extends ExceptionHandler implements InterruptVoid
{
	public MemoryFaultExceptionHandler()
	{
	}

	public void interrupt() throws Throwable
	{
		Exception exception = new NullPointerException("Доступ к некоторой области памяти запрещён.", true);
		MalikSystem.runExcept(MalikSystem.getReturnAddress(), exception);
	}
}

final class ExecutionExceptionHandler extends ExceptionHandler implements InterruptVoid
{
	public ExecutionExceptionHandler()
	{
	}

	public void interrupt() throws Throwable
	{
		Error error = new VerifyError("Попытка выполнить код в недоступной области памяти.");
		MalikSystem.runExcept(MalikSystem.getReturnAddress(), error);
	}
}

final class OperandExceptionHandler extends ExceptionHandler implements InterruptVoid
{
	public OperandExceptionHandler()
	{
	}

	public void interrupt() throws Throwable
	{
		Error error = new VerifyError("Команда не может обработать данные другого типа.");
		MalikSystem.runExcept(MalikSystem.getReturnAddress(), error);
	}
}

final class StackFaultExceptionHandler extends ExceptionHandler implements InterruptVoid
{
	public StackFaultExceptionHandler()
	{
	}

	public void interrupt() throws Throwable
	{
		Error error = new VerifyError("Попытка доступа за пределы стака.");
		MalikSystem.runExcept(MalikSystem.getReturnAddress(), error);
	}
}

final class StackOverflowExceptionHandler extends ExceptionHandler implements InterruptVoid
{
	public StackOverflowExceptionHandler()
	{
	}

	public void interrupt() throws Throwable
	{
		Error error = new StackOverflowError("Стак вызовов переполнен.");
		MalikSystem.runExcept(MalikSystem.getReturnAddress(), error);
	}
}

final class UnknownOperationExceptionHandler extends ExceptionHandler implements InterruptVoid
{
	public UnknownOperationExceptionHandler()
	{
	}

	public void interrupt() throws Throwable
	{
		Error error = new VerifyError("Неизвестная операция.");
		MalikSystem.runExcept(MalikSystem.getReturnAddress(), error);
	}
}

final class ArrayIndexOutOfBoundsExceptionHandler extends ExceptionHandler implements InterruptInt
{
	public ArrayIndexOutOfBoundsExceptionHandler()
	{
	}

	public void interrupt(int faultedIndex) throws Throwable
	{
		Exception exception = new ArrayIndexOutOfBoundsException(Integer.toString(faultedIndex), true);
		MalikSystem.runExcept(MalikSystem.getReturnAddress(), exception);
	}
}

final class UnhandledExceptionHandler extends ExceptionHandler implements InterruptObject
{
	public UnhandledExceptionHandler()
	{
	}

	public void interrupt(Object unhandledThrowable) throws Throwable
	{
		/* НЕВОЗМОЖНО: исключения всё равно будут где-нибудь обрабатываться. */
	}
}

final class ThrowStatementInterruptHandler extends InterruptHandler implements InterruptObject
{
	public ThrowStatementInterruptHandler()
	{
	}

	public void interrupt(Object throwingObject) throws Throwable
	{
		Throwable throwable;
		if(throwingObject == null)
		{
			throwable = new NullPointerException("Команда throw: throw null заменено на throw new NullPointerException(…).", true);
		}
		else if(!(throwingObject instanceof Throwable))
		{
			throwable = new VerifyError("Объект исключения может быть только типа Throwable.");
		}
		else
		{
			throwable = (Throwable) throwingObject;
		}
		MalikSystem.runExcept(MalikSystem.getReturnAddress() - 1, throwable);
	}
}

final class IsEnabledInterruptHandler extends InterruptHandler implements InterruptInt
{
	public IsEnabledInterruptHandler()
	{
	}

	public void interrupt(int resultAddress)
	{
		MalikSystem.setByteAt(resultAddress, (byte) (MalikSystem.getByteAt(MalikSystem.getLocalVariableAddress(this) - 0x18) & 0x01));
	}
}

final class SetEnabledInterruptHandler extends InterruptHandler implements InterruptInt
{
	public SetEnabledInterruptHandler()
	{
	}

	public void interrupt(int enabled)
	{
		MalikSystem.setIntAt(MalikSystem.getLocalVariableAddress(this) - 0x18, enabled);
	}
}