/*
Реализация спецификаций CLDC версии 1.1 (JSR-139), MIDP версии 2.1 (JSR-118)
и других спецификаций для функционирования компактных приложений на языке
Java (мидлетов) в среде программного обеспечения Малик Эмулятор.
Copyright © 2016–2017, 2019–2023 Малик Разработчик
Это свободная программа: вы можете перераспространять ее и/или изменять
ее на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
в каком она была опубликована Фондом свободного программного обеспечения;
либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.
Эта программа распространяется в надежде, что она будет полезной,
но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
общественной лицензии GNU.
Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
вместе с этой программой. Если это не так, см.
<https://www.gnu.org/licenses/>.
*/
package java.lang;
import java.io.*;
import malik.emulator.application.*;
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(Run.instance.getAppProperty("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);
}
}