Permalink
Fetching contributors…
Cannot retrieve contributors at this time
1103 lines (802 sloc) 51.5 KB
\chapter{Standard Library}
\label{std}
\state{NoContent}
\section{String}
\label{std-String}
\define[Type]{String}{define-string}{A String is a sequence of characters.}
%TODO: utf8 crap %
\paragraph{Character code}
Use the \ic{.code} property on a constant single-char string in order to compile its ASCII character code:
\begin{lstlisting}
"#".code // will compile as 35
\end{lstlisting}
\paragraph{Related content}
\begin{itemize}
\item See the \href{https://api.haxe.org/String.html}{String API} for details about its methods.
\end{itemize}
\section{Data Structures}
\label{std-ds}
\state{NoContent}
\subsection{Array}
\label{std-Array}
An \type{Array} is a collection of elements. It has one \tref{type parameter}{type-system-type-parameters} which corresponds to the type of these elements. Arrays can be created in three ways:
\begin{enumerate}
\item By using their constructor: \expr{new Array()}
\item By using \tref{array declaration syntax}{expression-array-declaration}: \expr{[1, 2, 3]}
\item By using \tref{array comprehension}{lf-array-comprehension}: \expr{[for (i in 0...10) if (i \% 2 == 0) i]}
\end{enumerate}
Arrays come with an \href{https://api.haxe.org/Array.html}{API} to cover most use-cases. Additionally they allow read and write \tref{array access}{expression-array-access}:
\haxe{assets/ArrayAccess.hx}
Since array access in Haxe is unbounded, i.e. it is guaranteed to not throw an exception, this requires further discussion:
\begin{itemize}
\item If a read access is made on a non-existing index, a target-dependent value is returned.
\item If a write access is made with a positive index which is out of bounds, \expr{null} (or the \tref{default value}{define-default-value} for \tref{basic types}{types-basic-types} on \tref{static targets}{define-static-target}) is inserted at all positions between the last defined index and the newly written one.
\item If a write access is made with a negative index, the result is unspecified.
\end{itemize}
Arrays define an \tref{iterator}{lf-iterators} over their elements. This iteration is typically optimized by the compiler to use a \tref{\expr{while} loop}{expression-while} with array index:
\haxe{assets/ArrayIterator.hx}
Haxe generates this optimized \target{JavaScript} output:
\lang{js}\begin{lstlisting}
Main.main = function() {
var scores = [110,170,35];
var sum = 0;
var _g = 0;
while(_g < scores.length) {
var score = scores[_g];
++_g;
sum += score;
}
console.log(sum);
};
\end{lstlisting}
Haxe does not allow arrays of mixed types unless the parameter type is forced to \tref{\type{Dynamic}}{types-dynamic}:
\haxe{assets/ArrayDynamic.hx}
\trivia{Dynamic Arrays}{In Haxe 2, mixed type array declarations were allowed. In Haxe 3, arrays can have mixed types only if they are explicitly declared as \expr{Array<Dynamic>}.}
\paragraph{Related content}
\begin{itemize}
\item See the \href{https://api.haxe.org/Array.html}{Array API} for details about its methods.
\item \href{http://code.haxe.org/category/data-structures/}{Data structures tutorials and examples} in the Haxe Code Cookbook.
\end{itemize}
\subsection{Vector}
\label{std-vector}
A \type{Vector} is an optimized fixed-length \emph{collection} of elements. Much like \tref{Array}{std-Array}, it has one \tref{type parameter}{type-system-type-parameters} and all elements of a vector must be of the specified type, it can be \emph{iterated over} using a \tref{for loop}{expression-for} and accessed using \tref{array access syntax}{types-abstract-array-access}. However, unlike \type{Array} and \type{List}, vector length is specified on creation and cannot be changed later.
\haxe{assets/Vector.hx}
\type{haxe.ds.Vector} is implemented as an abstract type (\ref{types-abstract}) over a native array implementation for given target and can be faster for fixed-size collections, because the memory for storing its elements is pre-allocated.
\paragraph{Related content}
\begin{itemize}
\item See the \href{https://api.haxe.org/haxe/ds/Vector.html}{Vector API} for details about the vector methods.
\item \href{http://code.haxe.org/category/data-structures/}{Data structures tutorials and examples} in the Haxe Code Cookbook.
\end{itemize}
\subsection{List}
\label{std-List}
A \type{List} is a \emph{collection} for storing elements. On the surface, a list is similar to an \Fullref{std-Array}. However, the underlying implementation is very different. This results in several functional differences:
\begin{enumerate}
\item A list can not be indexed using square brackets, i.e. \expr{[0]}.
\item A list can not be initialized.
\item There are no list comprehensions.
\item A list can freely modify/add/remove elements while iterating over them.
\end{enumerate}
A simple example for working with lists:
\haxe{assets/ListExample.hx}
\paragraph{Related content}
\begin{itemize}
\item See the \href{https://api.haxe.org/List.html}{List API} for details about the list methods.
\item \href{http://code.haxe.org/category/data-structures/}{Data structures tutorials and examples} in the Haxe Code Cookbook.
\end{itemize}
\subsection{GenericStack}
\label{std-GenericStack}
A \type{GenericStack}, like \type{Array} and \type{List} is a container for storing elements. It has one \tref{type parameter}{type-system-type-parameters} and all elements of the stack must be of the specified type. Here is a small example program for initializing and working with a \type{GenericStack}.
\haxe{assets/GenericStackExample.hx}
\trivia{FastList}{In Haxe 2, the GenericStack class was known as FastList. Since its behavior more closely resembled a typical stack, the name was changed for Haxe 3.}
The \emph{Generic} in \type{GenericStack} is literal. It is attributed with the \expr{:generic} metadata. Depending on the target, this can lead to improved performance on static targets. See \Fullref{type-system-generic} for more details.
\paragraph{Related content}
\begin{itemize}
\item See the \href{https://api.haxe.org/haxe/ds/GenericStack.html}{GenericStack API} for details about its methods.
\item \href{http://code.haxe.org/category/data-structures/}{Data structures tutorials and examples} in the Haxe Code Cookbook.
\end{itemize}
\subsection{Map}
\label{std-Map}
A \type{Map} is a container composed of \emph{key}, \emph{value} pairs. A \type{Map} is also commonly referred to as an associative array, dictionary, or symbol table. The following code gives a short example of working with maps:
\haxe{assets/MapExample.hx}
Under the hood, a \type{Map} is an \tref{abstract}{types-abstract} type. At compile time, it gets converted to one of several specialized types depending on the \emph{key} type:
\begin{itemize}
\item \type{String}: \type{haxe.ds.StringMap}
\item \type{Int}: \type{haxe.ds.IntMap}
\item \type{EnumValue}: \type{haxe.ds.EnumValueMap}
\item \type{\{\}}: \type{haxe.ds.ObjectMap}
\end{itemize}
The \type{Map} type does not exist at runtime and has been replaced with one of the above objects.
Map defines \tref{array access}{types-abstract-array-access} using its key type.
\paragraph{Related content}
\begin{itemize}
\item See the \href{https://api.haxe.org/Map.html}{Map API} for details of its methods.
\item \href{http://code.haxe.org/category/data-structures/}{Data structures tutorials and examples} in the Haxe Code Cookbook.
\end{itemize}
\subsection{Option}
\label{std-Option}
An \href{https://api.haxe.org/haxe/ds/Option.html}{Option} is an \tref{enum}{types-enum-instance} in the Haxe Standard Library which is defined like so:
\begin{lstlisting}
enum Option<T> {
Some(v:T);
None;
}
\end{lstlisting}
It can be used in various situations, such as communicating whether or not a method had a valid return and if so, what value it returned:
\haxe{assets/OptionUsage.hx}
\section{Regular Expressions}
\label{std-regex}
Haxe has built-in support for \emph{regular expressions}\footnote{http://en.wikipedia.org/wiki/Regular_expression}. They can be used to verify the format of a string, transform a string or extract some regular data from a given text.
Haxe has special syntax for creating regular expressions. We can create a regular expression object by typing it between the \expr{\textasciitilde/} combination and a single \expr{/} character:
\begin{lstlisting}
var r = ~/haxe/i;
\end{lstlisting}
Alternatively, we can create regular expression with regular syntax:
\begin{lstlisting}
var r = new EReg("haxe", "i");
\end{lstlisting}
First argument is a string with regular expression pattern, second one is a string with \emph{flags} (see below).
We can use standard regular expression patterns such as:
\begin{itemize}
\item \expr{.} any character
\item \expr{*} repeat zero-or-more
\item \expr{+} repeat one-or-more
\item \expr{?} optional zero-or-one
\item \expr{[A-Z0-9]} character ranges
\item \expr{[\textasciicircum\textbackslash r\textbackslash n\textbackslash t]} character not-in-range
\item \expr{(...)} parenthesis to match groups of characters
\item \expr{\textasciicircum} beginning of the string (beginning of a line in multiline matching mode)
\item \expr{\$} end of the string (end of a line in multiline matching mode)
\item \expr{|} "OR" statement.
\end{itemize}
For example, the following regular expression matches valid email addresses:
\begin{lstlisting}
~/[A-Z0-9._\%-]+@[A-Z0-9.-]+\.[A-Z][A-Z][A-Z]?/i;
\end{lstlisting}
Please notice that the \expr{i} at the end of the regular expression is a \emph{flag} that enables case-insensitive matching.
The possible flags are the following:
\begin{itemize}
\item \expr{i} case insensitive matching
\item \expr{g} global replace or split, see below
\item \expr{m} multiline matching, \expr{\textasciicircum} and \expr{\$} represent the beginning and end of a line
\item \expr{s} the dot \expr{.} will also match newlines \emph{(Neko, C++, PHP, Flash and Java targets only)}
\item \expr{u} use UTF-8 matching \emph{(Neko and C++ targets only)}
\end{itemize}
\paragraph{Related content}
\begin{itemize}
\item See the \href{https://api.haxe.org/EReg.html}{EReg API} for details about its methods.
\item \href{http://code.haxe.org/tag/ereg.html}{Haxe snippets and tutorials about regular expressions} in the Haxe Code Cookbook.
\end{itemize}
\subsection{Matching}
\label{std-regex-match}
Probably one of the most common uses for regular expressions is checking whether a string matches the specific pattern. The \expr{match} method of a regular expression object can be used to do that:
\haxe{assets/ERegMatch.hx}
\subsection{Groups}
\label{std-regex-groups}
Specific information can be extracted from a matched string by using \emph{groups}. If \expr{match()} returns true, we can get groups using the \expr{matched(X)} method, where X is the number of a group defined by regular expression pattern:
\haxe{assets/ERegGroups.hx}
Note that group numbers start with 1 and \expr{r.matched(0)} will always return the whole matched substring.
The \expr{r.matchedPos()} will return the position of this substring in the original string:
\haxe{assets/ERegMatchPos.hx}
Additionally, \expr{r.matchedLeft()} and \expr{r.matchedRight()} can be used to get substrings to the left and to the right of the matched substring:
\haxe{assets/ERegMatchLeftRight.hx}
\subsection{Replace}
\label{std-regex-replace}
A regular expression can also be used to replace a part of the string:
\haxe{assets/ERegReplace.hx}
We can use \expr{\$X} to reuse a matched group in the replacement:
\haxe{assets/ERegReplaceGroups.hx}
\subsection{Split}
\label{std-regex-split}
A regular expression can also be used to split a string into several substrings:
\haxe{assets/ERegSplit.hx}
\subsection{Map}
\label{std-regex-map}
The \expr{map} method of a regular expression object can be used to replace matched substrings using a custom function. This function takes a regular expression object as its first argument so we may use it to get additional information about the match being done and do conditional replacement. For example:
\haxe{assets/ERegMap.hx}
\subsection{Implementation Details}
\label{std-regex-implementation-details}
Regular Expressions are implemented:
\begin{itemize}
\item in JavaScript, the runtime is providing the implementation with the object RegExp.
\item in Neko and C++, the PCRE library is used
\item in Flash, PHP, C\# and Java, native implementations are used
\item in Flash 6/8, the implementation is not available
\end{itemize}
\section{Math}
\label{std-math}
Haxe includes a floating point math library for some common mathematical operations. Most of the functions operate on and return \type{floats}. However, an \type{Int} can be used where a \type{Float} is expected, and Haxe also converts \type{Int} to \type{Float} during most numeric operations (see \Fullref{types-numeric-operators} for more details).
Here are some example uses of the math library:
\haxe{assets/MathExample.hx}
\paragraph{Related content}
\begin{itemize}
\item See the \href{https://api.haxe.org/Math.html}{Math API documentation} for all available functions.
\item \href{http://code.haxe.org/tag/math.html}{Haxe snippets and tutorials about math} in the Haxe Code Cookbook.
\end{itemize}
\subsection{Special Numbers}
\label{std-math-special-numbers}
The math library has definitions for several special numbers:
\begin{itemize}
\item NaN (Not a Number): returned when a mathematically incorrect operation is executed, e.g. Math.sqrt(-1)
\item POSITIVE_INFINITY: e.g. divide a positive number by zero
\item NEGATIVE_INFINITY: e.g. divide a negative number by zero
\item PI : 3.1415...
\end{itemize}
\subsection{Mathematical Errors}
\label{std-math-mathematical-errors}
Although neko can fluidly handle mathematical errors, like division by zero, this is not true for all targets. Depending on the target, mathematical errors may produce exceptions and ultimately errors.
\subsection{Integer Math}
\label{std-math-integer-math}
If you are targeting a platform that can utilize integer operations, e.g. integer division, it should be wrapped in \emph{Std.int()} for improved performance. The Haxe Compiler can then optimize for integer operations. An example:
\begin{lstlisting}
var intDivision = Std.int(6.2/4.7);
\end{lstlisting}
\todo{I think C++ can use integer operatins, but I don't know about any other targets. Only saw this mentioned in an old discussion thread, still true?}
\subsection{Extensions}
\label{std-math-extensions}
It is common to see \Fullref{lf-static-extension} used with the math library. This code shows a simple example:
\haxe{assets/MathStaticExtension.hx}
\haxe{assets/MathExtensionUsage.hx}
\section{Lambda}
\label{std-Lambda}
\define{Lambda}{define-lambda}{Lambda is a functional language concept within Haxe that allows you to apply a function to a list or \tref{iterators}{lf-iterators}. The Lambda class is a collection of functional methods in order to use functional-style programming with Haxe.}
It is ideally used with \expr{using Lambda} (see \tref{Static Extension}{lf-static-extension}) and then acts as an extension to \type{Iterable} types.
On static platforms, working with the \type{Iterable} structure might be slower than performing the operations directly on known types, such as \type{Array} and \type{List}.
\paragraph{Lambda Functions}
The Lambda class allows us to operate on an entire \type{Iterable} at once.
This is often preferable to looping routines since it is less error prone and easier to read.
For convenience, the \type{Array} and \type{List} class contains some of the frequently used methods from the Lambda class.
It is helpful to look at an example. The exists function is specified as:
\begin{lstlisting}
static function exists<A>( it : Iterable<A>, f : A -> Bool ) : Bool
\end{lstlisting}
Most Lambda functions are called in similar ways. The first argument for all of the Lambda functions is the \type{Iterable} on which to operate. Many also take a function as an argument.
\begin{description}
\item[\expr{Lambda.array}, \expr{Lambda.list}] Convert Iterable to \type{Array} or \type{List}. It always returns a new instance.
\item[\expr{Lambda.count}] Count the number of elements. If the Iterable is a \type{Array} or \type{List} it is faster to use its length property.
\item[\expr{Lambda.empty}] Determine if the Iterable is empty. For all Iterables it is best to use the this function; it's also faster than compare the length (or result of Lambda.count) to zero.
\item[\expr{Lambda.has}] Determine if the specified element is in the Iterable.
\item[\expr{Lambda.exists}] Determine if criteria is satisfied by an element.
\item[\expr{Lambda.indexOf}] Find out the index of the specified element.
\item[\expr{Lambda.find}] Find first element of given search function.
\item[\expr{Lambda.foreach}] Determine if every element satisfies a criteria.
\item[\expr{Lambda.iter}] Call a function for each element.
\item[\expr{Lambda.concat}] Merge two Iterables, returning a new List.
\item[\expr{Lambda.filter}] Find the elements that satisfy a criteria, returning a new List.
\item[\expr{Lambda.map}, \expr{Lambda.mapi}] Apply a conversion to each element, returning a new List.
\item[\expr{Lambda.fold}] Functional fold, which is also known as reduce, accumulate, compress or inject.
\end{description}
This example demonstrates the Lambda filter and map on a set of strings:
\begin{lstlisting}
using Lambda;
class Main {
static function main() {
var words = ['car', 'boat', 'cat', 'frog'];
var isThreeLetters = function(word) return word.length == 3;
var capitalize = function(word) return word.toUpperCase();
// Three letter words and capitalized.
trace(words.filter(isThreeLetters).map(capitalize)); // [CAR,CAT]
}
}
\end{lstlisting}
This example demonstrates the Lambda count, has, foreach and fold function on a set of ints.
\begin{lstlisting}
using Lambda;
class Main {
static function main() {
var numbers = [1, 3, 5, 6, 7, 8];
trace(numbers.count()); // 6
trace(numbers.has(4)); // false
// test if all numbers are greater/smaller than 20
trace(numbers.foreach(function(v) return v < 20)); // true
trace(numbers.foreach(function(v) return v > 20)); // false
// sum all the numbers
var sum = function(num, total) return total += num;
trace(numbers.fold(sum, 0)); // 30
// get highest number
trace(numbers.fold(Math.max, numbers[0])); // 8
// get lowest number
trace(numbers.fold(Math.min, numbers[0])); // 1
}
}
\end{lstlisting}
\paragraph{Related content}
\begin{itemize}
\item See the \href{https://api.haxe.org/Lambda.html}{Lambda API documentation} for all available functions.
\end{itemize}
\section{Template}
\label{std-template}
Haxe comes with a standard template system with an easy to use syntax which is interpreted by a lightweight class called \href{https://api.haxe.org/haxe/Template.html}{haxe.Template}.
A template is a string or a file that is used to produce any kind of string output depending on the input. Here is a small template example:
\haxe{assets/Template.hx}
The console will trace \ic{My name is Mark, 30 years old}.
\paragraph{Expressions}
An expression can be put between the \ic{::}, the syntax allows the current possibilities:
\begin{description}
\item[\ic{::name::}] the variable name
\item[\ic{::expr.field::}] field access
\item[\ic{::(expr)::}] the expression expr is evaluated
\item[\ic{::(e1 op e2)::}] the operation op is applied to e1 and e2
\item[\ic{::(135)::}] the integer 135. Float constants are not allowed
\end{description}
\paragraph{Conditions}
It is possible to test conditions using \ic{::if flag1::}. Optionally, the condition may be followed by \ic{::elseif flag2::} or \ic{::else::}. Close the condition with \ic{::end::}.
\lang{none}\begin{lstlisting}
::if isValid:: valid ::else:: invalid ::end::
\end{lstlisting}
Operators can be used but they don't deal with operator precedence. Therefore it is required to enclose each operation in parentheses \ic{()}. Currently, the following operators are allowed: \ic{+}, \ic{-}, \ic{*}, \ic{/}, \ic{>}, \ic{<}, \ic{>=}, \ic{<=}, \ic{==}, \ic{!=}, \ic{\&\&} and \ic{||}.
For example \ic{::((1 + 3) == (2 + 2))::} will display true.
\lang{none}\begin{lstlisting}
::if (points == 10):: Great! ::end::
\end{lstlisting}
To compare to a string, use double quotes \ic{"} in the template.
\lang{none}\begin{lstlisting}
::if (name == "Mark"):: Hi Mark ::end::
\end{lstlisting}
\paragraph{Iterating}
Iterate on a structure by using \ic{::foreach::}. End the loop with \ic{::end::}.
\lang{xml}\begin{lstlisting}
<table>
<tr>
<th>Name</th>
<th>Age</th>
</tr>
::foreach users::
<tr>
<td>::name::</td>
<td>::age::</td>
</tr>
::end::
</table>
\end{lstlisting}
\paragraph{Sub-templates}
To include templates in other templates, pass the sub-template result string as a parameter.
\begin{lstlisting}
var users = [{name:"Mark", age:30}, {name:"John", age:45}];
var userTemplate = new haxe.Template("::foreach users:: ::name::(::age::) ::end::");
var userOutput = userTemplate.execute({users: users});
var template = new haxe.Template("The users are ::users::");
var output = template.execute({users: userOutput});
trace(output);
\end{lstlisting}
The console will trace \ic{The users are Mark(30) John(45)}.
\paragraph{Template macros}
To call custom functions while parts of the template are being rendered, provide a \expr{macros} object to the argument of \href{https://api.haxe.org/haxe/Template.html#execute}{Template.execute}. The key will act as the template variable name, the value refers to a callback function that should return a \type{String}. The first argument of this macro function is always a \expr{resolve()} method, followed by the given arguments. The resolve function can be called to retrieve values from the template context. If \expr{macros} has no such field, the result is unspecified.
The following example passes itself as macro function context and executes \ic{display} from the template.
\haxe{assets/TemplateMacros.hx}
The console will trace \ic{The results: Mark ran 3.5 kilometers in 15 minutes}.
\paragraph{Globals}
Use the \href{https://api.haxe.org/haxe/Template.html#globals}{Template.globals} object to store values that should be applied across all \type{haxe.Template} instances. This has lower priority than the context argument of \expr{Template.execute}.
\paragraph{Using resources}
To separate the content from the code, consider using the \tref{resource embedding system}{cr-resources}.
Place the template-content in a new file called \ic{sample.mtt}, add \ic{-resource sample.mtt@my_sample} to the compiler arguments and retrieve the content using \expr{haxe.Resource.getString}.
\haxe{assets/TemplateResource.hx}
When running the template system on the server side, you can simply use \expr{neko.Lib.print} or \expr{php.Lib.print} instead of trace to display the HTML template to the user.
\paragraph{Related content}
\begin{itemize}
\item See the \href{https://api.haxe.org/haxe/Template.html}{Template API} for details about its methods.
\end{itemize}
\section{Reflection}
\label{std-reflection}
Haxe supports runtime reflection of types and fields. Special care has to be taken here because runtime representation generally varies between targets. In order to use reflection correctly it is necessary to understand what kind of operations are supported and what is not. Given the dynamic nature of reflection, this can not always be determined at compile-time.
The reflection API consists of two classes:
\begin{description}
\item[Reflect:] A lightweight API which work best on \tref{anonymous structures}{types-anonymous-structure}, with limited support for \tref{classes}{types-class-instance}.
\item[Type:] A more robust API for working with classes and \tref{enums}{types-enum-instance}.
\end{description}
The available methods are detailed in the API for \href{https://api.haxe.org/Reflect.html}{Reflect} and \href{https://api.haxe.org/Type.html}{Type}.
Reflection can be a powerful tool, but it is important to understand why it can also cause problems. As an example, several functions expect a \tref{String}{std-String} argument and try to resolve it to a type or field. This is vulnerable to typing errors:
\haxe{assets/ReflectionTypo.hx}
However, even if there are no typing errors it is easy to come across unexpected behavior:
\haxe{assets/ReflectionMissingType.hx}
The problem here is that the compiler never actually ``sees'' the type \type{haxe.Template}, so it does not compile it into the output. Furthermore, even if it were to see the type there could be issues arising from \tref{dead code elimination}{cr-dce} eliminating types or fields which are only used via reflection.
Another set of problems comes from the fact that, by design, several reflection functions expect arguments of type \tref{Dynamic}{types-dynamic}, meaning the compiler cannot check if the passed in arguments are correct. The following example demonstrates a common mistake when working with \expr{callMethod}:
\haxe{assets/ReflectionWrongUsage.hx}
The commented out call would be accepted by the compiler because it assigns the string \expr{"f"} to the function argument \expr{func} which is specified to be \expr{Dynamic}.
A good advice when working with reflection is to wrap it in a few functions within an application or API which are called by otherwise type-safe code. An example could look like this:
\haxe{assets/ReflectionWrap.hx}
While the method \expr{reflective} could internally work with reflection (and \type{Dynamic} for that matter) a lot, its return value is a typed structure which the callers can use in a type-safe manner.
\section{Serialization}
\label{std-serialization}
Many runtime values can be serialized and deserialized using the \href{https://api.haxe.org/haxe/Serializer.html}{haxe.Serializer} and \href{https://api.haxe.org/haxe/Unserializer.html}{haxe.Unserializer} classes. Both support two usages:
\begin{enumerate}
\item Create an instance and continuously call the \expr{serialize}/\expr{unserialize} method to handle multiple values.
\item Call their static \expr{run} method to serialize/deserialize a single value.
\end{enumerate}
The following example demonstrates the first usage:
\haxe{assets/SerializationExample.hx}
The result of the serialization (here stored in local variable \expr{s}) is a \tref{String}{std-String} and can be passed around at will, even remotely. Its format is described in \Fullref{std-serialization-format}.
\paragraph{Supported values}
\begin{itemize}
\item \expr{null}
\item \type{Bool}, \type{Int} and \type{Float} (including infinities and \expr{NaN})
\item \type{String}
\item \type{Date}
\item \type{haxe.io.Bytes} (encoded as base64)
\item \tref{\type{Array}}{std-Array} and \tref{\type{List}}{std-List}
\item \type{haxe.ds.StringMap}, \type{haxe.ds.IntMap} and \type{haxe.ds.ObjectMap}
\item \tref{anonymous structures}{types-anonymous-structure}
\item Haxe \tref{class instances}{types-class-instance} (not native ones)
\item \tref{enum instances}{types-enum-instance}
\end{itemize}
\paragraph{Serialization configuration}
Serialization can be configured in two ways. For both a static variable can be set to influence all \href{https://api.haxe.org/haxe/Serializer.html}{haxe.Serializer} instances, and a member variable can be set to only influence a specific instance:
\begin{description}
\item[\expr{USE_CACHE}, \expr{useCache}:] If true, repeated structures or class\slash enum instances are serialized by reference. This can avoid infinite loops for recursive data at the expense of longer serialization time. By default, object caching is disabled; strings however are always cached.
\item[\expr{USE_ENUM_INDEX}, \expr{useEnumIndex}:] If true, enum constructors are serialized by their index instead of their name. This can make the resulting string shorter, but breaks if enum constructors are inserted into the type before deserialization. This behavior is disabled by default.
\end{description}
\paragraph{Deserialization behavior}
If the serialization result is stored and later used for deserialization, care has to be taken to maintain compatibility when working with class and enum instances. It is then important to understand exactly how unserialization is implemented.
\begin{itemize}
\item The type has to be available in the runtime where the deserialization is made. If \tref{dead code elimination}{cr-dce} is active, a type which is used only through serialization might be removed.
\item Each \type{Unserializer} has a member variable \expr{resolver} which is used to resolve classes and enums by name. Upon creation of the \type{Unserializer} this is set to \expr{Unserializer.DEFAULT_RESOLVER}. Both that and the instance member can be set to a custom resolver.
\item Classes are resolved by name using \expr{resolver.resolveClass(name)}. The instance is then created using \expr{Type.createEmptyInstance}, which means that the class constructor is not called. Finally, the instance fields are set according to the serialized value.
\item Enums are resolved by name using \expr{resolver.resolveEnum(name)}. The enum instance is then created using \expr{Type.createEnum}, using the serialized argument values if available. If the constructor arguments were changed since serialization, the result is unspecified.
\end{itemize}
\paragraph{Custom (de)serialization}
If a class defines the member method \expr{hxSerialize}, that method is called by the serializer and allows custom serialization of the class. Likewise, if a class defines the member method \expr{hxUnserialize} it is called by the deserializer:
\haxe{assets/SerializationCustom.hx}
In this example we decide that we want to ignore the value of member variable \expr{y} and do not serialize it. Instead we default it to \expr{-1} in \expr{hxUnserialize}. Both methods are annotated with the \expr{@:keep} metadata to prevent \tref{dead code elimination}{cr-dce} from removing them as they are never properly referenced in the code.
See \href{https://api.haxe.org/haxe/Serializer.html}{Serializer} and \href{https://api.haxe.org/haxe/Unserializer.html}{Unserializer} API documentation for details.
\subsection{Serialization format}
\label{std-serialization-format}
Each supported value is translated to a distinct prefix character, followed by the necessary data.
\begin{description}
\item[\expr{null}:] \expr{n}
\item[\type{Int}:] \expr{z} for zero, or \expr{i} followed by the integer display (e.g. \expr{i456})
\item[\type{Float}:] \mbox{}
\begin{description}
\item[\expr{NaN}:] \expr{k}
\item[negative infinity:] \expr{m}
\item[positive infinity:] \expr{p}
\item[finite floats:] \expr{d} followed by the float display (e.g. \expr{d1.45e-8})
\end{description}
\item[\type{Bool}:] \expr{t} for \expr{true}, \expr{f} for \expr{false}
\item[\type{String}:] \expr{y} followed by the url encoded string length, then \expr{:} and the url encoded string (e.g. \expr{y10:hi\%20there for "hi there".}
\item[name-value pairs:] a serialized string representing the name followed by the serialized value
\item[structure:] \expr{o} followed by the list of name-value pairs and terminated by \expr{g} (e.g. \expr{oy1:xi2y1:kng} for \expr{\{x:2, k:null\}})
\item[\type{List}:] \expr{l} followed by the list of serialized items, followed by \expr{h} (e.g. \expr{lnnh} for a list of two \expr{null} values)
\item[\type{Array}:] \expr{a} followed by the list of serialized items, followed by \expr{h}. For multiple consecutive \expr{null} values, \expr{u} followed by the number of \expr{null} values is used (e.g. \expr{ai1i2u4i7ni9h for [1,2,null,null,null,null,7,null,9]})
\item[\type{Date}:] \expr{v} followed by the date itself (e.g. \expr{v2010-01-01 12:45:10})
\item[\type{haxe.ds.StringMap}:] \expr{b} followed by the name-value pairs, followed by \expr{h} (e.g. \expr{by1:xi2y1:knh} for \expr{\{"x" => 2, "k" => null\}})
\item[\type{haxe.ds.IntMap}:] \expr{q} followed by the key-value pairs, followed by \expr{h}. Each key is represented as \expr{:<int>} (e.g. \expr{q:4n:5i45:6i7h} for \expr{\{4 => null, 5 => 45, 6 => 7\}})
\item[\type{haxe.ds.ObjectMap}:] \expr{M} followed by serialized value pairs representing the key and value, followed by \expr{h}
\item[\type{haxe.io.Bytes}:] \expr{s} followed by the length of the base64 encoded bytes, then \expr{:} and the byte representation using the codes \expr{A-Za-z0-9\%} (e.g. \expr{s3:AAA} for 2 bytes equal to \expr{0}, and \expr{s10:SGVsbG8gIQ} for \expr{haxe.io.Bytes.ofString("Hello !")})
\item[exception:] \expr{x} followed by the exception value
\item[class instance:] \expr{c} followed by the serialized class name, followed by the name-value pairs of the fields, followed by \expr{g} (e.g. \expr{cy5:Pointy1:xzy1:yzg} for \expr{new Point(0, 0)} (having two integer fields \expr{x} and \expr{y})
\item[enum instance (by name):] \expr{w} followed by the serialized enum name, followed by the serialized constructor name, followed by \expr{:}, followed by the number of arguments, followed by the argument values (e.g. \expr{wy3:Fooy1:A:0} for \expr{Foo.A} (with no arguments), \expr{wy3:Fooy1:B:2i4n} for \expr{Foo.B(4,null)})
\item[enum instance (by index):] \expr{j} followed by the serialized enum name, followed by \expr{:}, followed by the constructor index (starting from 0), followed by \expr{:}, followed by the number of arguments, followed by the argument values (e.g. \expr{jy3:Foo:0:0} for \expr{Foo.A} (with no arguments), \expr{jy3:Foo:1:2i4n} for \expr{Foo.B(4,null)})
\item[cache references:] \mbox{}
\begin{description}
\item[\type{String}:] \expr{R} followed by the corresponding index in the string cache (e.g. \expr{R456})
\item[class, enum or structure] \expr{r} followed by the corresponding index in the object cache (e.g. \expr{r42})
\end{description}
\item[custom:] \expr{C} followed by the class name, followed by the custom serialized data, followed by \expr{g}
\end{description}
\noindent Cached elements and enum constructors are indexed from zero.
\section{Xml}
\label{std-Xml}
Haxe provides built-in support for working with \emph{XML}\footnote{http://en.wikipedia.org/wiki/XML} data via the \href{https://api.haxe.org/Xml.html}{haxe.Xml} class.
\subsection{Getting started with Xml}
\label{std-Xml-getting-started}
\paragraph{Creating a root element}
A \type{Xml} root element can be created using the \expr{Xml.createElement} method.
\begin{lstlisting}
var root = Xml.createElement('root');
trace(root); // <root />
\end{lstlisting}
An root node element can also be created by parsing a \type{String} containing the XML data.
\begin{lstlisting}
var root = Xml.parse('<root />').firstElement();
trace(root); // <root />
\end{lstlisting}
\paragraph{Creating child elements}
Adding child elements to the root can be done using the \expr{addChild} method.
\begin{lstlisting}
var child:Xml = Xml.createElement('child');
root.addChild(child);
trace(root); // <root><child/></root>
\end{lstlisting}
Adding attributes to an element can be done by using the \expr{set()} method.
\begin{lstlisting}
child.set('name', 'John');
trace(root); // <root><child name="John"/></root>
\end{lstlisting}
\paragraph{Accessing elements and values}
This code parses an XML string into an object structure \type{Xml} and then accesses properties of the object.
\begin{lstlisting}
var xmlString = '<hello name="world!">Haxe is great!</hello>';
var xml:Xml = Xml.parse(xmlString).firstElement();
trace(xml.nodeName); // hello
trace(xml.get('name')); // world!
trace(xml.firstChild().nodeValue); // Haxe is great!
\end{lstlisting}
The difference between \expr{firstChild} and \expr{firstElement} is that the second function will return the first child with the type \type{Xml.Element}.
\paragraph{Iterate on Xml elements}
We can as well use other methods to iterate either over children or elements.
\begin{lstlisting}
for (child in xml) {
// iterate on all children.
}
for (elt in xml.elements()) {
// iterate on all elements.
}
for (user in xml.elementsNamed("user")) {
// iterate on all elements with a nodeName "user".
}
for (att in xml.attributes()) {
// iterator on all attributes.
}
\end{lstlisting}
See \href{https://api.haxe.org/Xml.html}{Xml} API documentation for details about its methods.
\subsection{Parsing Xml}
\label{std-Xml-parsing}
The static method \href{https://api.haxe.org/Xml.html#parse}{Xml.parse} can be used to parse \emph{XML} data and obtain a Haxe value from it.
\begin{lstlisting}
var xml = Xml.parse('<root>Haxe is great!</root>').firstElement();
trace(xml.firstChild().nodeValue);
\end{lstlisting}
\subsection{Encoding Xml}
\label{std-Xml-encoding}
The method \href{https://api.haxe.org/Xml.html#toString}{xml.toString()} can be used to obtain the \type{String} representation.
\begin{lstlisting}
var xml = Xml.createElement('root');
xml.addChild(Xml.createElement('child1'));
xml.addChild(Xml.createElement('child2'));
trace(xml.toString()); // <root><child1/><child2/></root>
\end{lstlisting}
\subsection{Simplified Xml access}
\label{std-Xml-simplified-access}
The \href{https://api.haxe.org/haxe/xml/Access.html}{haxe.xml.Access} API exists to provide a dot-syntax access to the most common \type{Xml} methods.
Since Haxe 4 this class is named \type{haxe.xml.Access}. In Haxe 3 it was known as \type{haxe.xml.Fast}.
Here's an example of XML Access usage:
\begin{lstlisting}
// parse some XML data
var xml = Xml.parse("
<user name='John'>
<phone>
<number>0000</number>
<number>111</number>
</phone>
</user>
");
// wrap the Xml for Access
var access = new haxe.xml.Access(xml.firstElement());
// access attributes
trace(access.att.name); // attribute "name"
if (access.has.age) trace( access.att.age ); // optional attribute
// access the "phone" child, which is wrapped with haxe.xml.Access too
var phone = access.node.phone;
// iterate over numbers
for (p in phone.nodes.number) {
trace(p.innerData);
}
\end{lstlisting}
This code works the same on all platforms.
\paragraph{Accessors}
There are different accessors that can be used with the Access API:
\begin{description}
\item[\expr{.name}] returns the name of the current element (same as \expr{Xml.nodeName}).
\item[\expr{.x}] returns the current corresponding \type{Xml} node.
\item[\expr{.att.<name>}] access to a given attribute. An exception is thrown if the attribute doesn't exists.
\item[\expr{.has.<name>}] check the existence of an attribute.
\item[\expr{.elements}] the list of all sub-elements (which are the nodes with type \type{Xml.Element}).
\item[\expr{.node.<name>}] access to the first sub element with the given name. An exception is thrown if the element doesn't exists.
\item[\expr{.nodes.<name>}] returns a List of elements with the given name.
\item[\expr{.hasNode.<name>}] check the existence of a sub node with the given name.
\item[\expr{.innerData}] returns the inner \ic{PCDATA} or \ic{CDATA} of the node. An exception is thrown if there is no data or if there not only data but also other nodes.
\item[\expr{.innerHTML}] returns the XML string built with all the sub nodes, excluding the current one.
\end{description}
\paragraph{Self-closed nodes}
Please note that we cannot access self-closed nodes as "regular" ones.
For example, provided this XML:
\begin{lstlisting}
var xml = Xml.parse("<xml>
<myNode1></myNode1>
<myNode2/>
</xml>");
var access = new haxe.xml.Access(xml.firstElement());
var myNode1Value = access.node.myNode1.innerData;
// no problem
var myNode2Value = access.node.myNode2.innerData;
// ERROR, an exception is thrown!
\end{lstlisting}
If the XML might contain self-closed nodes, check \expr{hasNode.innerData} before and default to an alternative value.
\begin{lstlisting}
var myNode2Value = access.node.myNode2.hasNode.innerData ? access.node.myNode2.innerData : null;
\end{lstlisting}
\section{Json}
\label{std-Json}
Haxe provides built-in support for (de-)serializing \emph{JSON}\footnote{http://en.wikipedia.org/wiki/JSON} data via the \ic{haxe.Json} class.
\paragraph{Related content}
\begin{itemize}
\item See the \href{https://api.haxe.org/haxe/Json.html}{Haxe Json API documentation}.
\item \href{http://code.haxe.org/tag/json.html}{Haxe snippets and tutorials about JSON} in the Haxe Code Cookbook.
\end{itemize}
\subsection{Parsing JSON}
\label{std-Json-parsing}
Use the \href{https://api.haxe.org/haxe/Json.html#parse}{haxe.Json.parse} static method to parse \emph{JSON} data and obtain a Haxe value from it:
\haxe{assets/JsonParse.hx}
Note that the type of the object returned by \expr{haxe.Json.parse} is \expr{Dynamic}, so if the structure of our data is well-known, we may want to specify a type using \tref{anonymous structures}{types-anonymous-structure}. This way we provide compile-time checks for accessing our data and most likely more optimal code generation, because compiler knows about types in a structure:
\haxe{assets/JsonParseTyped.hx}
\subsection{Encoding JSON}
\label{std-Json-encoding}
Use the \href{https://api.haxe.org/haxe/Json.html#stringify}{haxe.Json.stringify} static method to encode a Haxe value into a \emph{JSON} string:
\haxe{assets/JsonStringify.hx}
\subsection{Implementation details}
\label{std-Json-implementation-details}
The \href{https://api.haxe.org/haxe/Json.html}{haxe.Json} API automatically uses native implementation on targets where it is available, i.e. \emph{JavaScript}, \emph{Flash} and \emph{PHP} and provides its own implementation for other targets.
Usage of Haxe own implementation can be forced with \expr{-D haxeJSON} compiler argument. This will also provide serialization of \tref{enums}{types-enum-instance} by their index, \tref{maps}{std-Map} with string keys and class instances.
Older browsers (Internet Explorer 7, for instance) may not have native \emph{JSON} implementation. In case it's required to support them, we can include one of the JSON implementations available on the internet in the HTML page. Alternatively, a \expr{-D old_browser} compiler argument that will make \type{haxe.Json} try to use native JSON and, in case it's not available, fallback to its own implementation.
\section{Input/Output}
\label{std-input-output}
\section{Sys/sys}
\label{std-sys}
\section{Remoting}
\label{std-remoting}
Haxe remoting is a way to communicate between different platforms. With Haxe remoting, applications can transmit data transparently, send data and call methods between server and client side.
\paragraph{Related content}
\begin{itemize}
\item See the \href{https://api.haxe.org/haxe/remoting/}{remoting package} on the API documentation for more details on its classes.
\end{itemize}
\subsection{Remoting Connection}
\label{std-remoting-connection}
In order to use remoting, there must be a connection established. There are two kinds of Haxe Remoting connections:
\begin{description}
\item[\href{https://api.haxe.org/haxe/remoting/Connection.html}{haxe.remoting.Connection}] is used for \emph{synchronous connections}, where the results can be directly obtained when calling a method.
\item[\href{https://api.haxe.org/haxe/remoting/AsyncConnection.html}{haxe.remoting.AsyncConnection}] is used for \emph{asynchronous connections}, where the results are events that will happen later in the execution process.
\end{description}
\paragraph{Start a connection}
There are some target-specific constructors with different purposes that can be used to set up a connection:
\begin{description}
\item[All targets:]
\begin{description}
\item[\expr{HttpAsyncConnection.urlConnect(url:String)}]
Returns an asynchronous connection to the given URL which should link to a Haxe server application.
\end{description}
\item[Flash:]
\begin{description}
\item[\expr{ExternalConnection.jsConnect(name:String, ctx:Context)}]
Allows a connection to the local JavaScript Haxe code. The JS Haxe code must be compiled with the class ExternalConnection included. This only works with Flash Player 8 and higher.
\item[\expr{AMFConnection.urlConnect(url:String)} and \expr{AMFConnection.connect( cnx : NetConnection )}]
Allows a connection to an \href{http://en.wikipedia.org/wiki/Action_Message_Format}{AMF Remoting server} such as \href{http://www.adobe.com/products/adobe-media-server-family.html}{Flash Media Server} or \href{http://www.silexlabs.org/amfphp/}{AMFPHP}.
\item[\expr{SocketConnection.create(sock:flash.XMLSocket)}]
Allows remoting communications over an \type{XMLSocket}
\item[\expr{LocalConnection.connect(name:String)}]
Allows remoting communications over a \href{https://api.haxe.org/haxe/remoting/LocalConnection.html}{Flash LocalConnection}
\end{description}
\item[JavaScript:]
\begin{description}
\item[\expr{ExternalConnection.flashConnect(name:String, obj:String, ctx:Context)}]
Allows a connection to a given Flash Object. The Haxe Flash content must be loaded and it must include the \expr{haxe.remoting.Connection} class. This only works with Flash 8 and higher.
\end{description}
\item[Neko:]
\begin{description}
\item[\expr{HttpConnection.urlConnect(url:String)}]
Will work like the asynchronous version but in synchronous mode.
\item[\expr{SocketConnection.create(...)}]
Allows real-time communications with a Flash client which is using an \type{XMLSocket} to connect to the server.
\end{description}
\end{description}
\paragraph{Remoting context}
Before communicating between platforms, a remoting context has to be defined. This is a shared API that can be called on the connection at the client code.
This server code example creates and shares an API:
\begin{lstlisting}
class Server {
function new() { }
function foo(x, y) { return x + y; }
static function main() {
var ctx = new haxe.remoting.Context();
ctx.addObject("Server", new Server());
if(haxe.remoting.HttpConnection.handleRequest(ctx))
{
return;
}
// handle normal request
trace("This is a remoting server !");
}
}
\end{lstlisting}
\paragraph{Using the connection}
Using a connection is pretty convenient. Once the connection is obtained, use classic dot-access to evaluate a path and then use \expr{call()} to call the method in the remoting context and get the result.
The asynchronous connection takes an additional function parameter that will be called when the result is available.
This client code example connects to the server remoting context and calls a function \expr{foo()} on its API.
\begin{lstlisting}
class Client {
static function main() {
var cnx = haxe.remoting.HttpAsyncConnection.urlConnect("http://localhost/");
cnx.setErrorHandler( function(err) { trace('Error: $err'); } );
cnx.Server.foo.call([1,2], function(data) { trace('Result: $data'); });
}
}
\end{lstlisting}
To make this work for the Neko target, setup a Neko Web Server, point the url in the Client to \ic{"http://localhost2000/remoting.n"} and compile the Server using \ic{-main Server -neko remoting.n}.
\paragraph{Error handling}
\begin{itemize}
\item When an error occurs in a asynchronous call, the error handler is called as seen in the example above.
\item When an error occurs in a synchronous call, an exception is raised on the caller-side as if we were calling a local method.
\end{itemize}
\paragraph{Data serialization}
Haxe Remoting can send a lot of different kinds of data. See \tref{Serialization}{std-serialization}.
\paragraph{Related content}
\begin{itemize}
\item See the \href{https://api.haxe.org/haxe/remoting/}{remoting package} on the API documentation for more details on its classes.
\end{itemize}
\subsection{Implementation details}
\label{std-remoting-implementation-details}
\paragraph{JavaScript security specifics}
The html-page wrapping the js client must be served from the same domain as the one where the server is running. The same-origin policy restricts how a document or script loaded from one origin can interact with a resource from another origin. The same-origin policy is used as a means to prevent some of the cross-site request forgery attacks.
To use the remoting across domain boundaries, CORS (cross-origin resource sharing) needs to be enabled by defining the header \ic{X-Haxe-Remoting} in the \ic{.htaccess}:
\lang{none}\begin{lstlisting}
# Enable CORS
Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Methods: "GET,POST,OPTIONS,DELETE,PUT"
Header set Access-Control-Allow-Headers: X-Haxe-Remoting
\end{lstlisting}
See \href{http://en.wikipedia.org/wiki/Same-origin_policy}{same-origin policy} for more information on this topic.
Also note that this means that the page can't be served directly from the file system \ic{"file:///C:/example/path/index.html"}.
\paragraph{Flash security specifics}
When Flash accesses a server from a different domain, set up a \ic{crossdomain.xml} file on the server, enabling the \ic{X-Haxe} headers.
\lang{xml}\begin{lstlisting}
<cross-domain-policy>
<allow-access-from domain="*"/> <!-- or the appropriate domains -->
<allow-http-request-headers-from domain="*" headers="X-Haxe*"/>
</cross-domain-policy>
\end{lstlisting}
\paragraph{Arguments types are not ensured}
There is no guarantee of any kind that the arguments types will be respected when a method is called using remoting.
That means even if the arguments of function \expr{foo} are typed to \type{Int}, the client will still be able to use strings while calling the method.
This can lead to security issues in some cases. When in doubt, check the argument type when the function is called by using the \expr{Std.is} method.
\section{Unit Testing}
\label{std-unit-testing}
The Haxe Standard Library provides basic unit testing classes from the \href{https://api.haxe.org/haxe/unit/}{haxe.unit} package.
\paragraph{Creating new test cases}
First, create a new class extending \href{https://api.haxe.org/haxe/unit/TestCase.html}{haxe.unit.TestCase} and add own test methods. Every test method name must start with "\ic{test}".
\haxe{assets/UnitTestCase.hx}
\paragraph{Running unit tests}
To run the test, an instance of \href{https://api.haxe.org/haxe/unit/TestRunner.html}{haxe.unit.TestRunner} has to be created. Add the \href{https://api.haxe.org/haxe/unit/TestCase.html}{TestCase} using the \expr{add} method and call \expr{run} to start the test.
\haxe{assets/UnitTestRunner.hx}
The result of the test looks like this:
\lang{none}\begin{lstlisting}
Class: MyTestCase
.
OK 1 tests, 0 failed, 1 success
\end{lstlisting}
\paragraph{Test functions}
The \type{haxe.unit.TestCase} class comes with three test functions.
\begin{description}
\item[\expr{assertEquals(expected, actual)}] Succeeds if \ic{expected} and \ic{actual} are equal
\item[\expr{assertTrue(a)}] Succeeds if \ic{a} is \expr{true}
\item[\expr{assertFalse(a)}] Succeeds if \ic{a} is \expr{false}
\end{description}
\paragraph{Setup and tear down}
To run code before or after the test, override the functions \expr{setup} and \expr{tearDown} in the \expr{TestCase}.
\begin{description}
\item[\expr{setup}] is called before each test runs.
\item[\expr{tearDown}] is called once after all tests are run.
\end{description}
\haxe{assets/UnitTestSetup.hx}
\paragraph{Comparing Complex Objects}
With complex objects it can be difficult to generate expected values to compare to the actual ones. It can also be a problem that \expr{assertEquals} doesn't do a deep comparison. One way around these issues is to use a string as the expected value and compare it to the actual value converted to a string using \expr{Std.string}. Below is a trivial example using an array.
\begin{lstlisting}
public function testArray() {
var actual = [1,2,3];
assertEquals("[1, 2, 3]", Std.string(actual));
}
\end{lstlisting}
\paragraph{Run unit test}
This is an example showing how to run your unit tests (on Neko and Node.js) after compilation using a \tref{HXML}{compiler-usage-hxml}.
\lang{hxml}\begin{lstlisting}
-cp source/main/haxe
-cp source/test/haxe
-main your.package.TestRunnerMain
--each
-neko output/neko/test.n
-cmd neko ./output/neko/test.n
--next
-js output/javascript/test.js
-cmd node ./output/javascript/test.js
\end{lstlisting}
\paragraph{Related content}
\begin{itemize}
\item See the \href{https://api.haxe.org/haxe/unit/}{haxe.unit} package on the API documentation for more details.
\end{itemize}