/*
Реализация среды исполнения языка программирования
Объектно-ориентированный продвинутый векторный транслятор
Copyright © 2021, 2024 Малик Разработчик
Это свободная программа: вы можете перераспространять ее и/или изменять
ее на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
в каком она была опубликована Фондом свободного программного обеспечения;
либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.
Эта программа распространяется в надежде, что она будет полезной,
но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
общественной лицензии GNU.
Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
вместе с этой программой. Если это не так, см.
<https://www.gnu.org/licenses/>.
*/
package avt.lang;
import platform.dependent.*;
/** Класс <code>Object</code> является корнем всей иерархии типов программы.
* Каждый класс имеет <code>Object</code> в качестве суперкласса.
* Все объекты, включая массивы и структуры, реализуют методы этого класса.
*
* С каждым объектом связан монитор — объект синхронизации в многопоточном приложении.
* Этот монитор уникален для каждого объекта.
* @see Class
* @see Thread
*/
public class Object
{
private long fldRefCountFromOthers;
private long fldRefCountFromObjects;
private long fldClassOffset;
private Monitor fldMonitor;
/** Создаёт новый объект.
* Этот конструктор явно или неявно вызывается из всех других конструкторов.
* Исключением являются массивы: для них не вызывается какой-либо конструктор, поскольку массивы в ПВТ-ОО имеют иной механизм инициализации.
* @see #afterConstruction()
* @see #beforeDestruction()
*/
public () { }
/** Показывает, когда некоторый другой объект «равен» этому.
* Этот метод реализует отношение равенства:
* <ul><li>
* Оно <em>рефлексивно</em>: для любой ссылки <code>x</code> <code>x.equals(x)</code> должно возвращать <code>true</code>.
* </li><li>
* Оно <em>симметрично</em>: для любых ссылок <code>x</code> и <code>y</code> <code>x.equals(y) == y.equals(x)</code>.
* </li><li>
* Оно <em>транзитивно</em>: для любых ссылок <code>x</code>, <code>y</code> и <code>z</code> если <code>x.equals(y)</code>
* и <code>y.equals(z)</code> возвращают <code>true</code>, то и <code>x.equals(z)</code> так же должно возвращать <code>true</code>.
* </li><li>
* Оно <em>консистентно</em>: если содержимое ссылок <code>x</code> и <code>y</code> не изменяется, то и возвращаемое значение <code>x.equals(y)</code>
* не изменяется даже при повторяющихся вызовах.
* </li><li>
* Для любой ссылки <code>x</code> <code>x.equals(null)</code> должно возвращать <code>false</code>.
* </li></ul>
* Использование этого метода может заменить собой операцию сравнения двух объектных ссылок, поскольку если <code>(x == y)</code>, то и <code>(x.equals(y) == true)</code>.
*
* Этот метод нужно так же переопределять, если переопределён метод <code>{@link #hashCodeAsLong() hashCodeAsLong()}</code>.
*
* Реализация данного метода в классе <code>Object</code> возвращает результат сравнения двух ссылок: <code>anot == this</code>.
* @param anot объект, с которым производится «сравнение» этого объекта.
* @return <code>true</code>, если объекты «равны», <code>false</code> — во всех остальных случаях.
* @see #hashCodeAsLong()
*/
public boolean equals(Object anot) { return anot == this; }
/** Возвращает контрольную сумму в виде значения типа <code>int</code> для этого объекта.
* Подклассы могут переопределить этот метод в одном из следующих случаев:
* <ul><li>
* если используется свой алгоритм вычисления контрольной суммы объекта;
* </li><li>
* если есть возможность более быстрого вычисления контрольной суммы объекта.
* </li></ul>
* Этот метод можно переопределить только в том случае, если переопределены методы <code>{@link #hashCodeAsLong() hashCodeAsLong()}</code> и <code>{@link #equals(Object) equals(Object)}</code>.
* При переопределении и реализации этого метода следует руководствоваться тем же, что и при реализации метода <code>hashCodeAsLong()</code>.
*
* Реализация данного метода в классе <code>Object</code> такова:
* <pre>
* <b>long</b> hash = hashCodeAsLong();
* <b>return</b> (<b>int</b>) (hash ^ (hash >> 32));
* </pre>
* @return контрольную сумму типа <code>int</code>.
* @see #equals(Object)
* @see #hashCodeAsLong()
*/
public int hashCode() {
long hash = hashCodeAsLong();
return (int) (hash ^ (hash >> 32));
}
/** Возвращает контрольную сумму в виде значения типа <code>long</code> для этого объекта.
* Такие классы как <code>{@link avt.util.Hashtable Hashtable}</code> и <code>{@link avt.util.Vector Vector}</code> из пакета <code>{@link avt.util avt.util}</code> используют этот метод.
*
* При переопределении и реализации этого метода следует руководствоваться следующим:
* <ul><li>
* При каждом вызове этого метода должно возвращаться одно и то же значение, даже если данные объекта были модифицированы.
* Соответственно, при реализации объектов, поддерживающих модифицирование своих данных, данный метод не должен пользоваться данными, которые использует метод <code>equals</code>.
* </li><li>
* Если два объекта «равны» согласно реализации метода <code>equals</code>, то и контрольные суммы этих объектов так же должны быть равны.
* Данное требование не распространяется на объекты, данные которых были модифицированы.
* </li><li>
* Если два объекта «не равны» согласно реализации метода <code>equals</code>, то их контрольные суммы не обязаны быть неравными.
* </li></ul>
* Этот метод нужно так же переопределять, если переопределён метод <code>{@link #equals(Object) equals(Object)}</code>.
*
* Реализация данного метода в классе <code>Object</code> возвращает уникальное ненулевое значение, связанное с этим объектом.
* @return контрольную сумму типа <code>long</code>.
* @see #equals(Object)
*/
public long hashCodeAsLong() { return System.identityHashCode(this); }
/** Возвращает контрольную сумму в виде значения типа <code>long2</code> для этого объекта.
* Подклассы могут переопределить этот метод в одном из следующих случаев:
* <ul><li>
* если используется свой алгоритм вычисления контрольной суммы объекта;
* </li><li>
* если есть возможность получения контрольной суммы типа <code>long2</code>;
* </li><li>
* если есть возможность более быстрого вычисления контрольной суммы объекта.
* </li></ul>
* Этот метод можно переопределить только в том случае, если переопределены методы
* <code>{@link #hashCodeAsLong() hashCodeAsLong()}</code> и
* <code>{@link #equals(Object) equals(Object)}</code>.
* При переопределении и реализации этого метода следует руководствоваться тем же, что и при реализации метода <code>hashCodeAsLong()</code>.
*
* Реализация данного метода в классе <code>Object</code> вызывает метод <code>hashCodeAsLong()</code> и приводит его результат к типу <code>long2</code>.
* @return контрольную сумму типа <code>long2</code>.
* @see #equals(Object)
* @see #hashCodeAsLong()
*/
public long2 hashCodeAsLong2() { return (long2) hashCodeAsLong(); }
/** Возвращает контрольную сумму в виде значения типа <code>long4</code> для этого объекта.
* Подклассы могут переопределить этот метод в одном из следующих случаев:
* <ul><li>
* если используется свой алгоритм вычисления контрольной суммы объекта;
* </li><li>
* если есть возможность получения контрольной суммы типа <code>long4</code>;
* </li><li>
* если есть возможность более быстрого вычисления контрольной суммы объекта.
* </li></ul>
* Этот метод можно переопределить только в том случае, если переопределены методы
* <code>{@link #hashCodeAsLong() hashCodeAsLong()}</code>,
* <code>{@link #hashCodeAsLong2() hashCodeAsLong2()}</code> и
* <code>{@link #equals(Object) equals(Object)}</code>.
* При переопределении и реализации этого метода следует руководствоваться тем же, что и при реализации метода <code>hashCodeAsLong()</code>.
*
* Реализация данного метода в классе <code>Object</code> вызывает метод <code>hashCodeAsLong2()</code> и приводит его результат к типу <code>long4</code>.
* @return контрольную сумму типа <code>long4</code>.
* @see #equals(Object)
* @see #hashCodeAsLong()
* @see #hashCodeAsLong2()
*/
public long4 hashCodeAsLong4() { return (long4) hashCodeAsLong2(); }
/** Возвращает контрольную сумму в виде значения типа <code>long8</code> для этого объекта.
* Подклассы могут переопределить этот метод в одном из следующих случаев:
* <ul><li>
* если используется свой алгоритм вычисления контрольной суммы объекта;
* </li><li>
* если есть возможность получения контрольной суммы типа <code>long8</code>;
* </li><li>
* если есть возможность более быстрого вычисления контрольной суммы объекта.
* </li></ul>
* Этот метод можно переопределить только в том случае, если переопределены методы
* <code>{@link #hashCodeAsLong() hashCodeAsLong()}</code>,
* <code>{@link #hashCodeAsLong2() hashCodeAsLong2()}</code>,
* <code>{@link #hashCodeAsLong4() hashCodeAsLong4()}</code> и
* <code>{@link #equals(Object) equals(Object)}</code>.
* При переопределении и реализации этого метода следует руководствоваться тем же, что и при реализации метода <code>hashCodeAsLong()</code>.
*
* Реализация данного метода в классе <code>Object</code> вызывает метод <code>hashCodeAsLong4()</code> и приводит его результат к типу <code>long8</code>.
* @return контрольную сумму типа <code>long8</code>.
* @see #equals(Object)
* @see #hashCodeAsLong()
* @see #hashCodeAsLong2()
* @see #hashCodeAsLong4()
*/
public long8 hashCodeAsLong8() { return (long8) hashCodeAsLong4(); }
/** Возвращает текстовое представление объекта.
* Иными словами, этот метод возвращает строку, которая «текстовым образом представляет» этот объект.
* Результат не обязательно должен быть подробным, но должен быть информативным для читателя.
* Этот метод рекомендуется переопределять всем подклассам.
*
* Реализация данного метода в классе <code>Object</code> возвращает конкатенацию трёх элементов: канонического имени класса объекта,
* символа «@» и шестнадцатеричного представления беззнакового значения контрольной суммы типа <code>long</code> для этого объекта.
* Другими словами, возвращается значение, эквивалентное следующему:
*
* <code>getClass().canonicalName + '@' + Long.toHexString(hashCodeAsLong())</code>
* @return текстовое представление объекта.
*/
public String toString() { return (new StringBuilder() + getClass().canonicalName + '@' + Long.toHexString(hashCodeAsLong())).toString(); }
/** Пробуждает один поток исполнения, ожидающий на мониторе этого объекта.
* Если какие-либо потоки исполнения ожидают на мониторе, то один из них выбирается для пробуждения.
* Какой из них будет выбран — зависит от реализации.
* Поток исполнения ожидает на мониторе при вызове одного из методов <code>wait</code>.
*
* Пробужденный поток исполнения не сразу получает управление, а лишь после того, как текущий поток исполнения перестанет быть владельцем монитора, связанным с этим объектом.
* Этот метод может быть вызван только если текущий поток исполнения является владельцем монитора этого объекта.
* Поток исполнения становится владельцем монитора в одном из следующих случаев:
* <ul><li>
* Выполняет тело управляющей конструкции <code>synchronized</code> или <code>synchronized with</code>, которая синхронизирует выполнение кода на мониторе этого объекта;
* </li><li>
* Выполняет тело синхронизированного инстанционного метода этого объекта;
* </li><li>
* Для объектов типа <code>Class</code>: выполняет тело синхронизированного статичного метода этого класса.
* </li></ul>
* Только один поток исполнения одновременно может владеть монитором объекта.
* @throws IllegalMonitorStateException если текущий поток исполнения не владеет монитором этого объекта.
* @see #notify(Thread)
* @see #notifyAll()
* @see #wait()
*/
public final void notify() {
Waitable monitor = fldMonitor;
if(monitor == null)
{
throw new IllegalMonitorStateException(package.getResourceString("illegal-state.monitor"));
}
monitor.notify(false, 0L);
}
/** Пробуждает заданный поток исполнения, ожидающий на мониторе этого объекта.
* Если какие-либо потоки исполнения ожидают на мониторе, то один из них выбирается для пробуждения,
* однако предпочтение отдаётся тому потоку, который был передан в качестве аргумента этому методу.
* Другими словами, если заданный в качестве аргумента поток исполнения ожидает на мониторе, то будет пробужден именно он.
* Иначе — любой другой ожидающий поток исполнения.
* Поток исполнения ожидает на мониторе при вызове одного из методов <code>wait</code>.
*
* Пробужденный поток исполнения не сразу получает управление, а лишь после того, как текущий поток исполнения перестанет быть владельцем монитора, связанным с этим объектом.
* Этот метод может быть вызван только если текущий поток исполнения является владельцем монитора этого объекта.
* О том, как поток исполнения становится владельцем монитора, можно узнать в описании метода <code>notify()</code>.
* @param notifying поток исполнения, который следует пробудить в первую очередь.
* @throws IllegalMonitorStateException если текущий поток исполнения не владеет монитором этого объекта.
* @see #notify()
* @see #notifyAll()
* @see #wait()
*/
public final void notify(Thread notifying) {
Waitable monitor = fldMonitor;
if(monitor == null)
{
throw new IllegalMonitorStateException(package.getResourceString("illegal-state.monitor"));
}
monitor.notify(false, notifying == null ? 0L : notifying.threadId);
}
/** Пробуждает все потоки исполнения, ожидающие на мониторе этого объекта.
* Поток исполнения ожидает на мониторе при вызове одного из методов <code>wait</code>.
*
* Пробужденные потоки исполнения не сразу получают управление, а лишь после того, как текущий поток исполнения перестанет быть владельцем монитора, связанным с этим объектом,
* причём управление получит только один из этих потоков до тех пор, пока он не перестанет быть владельцем монитора,
* после чего управление получит другой поток до тех пор, пока он не перестанет быть владельцем монитора и т. д.
* Этот метод может быть вызван только если текущий поток исполнения является владельцем монитора этого объекта.
* О том, как поток исполнения становится владельцем монитора, можно узнать в описании метода <code>notify()</code>.
* @throws IllegalMonitorStateException если текущий поток исполнения не владеет монитором этого объекта.
* @see #notify()
* @see #notify(Thread)
* @see #wait()
*/
public final void notifyAll() {
Waitable monitor = fldMonitor;
if(monitor == null)
{
throw new IllegalMonitorStateException(package.getResourceString("illegal-state.monitor"));
}
monitor.notify(true, 0L);
}
/** Заставляет текущий поток исполнения ждать на мониторе этого объекта до тех пор, пока другой поток не вызовет метод <code>notify()</code> или <code>notifyAll()</code> для этого объекта.
* Другими словами, этот метод ведёт себя подобно вызову <code>wait(0L)</code> или <code>wait(0L, 0)</code>.
*
* Текущий поток исполнения должен владеть монитором этого объекта.
* Поток перестаёт быть владельцем монитора, уходит в спячку, а после пробуждения — снова становится владельцем этого монитора, после чего метод <code>wait</code> возвращает управление.
* О том, как поток исполнения становится владельцем монитора, можно узнать в описании метода <code>notify()</code>.
* @throws IllegalMonitorStateException если текущий поток исполнения не владеет монитором этого объекта.
* @throws InterruptedException если текущий поток исполнения был прерван. <em>Статус прерывания</em> сбрасывается после того,
* как текущий поток исполнения станет владельцем монитора этого объекта, и перед тем, как это исключение будет возбуждено.
* @see #notify()
* @see #wait(long)
* @see #wait(long, int)
*/
public final void wait() throws InterruptedException {
Waitable monitor = fldMonitor;
if(monitor == null)
{
throw new IllegalMonitorStateException(package.getResourceString("illegal-state.monitor"));
}
monitor.wait(0i, Thread.current().interruptioHandler);
}
/** Заставляет текущий поток исполнения ждать на мониторе этого объекта до тех пор, пока другой поток не вызовет метод <code>notify()</code> или <code>notifyAll()</code> для этого объекта,
* но не дольше заданного времени.
* Если заданное время ожидания равно нулю, то ожидание будет длиться до тех пор, пока другой поток не вызовет метод <code>notify()</code> или <code>notifyAll()</code> для этого объекта.
*
* Текущий поток исполнения должен владеть монитором этого объекта.
* Поток перестаёт быть владельцем монитора, уходит в спячку, а после пробуждения — снова становится владельцем этого монитора, после чего метод <code>wait</code> возвращает управление.
* О том, как поток исполнения становится владельцем монитора, можно узнать в описании метода <code>notify()</code>.
* @param timeInMillis время ожидания в миллисекундах. Если этот аргумент равен нулю, то он игнорируется.
* @throws IllegalArgumentException если <code>timeInMillis</code> отрицательный.
* @throws IllegalMonitorStateException если текущий поток исполнения не владеет монитором этого объекта.
* @throws InterruptedException если текущий поток исполнения был прерван. <em>Статус прерывания</em> сбрасывается после того,
* как текущий поток исполнения станет владельцем монитора этого объекта, и перед тем, как это исключение будет возбуждено.
* @see #notify()
* @see #wait()
* @see #wait(long, int)
*/
public final void wait(long timeInMillis) throws InterruptedException {
if(timeInMillis < 0)
{
throw new IllegalArgumentException(String.format(package.getResourceString("illegal-argument.negative"), new Object[] { "timeInMillis" }));
}
Waitable monitor = fldMonitor;
if(monitor == null)
{
throw new IllegalMonitorStateException(package.getResourceString("illegal-state.monitor"));
}
monitor.wait(timeInMillis, Thread.current().interruptioHandler);
}
/** Заставляет текущий поток исполнения ждать на мониторе этого объекта до тех пор, пока другой поток не вызовет метод <code>notify()</code> или <code>notifyAll()</code> для этого объекта,
* но не дольше заданного времени.
* Если заданное время ожидания равно нулю, то ожидание будет длиться до тех пор, пока другой поток не вызовет метод <code>notify()</code> или <code>notifyAll()</code> для этого объекта.
*
* Данный метод позволяет задать время ожидания с точностью до наносекунды.
* Время ожидания имеет величину (1000000 × <code>timeInMillis</code> + <code>timeInNanos</code>) наносекунд.
* Однако далеко не все операционные системы и аппаратные возможности позволяют измерять время с точностью до наносекунды.
* Поэтому реальное время ожидания может быть округлено до миллисекунд.
*
* Текущий поток исполнения должен владеть монитором этого объекта.
* Поток перестаёт быть владельцем монитора, уходит в спячку, а после пробуждения — снова становится владельцем этого монитора, после чего метод <code>wait</code> возвращает управление.
* О том, как поток исполнения становится владельцем монитора, можно узнать в описании метода <code>notify()</code>.
* @param timeInMillis время ожидания в миллисекундах.
* @param timeInNanos дополнительное время ожидания в наносекундах. Если и <code>timeInMillis</code>, и <code>timeInNanos</code> оба равны нулю, то оба этих аргумента игнорируются.
* @throws IllegalArgumentException если <code>timeInMillis</code> отрицательный.
* @throws IllegalArgumentException если <code>timeInNanos</code> отрицательный или больше 999999.
* @throws IllegalMonitorStateException если текущий поток исполнения не владеет монитором этого объекта.
* @throws InterruptedException если текущий поток исполнения был прерван. <em>Статус прерывания</em> сбрасывается после того,
* как текущий поток исполнения станет владельцем монитора этого объекта, и перед тем, как это исключение будет возбуждено.
* @see #notify()
* @see #wait()
* @see #wait(long)
*/
public final void wait(long timeInMillis, int timeInNanos) throws InterruptedException {
if(timeInMillis < 0)
{
throw new IllegalArgumentException(String.format(package.getResourceString("illegal-argument.negative"), new Object[] { "timeInMillis" }));
}
if(timeInNanos < 0 || timeInNanos > 999999)
{
throw new IllegalArgumentException(String.format(package.getResourceString("illegal-argument.out-of-range"), new Object[] { "timeInNanos" }));
}
Waitable monitor = fldMonitor;
if(monitor == null)
{
throw new IllegalMonitorStateException(package.getResourceString("illegal-state.monitor"));
}
monitor.wait(timeInMillis, timeInNanos, Thread.current().interruptioHandler);
}
/** Возвращает настоящий класс этого объекта.
* Этот объект типа <code>Class</code> используется в качестве монитора в статичных синхронизированных (<code>static synchronized</code>) методах этого класса.
* @return объект типа <code>Class</code>, представляющий настоящий класс этого объекта.
*/
public final native Class getClass();
/** Автоматически вызывается сразу после того, как конструктор вернул управление.
* Компилятор ПВТ-ОО вставляет инструкцию вызова этого метода после каждой инструкции вызова конструктора в операторах <code>new</code>.
* Допустим, что написана строка кода:
*
* <code>Vector v = <b>new</b> Vector(15);</code>
*
* Компилятор ПВТ-ОО сгенерирует для неё следующие инструкции:
* <ol><li>
* Создать пустую инстанцию класса <code>Vector</code>.
* </li><li>
* Передать <code>int</code> 15 конструктору в качестве аргумента.
* </li><li>
* Вызвать конструктор <code>Vector(int)</code> для созданной пустой инстанции. После возврата из конструктора инстанция считается проинициализированной.
* </li><li>
* Вызвать метод <code>afterConstruction()</code> для этой инстанции.
* </li><li>
* Присвоить ссылку на эту инстанцию переменной <code>v</code>.
* </li></ol>
* Таким образом, код внутри переопределённого <code>afterConstruction()</code> выполняется для только что созданного и полностью проинициализированного объекта.
*
* Для массивов этот метод не вызывается, поскольку массивы в ПВТ-ОО имеют иной механизм инициализации.
*
* Реализация данного метода в классе <code>Object</code> не делет чего-либо и сразу же возвращает управление.
* @see #beforeDestruction()
*/
protected void afterConstruction() { }
/** Вызывается автоматическим сборщиком мусора перед утилизацией этого объекта.
* Если автоматический сборщик мусора видит, что этот объект больше не используется программой, то вызовет
* для него метод <code>beforeDestruction()</code> и отметит выделенную под него память как свободную.
* Переопределение этого метода необходимо для освобождения ресурсов, созданных системными библиотеками опреационной системы и требующих явного удаления из памяти.
*
* При переопределении данного метода следует помнить о том, что этот объект будет утилизирован после возврата управления,
* поэтому коду внутри переопределённого <code>beforeDestruction()</code> не следует сохранять где-либо ссылку на этот объект, иначе неизбежны ошибки в работе программы.
* Любые исключения, возбужденные этим методом, игнорируются автоматическим сборщиком мусора.
*
* Для массивов этот метод не вызывается, поскольку массивы в ПВТ-ОО имеют иной механизм инициализации.
*
* Реализация данного метода в классе <code>Object</code> не делет чего-либо и сразу же возвращает управление.
* @see #afterConstruction()
*/
protected void beforeDestruction() { }
package final native void defaultConstructor();
package final void checkObjectArrayAssignable(Object component) {
Class saving;
Class required = getClass().componentType;
if(required == null || (required.attributes & Class.TYPE_MASK) == Class.PRIMITIVE)
{
throw new VerifyError(package.getResourceString("!machine-error.verify.array-of-objects"));
}
if(component != null && !required.isAssignableFrom(saving = component.getClass()))
{
throw new ArrayStoreException(package.getResourceString("array-store")) { saving = saving, required = required };
}
}
package final Monitor monitor { read = fldMonitor }
private void lock() {
Mutex monitor = fldMonitor;
if(monitor == null)
{
Runtime runtime = Runtime.getInstance();
Mutex operation = runtime.systemOperation;
operation.lock();
try
{
if((monitor = fldMonitor) == null) monitor = fldMonitor = runtime.newMonitor();
} finally
{
operation.unlock();
}
}
monitor.lock();
}
private void unlock() {
Mutex monitor = fldMonitor;
if(monitor == null)
{
throw new IllegalMonitorStateException(package.getResourceString("illegal-state.monitor"));
}
monitor.unlock();
}
}