FPC JVM/Language/ru

From Free Pascal wiki
Revision as of 03:32, 7 November 2011 by AlexVinS (talk | contribs) (→‎New language features: +перевод)
Jump to navigationJump to search

English (en) русский (ru)

Общая информация

Поддержка JVM это пока лишь изменения в компиляторе. А это значит, что портирован только модуль System и модули которые импортируют лишь некоторые JDK классы. Другие RTL модули не доступны. Функционал модуля System так же очень ограничен (подробности ниже).

Со временем, вполне возможно реализовать большинство стандартных модулей RTL таким образом, чтобы они компилировались для JVM платформы. Но это не является целью и баг-репорты о том что какого-то модуля не хватает - особой пользы не принесут. (Но конечно, патчи приветствуются!)

Компиляция в JVM код на данный момент довольно медленная. Причина - используемый Ява ассемблер Jasmin - медленный. И причина не в том, что сам ассемблер написан на Java, а просто потому что он так написан.

Минимальная требуемая версия JDK для программ скомпилированных FPC - 1.5.

Терминология и соглашения

  • on the Pascal side/on the Java side: refers to code written in resp. Pascal or Java. A number of features are only available when programming in Pascal, because the Java language has no similar concept.
  • неявные указатели - некоторые типы, которые не являются указателями в Паскале, на уровне Ява реализованы как классы или массивы. Что делает их неявными указателями на данные, т.к. и Ява-массивы и классы - указатели. Возможно это и не очевидно, но переменные такого типа, как раз можно заставить действовать, точно так же как и на нативных платформах, чем переменные каких-либо других типов.
  • полное имя класса - на данный момент компилятор не поддерживает пространство имён (такие как "org.freepascal" или "java.lang") как элемент синтаксиса. Точно так же имена модулей с точками не поддерживаются. Это означает что нельзя в качестве имён использовать строки вроде "java.lang.Object". Все Паскаль-заголовки для импортированных Ява-модулей используют аббревиатуру имени класса, используя первые буквы из имени каждого пакета, и заканчиваясь именем класса. Например "Java.lang.Object" - в паскале объявлен как "JLObject". Для типов объявленых в паскаль-исходниках синтаксис "имя_модуля.определитель" - работает как ожидается. Тем не менее, для всех определителей описанных ниже, будут указаны их полное имя Ява-класса, т.к. получить паскаль имя от ява-класса достаточно просто, обратное действие практически невозможно.

Базовое поведение платформы

По устройству компилятора платформа Java/JVM рассматривается как 32-битная. Это лишь означает, что вся арифметика ориентируется, по умолчанию, на 32-бита, а не 64. Точно так же, как и на нативных платформах, созданный код будет без проблем запускаться и на 64-битных ява-машинах. Компилятор так же будет использовать 64-битные инструкции (опкоды) для 64-битных вычислений.

Причина такого выбора:

  • 32-битна арифметика выполняется эффективнее, чем 64-битная;
  • Индексы в JVM массивах всегда 32-битные;

Тип extended приравнен к типу double, точно так же, как и на других платформах, не поддерживающих 80-битовые числа с плавающей точкой.

Языковая поддержка

В связи с разницей между JVM и нативными платформами, не все особенности языка FPC могут быть поддержаны на JVM. Языковая конструкция не указанная ниже, при компиляции в JVM работает точно так же и при обычной компиляции. Однако может возникать некоторая разница в производительности и внутренней реализации.

Основные конструкции языка

Неподдерживаемые конструкции

  • Turbo Pascal объекты (objects) - поддержка может быть добавлена в будущем, т.к. они очень похожи на структуры (records).
  • Bitpacking (битовая упаковка)- а так же любые преобразования которые влияют на размещение данных в памяти ({$packset xxx}, {$packrecords xxx}, {$packenum xxx}, ...).
  • Class helpers - Возможно реализовать, но достаточно сложно. На стороне Ява они могут использоваться в лучшем случае, непосредственным вызовом методов, так как Язык Java не поддерживает автоматическое перенаправленные вызова методов от одного класса к другому.
  • Variants - возможно реализовать, однако их реализация может быть не очень удобной, т.к; ява не поддерживает перегрузку операторов.
  • Delphi-style RTTI - возможно может быть эмулирована.
  • Resourcestring - пока неизвестно насколько трудоёмкая задача по их добавлению.
  • Вложенные процедурные типы - вложенные процедурные типы. В то время как вложенные процедуры и процедурные типы поддерживаются, вложенный-процедурный тип остаётся не реализованным. Возможно будет добавлен в будущем.
  • Не локальные goto. - скорее всего никогда не будет реализовано. Теоретически реализовать возможно используя исключения (exceptions) и внутрепроцедурные переходы (goto).
  • Inline assember - пока что нет поддержки ассемблера (Java byte code) для JVM.

Частично поддерживаемые конструкции

  • Функционал модуля System. В настоящее время модуль System существенно ограничен в фукциях и не содержит процедур для поддержки некоторый стандартных функций языка (включая все виды ввода/вывода, поддержку ресурсов и вариантного типа). Больгинство этих функций могут быть реализованы в будущем. На данный момент реализовано: randomize/random, copy (для массивов и строк), halt, lo/hi, abs, sqr, odd, endian swapping routines, ror*/rol*/sar*/bsf*/bsr*, upcase/lowercase, runerror, insert, pos, delete, val, str и большинство математических функций (cos, sin, и т.д.).
  • Указатели: существует возможность определять указатели. Однако, можно только взять адрес var/out/constref - параметра или Неявного указателя. Арифметика указателей не поддерживается. Индексирование указателей как массивов однако возможно при активированном {$modeswitch cs_pointermath}'. FIXME: на данный момент постоянно включено.
  • Variant records: the variant parts of variant records do not overlap in memory, and hence cannot be used to map the same data in different ways. As a result, they also do not save any memory unlike on native targets.
  • Call-by-reference parameters (var/out/constref): the JVM does not support call-by-reference, nor taking the address of a local variable. For #Implicit_pointer_types, this is no problem since there the compiler always has a pointer to the actual data available. For other types, call-by-reference is emulated by the compiler via copy-in/copy-out, which means that changes are not immediately visible outside the called routine. The steps followed by the compiler are:
    • construct an array of one element
    • store the parameter value in the array (in case of var/constref)
    • pass the array to the called routine
    • the routine can change the value in the array
    • on return, the potentially changed value is copied out of the array back into the original variable (in case of var/out)
  • Untyped const/var/out parameters. These are supported in the same way as they are in Delphi.NET, see http://hallvards.blogspot.com/2007/10/dn4dp24-net-vs-win32-untyped-parameters.html (with the same limitations as regular var/out parameters on the JVM target)
  • Include files. While include files will work fine at compile time, the Java class file format does not support referring to more than one source file. As a result, the compiler will only insert debugging line information for code in the main unit file. This limitation may be resolved in the future through the use of SMAP files as described in http://jcp.org/aboutJava/communityprocess/final/jsr045/index.html
  • Resources. Currently, files specified in {$r xxx} directives will be copied without any further processing into a jar file with the same name as the program, under the directory org/freepascal/rawresources. If you add this jar file to the Java class path when executing the program, you can load the resource files using the JDK's built-in resource file helpers (see java.lang.Class.getResource() and java.lang.Class.getResourceAsStream()). Delphi-style resource support may be (partially) added in the future.
  • Unit initialization code. If a unit is used from an FPC-compiled program, then the unit initialization code will be run on startup. If the main program is a Java program, then the unit initialization code will only run when the first method is entered that accesses a global constant or variable or calls a global procedure/function from that unit. Note: using classes, types or class variables from a unit does not by itself trigger executing the unit's initialzation code. If you wish to manually trigger this, you can do so by adding a statement such as Object dummy = new unitName(); to your Java code.

Новые языковые конструкции

  • директива {$namespace x.y.z}. Хотя пунктирной имена модулей "с точками" не поддерживаются, (глобальная) директива пространства имён может использоваться, чтобы указать компилятору помещать все определения в текущем модуле в указанный пакет Java.
  • Формальные определения классов. Они не существуют в Java, но могут быть необходимы для решения проблем с циклическими ссылками между классами Java. Они такие же, как их Objective-C эквиваленты, за исключением того, что используют ключевое слово class, а не objcclass.
  • Внешние определения классов. Внешние определения классов используются для импорта модулей JDK. Опять же, они очень похожи на своих коллег из Objective-C, кроме того, что дополнительно может быть указан пакет (заметьте, что явно пакет может быть задан только для внешних классов, не внешние определение класса всегда использовать пакет из {$namespace xxx} директивы):

<delphi> type JavaClassName = class [external ['package.name'] [name 'ExternalClassName']] [(JavaSuperClassName|ProtocolName [, ProtocolName, ProtocolName])] [strict][private, protected, public]

[variables declarations]
[method declarations]

end; </delphi>

  • Окончательные(final) поля. Поля класса и экземпляра класса могут быть объявлены окончательнми.Окончательное поле класса может быть записано только в конструкторе класса, в то время окончательное поле экземпляра класса может быть написано только в регулярном конструкторе. Вне этих процедур, они могут быть только прочитаны. Компилятор не в состоянии в полной мере проверить это ограничение, если вы используете указатель на поле.

Overloading

A number of different Pascal types and parameter passing conventions map to the same type signature in the JVM. The reason is that the JVM supports less primitive types than Pascal, and because some parameter passing constructs have to be emulated using other types.

This means that certain overloads that are valid for native targets are invalid when targeting the JVM, because the internal method name has to encode the parameter types on that target. The compiler will detect such situations and print an error when they occur.

Cases that you may encounter include:

  • Class references. All class reference types map to the same Java type (java.lang.Class). As a result, overloading based on different class reference types is not possible.
  • Ordinal types (except for enumerations). The JVM only supports signed ordinal types. This means that overloading based on signed and unsigned ordinal types of the same size is not possible. Additionally, the currency type maps to int64, and the ansichar type to shortint. Widechar (= unicodechar) is a separate type.
  • Enumerations. Every base enumeration type is a separate type, but subtypes of enumerations map to the same type as the base type.
  • Sets. All sets of non-enumeration types map to the same type (org.freepascal.rtl.FpcBitSet). All sets of enumeration types (regardless of the enumeration involved) also map to the same type (java.util.EnumSet).
  • Array types. Every array of a particular type (integer, a class type, an enumeration, ...) maps to the same signature, regardless of the size or kind of the array. This includes fixed size, open and dynamic arrays.
  • Call-by-reference parameters and arrays. As explained earlier, call-by-reference parameters are emulated using arrays. This means that e.g. a var byte and a const array of byte parameter will map to the same type signature and cannot be overloaded. Pointers to types other than the implicit pointer types also map to arrays.

Specific language features information

Classes

Classes are implemented as Java classes:

  • The system unit contains an extremely minimal TObject implementation based on java.lang.Object. It basically only adds a virtual destroy destructor and the Free instance method, and lacks all other standard TObject functionality. Notice that the destructor is only guaranteed to be called if you explicitly call Free, or when the dead object is collected by the garbage collector.
  • If no super class is specified, a class will inherit from TObject. It is also possible to inherit from java.lang.Object by specifying JLObject as superclass instead.
  • There is no TInterfacedObject, nor is it required. Any class type can implement any kind of interface.
  • Message handlers are not supported.
  • Java classes do not support non-virtual instance methods. The compiler emulates this Pascal language feature by translating such instance methods into virtual; final; methods. This means that it is not possible to "hide" such methods in child classes by declaring other (virtual or non-virtual) methods with the same name and type signature.
  • Java classes do not support starting a new inheritance tree by reintroducing a method. This is only a problem if the reintroduced method has the same signature as a method in a parent class. If the signature is different, there is no problem and the compiler will allow it.
  • Java classes do not support virtual class methods nor virtual constructors. These are emulated by the compiler, but the resulting code is fairly slow. Do not use them in performance-sensitive code.
  • Constructors do not have a name in Java (and a fixed name in JVM bytecode). As a result, you cannot declare multiple constructors for a class with a different name but with the same parameters, as these would map to the same Java constructor signature.
  • Properties are only supported on the Pascal side. No getters or setters are automatically generated for use on the Java side.

Issues to take into account when using FPC-defined classes from Java code:

  • Virtual constructors are exposed as class methods. These class methods have first parameter that specifies the class type of which you want to invoke the virtual constructor, and their name matches the constructor's name in the Pascal code. Example:

<delphi> type TMyClass = class constructor Create(l: longint); virtual; end; </delphi>

becomes

<java> class TMyClass { TMyClass(int l) { ... }

// the generic parameter declaration means "any class reference // type for TMyClass or one of its descendants") static TMyClass Create(java.lang.Class<? extends TMyClass> self, int l) { ... } </java>

  • virtual and regular (non-static) class methods are exposed in a similar way as virtual class constructors, including the special first class parameter that indicates the class type from which the class method has been invoked (class methods in Java do not have a hidden self-parameter like in Pascal, so it has to be specified explicitly)
  • Overriding virtual class methods and constructors on the Java side can be done by overriding the class method whose name consists of the virtual constructor/class method's name followed by __fpcvirtualclassmethod__.
  • static class methods behave the same in Pascal as in Java

Интерфейсы (Interface)

Интерфейсы реализованы, используя Ява интерфейсы:

  • Нет счётчика ссылок - сборка мусора;
  • GUID не используются;
  • Не поддерживается возможность использовать разное имя метода в интерфейсы и в классе-реализации (implements);

Implicit pointer types

As mentioned in the #Used_terminology_and_conventions, certain types that are not pointer-based in the Pascal language are implemented on top of Java classes or arrays. These data structures are pointers in Java, and as a result we can do some things with them that we cannot do with other types that map more directly onto a Java type.

The implicit pointer types and their corresponding Java representations are listed below:

  • Record: sealed subclasses of org.freepascal.rtl.FpcBaseRecordClass
  • Set: sets of enumerations are implemented as specialized java.util.EnumSet instances, while other sets are implemented as org.freepascal.rtl.FpcBitSet
  • Shortstring: instances of org.freepascal.rtl.ShortStringClass
  • Procedure of object: subclasses of org.freepascal.rtl.FpcBaseProcVarClass
  • non-dynamic arrays: regular Java arrays

Specific properties of implicit pointer types are:

  • It is always possible to take their address and store it in a pointer
  • When passing them to var or out parameters, changes made via the parameter are also immediately visible in the original location (and when passing them as a constref parameters, changes made to the original value are also immediately visible in the parameter value)
  • Pointers to implicit pointer types can never be indexed as arrays

Строки

String, unicodestring и java.lang.String

JVM поддерживает только 16-bit unicodestring тип (java.lang.String). По этой причине, unicodestring в целом более эффективен, чем ansistring или shortstring на JVM.

Тип unicodestring напрямую перенесён в тип java.lang.String. Что так же означает, что если ваш код будет использоваться другим Ява-кодоам, все публичные строковые параметры и константы должны быть объявлены как unicodestring, а не какой-либо другой строковый тип.

Чтобы использовать тип unicodestring по-умолчанию, была добавлена директива компиляции {$modeswitch unicodestrings}. Если директива объявлена ВМЕСТЕ с директивной {$h+}, тип string преобразуется в тип uncodestring по-умолчанию, а char в тип widechar. Подобное преобразование происходит в Delphi 2009, затем исключением, что modeswitch действует на 1 модуль а не на весь код.

Если {$modeswitch unicodestrings} использован с {$h-}, тогда string тип преобразуется в shortstring, но тип char становится widechar. Для синтаксиса {$mode delphi} режим {$h+} включается по-умолчанию.

Кодировка Ansistring и shortstring

"ansi" кодировка устанавливается java программе при запуске. Кодировка устанавливается в зависимости от кодировки выбранной в операционной системы, в которой запущена программа Linux или Windows. Для Mac OS X, по причинем обратной совместимости кодировка всегда MacRoman, и скорее всего, эту кодировку вы использовать не хотите. Для дополнительной информации о кодировке см. Usage instructions.

String conversions

The compiler will implicitly convert any string type to java.lang.String and vice versa.

Type aliases and subrange types

The JVM supports neither type aliases nor subrange types. As a result, none of the marked type definitions in the following example will be visible to Java code. Of course, they still work as expected in Pascal code. <delphi> type

 tc = class
 end;
 tenum = (ea,eb,ec);
 tc2 = tc; // Pascal-only
 intsub = 3..41; // Pascal-only
 enumsub = eb..ec; // Pascal-only

</delphi>