= Runtime representation

This page describes how OCaml values are represented at runtime in JavaScript.
Understanding this is useful for debugging, writing JavaScript primitives, and
understanding compatibility limitations.

==@@id="overview"@@ Overview

Js_of_ocaml maps OCaml values to JavaScript values as directly as possible
for performance. This differs from the standard OCaml runtime representation.

|= OCaml type |= JavaScript representation |
| {{{int}}} | Number (32-bit) |
| {{{float}}} | Number (unboxed) |
| {{{bool}}} | Number (0 or 1) |
| {{{char}}} | Number (0-255) |
| {{{string}}} | Special string object (MlBytes) or JavaScript string |
| {{{bytes}}} | Special bytes object (MlBytes) |
| {{{array}}} | JavaScript Array with tag |
| {{{tuple}}} | JavaScript Array with tag 0 |
| {{{record}}} | JavaScript Array with tag 0 |
| {{{variant}}} | Number (constant) or Array (with arguments) |
| {{{int32}}} | Number (32-bit) |
| {{{nativeint}}} | Number (32-bit) |
| {{{int64}}} | MlInt64 object |
| {{{bigarray}}} | Ml_Bigarray object |
| {{{exception}}} | Array with exception identity |
| {{{object}}} | Array with method table and instance variables |

==@@id="integers"@@ Integers

OCaml integers are represented as JavaScript numbers. Unlike native OCaml
where integers are 31 or 63 bits (with one bit used for tagging), js_of_ocaml
uses the full 32-bit range.

**Implications**:
* Arithmetic behaves slightly differently at the boundaries
* {{{max_int}}} and {{{min_int}}} have different values than native OCaml

==@@id="floats"@@ Floats

OCaml floats are JavaScript numbers directly.

**Implications**:
* Better performance for float-heavy code
* Polymorphic hash and comparison may give different results than native
* Marshalled floats cannot be unmarshalled by the native OCaml runtime

**Rounding difference**: JavaScript rounds ties away from zero, while native
OCaml (libc) rounds ties to even.

==@@id="booleans"@@ Booleans

OCaml booleans are represented as the numbers 0 ({{{false}}}) and 1 ({{{true}}}),
not JavaScript's {{{true}}} and {{{false}}}. See
<<a_manual chapter="javascript-interop" fragment="conversions"|type conversions>>
for converting between them.

==@@id="bytes"@@ Bytes

OCaml bytes are always represented as MlBytes objects with:
* {{{t}}} — tag indicating the encoding
* {{{l}}} — length in bytes
* {{{c}}} — contents (a JavaScript string or array of bytes)

The contents can be a JavaScript string (more memory efficient) or an array
of bytes (for mutation). The runtime converts between these as needed.

==@@id="strings"@@ Strings

OCaml strings can be represented as:
* A JavaScript string directly (when {{{--enable use-js-string}}} is set, which is the default)
* An MlBytes object (above)

**Important**: Even when represented as a JavaScript string, it encodes a sequence
of bytes, not UTF-16 text. Each character's code point represents one byte (0-255).
Assuming the bytes are UTF-8 encoded text, use {{{Js.string}}} to convert to a
native UTF-16 JavaScript string. To convert back, use {{{Js.to_string}}}.
See <<a_manual chapter="javascript-interop" fragment="conversions"|conversions>>.

==@@id="blocks"@@ Blocks, arrays, records, modules

OCaml heap-allocated values (arrays, tuples, records, variant constructors with
arguments) are represented as JavaScript arrays where:
* Index 0 contains the block tag
* Remaining indices contain the fields

For example, {{{Some 42}}} becomes {{{[0, 42]}}} (tag 0, one field).

==@@id="variants"@@ Variants

* **Constant constructors** (no arguments): represented as integers starting from 0
* **Non-constant constructors**: represented as arrays with a tag

<<code language="ocaml"|
type t =
  | A        (* 0 *)
  | B        (* 1 *)
  | C of int (* [0, n] — tag 0, since it's the first non-constant *)
  | D of int (* [1, n] — tag 1 *)
>>

==@@id="int32"@@ Int32 and Nativeint

{{{Int32.t}}} and {{{Nativeint.t}}} are represented directly as JavaScript
numbers, like regular {{{int}}} values.

Note that {{{nativeint}}} is 32 bits in js_of_ocaml (matching JavaScript's
bitwise operations), not the platform's native word size.

==@@id="int64"@@ Int64

{{{Int64.t}}} values are represented as MlInt64 objects, which store the
value as three numbers: two 24-bit integers (lo, mi) and one 16-bit integer (hi),
since JavaScript numbers cannot represent the full 64-bit range precisely.

==@@id="bigarray"@@ Bigarray

Bigarrays are represented as Ml_Bigarray objects, which use JavaScript Typed
Arrays for storing the underlying data:

|= OCaml kind |= JavaScript Typed Array |
| {{{Float32}}} | {{{Float32Array}}} |
| {{{Float64}}} | {{{Float64Array}}} |
| {{{Int8_signed}}} | {{{Int8Array}}} |
| {{{Int8_unsigned}}} | {{{Uint8Array}}} |
| {{{Int16_signed}}} | {{{Int16Array}}} |
| {{{Int16_unsigned}}} | {{{Uint16Array}}} |
| {{{Int32}}} | {{{Int32Array}}} |
| {{{Int64}}} | Two {{{Int32Array}}}s |
| {{{Int}}} | {{{Int32Array}}} |
| {{{Nativeint}}} | {{{Int32Array}}} |
| {{{Complex32}}} | {{{Float32Array}}} (2 elements per complex) |
| {{{Complex64}}} | {{{Float64Array}}} (2 elements per complex) |
| {{{Char}}} | {{{Uint8Array}}} |

==@@id="exceptions"@@ Exceptions

OCaml exceptions are represented as:
* **Without arguments**: the exception identity directly (a unique object per exception type)
* **With arguments**: a JavaScript array where index 1 is the exception identity and remaining indices contain the arguments

For example, {{{Not_found}}} is just its identity object, while {{{Failure "oops"}}}
becomes {{{[<Failure identity>, "oops"]}}}.

**Note**: OCaml exceptions are not JavaScript {{{Error}}} objects. When an OCaml
exception propagates to JavaScript code, it appears as an array, not an {{{Error}}}.
See <<a_manual chapter="errors"|error handling>> for how to work with exceptions
across the OCaml/JavaScript boundary.

==@@id="objects"@@ Objects

OCaml objects (from the object-oriented layer) are implemented using JavaScript
arrays. They have nothing to do with JavaScript objects. Use the
<<a_manual chapter="ppx"|PPX syntax>> ({{{object%js}}}) to create JavaScript objects.

==@@id="compatibility"@@ Compatibility considerations

Due to representation differences, some operations behave differently:

**Marshalling ({{{Marshal}}} module)**:
* Output may differ from native OCaml
* Do not unmarshal js_of_ocaml output with native OCaml (especially floats)
* Marshalling between js_of_ocaml programs works correctly

**Polymorphic comparison and hashing**:
* Results may differ from native OCaml for floats

== See also

* <<a_manual chapter="javascript-interop" fragment="conversions"|Type conversions>> — Converting between OCaml and JavaScript types
* <<a_manual chapter="linker"|JavaScript primitives>> — Writing custom primitives
