Stability​

JaiScript 0.5 is a beta: the language and its contract are complete, and it stays 0.x until 1.0. This page sorts the language and the embedding API into what is settled and what a 0.x release may still change.

A settled rule is the contract. Where the implementation departs from one, the implementation is the bug and changes to match; the rule stays. Everything under May change can move in any 0.x release, and anything this page does not mention counts as may change.

Settled: the language​

  • Value semantics. Assignment, by-value parameters and returns copy. Aliasing exists only where a declaration asks for it: a T& reference or a shared_ptr<T> handle. A reference that is not const binds storage of its own type (int& refuses a float variable); a const T& declaration also binds a temporary, and a value of another type that a T declaration takes binds a T made from it, which lives as long as the reference, as in C++. A store through a reference follows the target's declared type. Copying an instance, array or map copies its values and shares its handles, as C++ does. Value Semantics
  • The type ladder. A typed slot (int x) is enforced, auto is inferred and then enforced, var stays dynamic. The Type Ladder
  • Strict checking, always. The checker runs before anything executes. A typed-to-typed mistake it can see fails the load; a value that came through var fails at run time, where try/catch sees it. [[warn_mode]] and [[strict_mode]] apply to a region, extern declares a name the host provides, and cast<T>, cast<T&> and cast<shared_ptr<T>> are the explicit boundary. A program that runs clean is never rejected. Strict checking
  • One conversion table. Declarations, assignments, by-value parameters, typed returns and field stores all convert through the same table, with at most one converting constructor per conversion. The table
  • Typed containers. array<T> and map<K, V> hold what they declare: a store converts by the element rules or raises, nothing demotes silently, array<var> is the dynamic form, a copy into a var or a slice keeps the element type, map keys never coerce, array<Base> holds subclasses, and handle-typed elements take null. Typed containers
  • Integers and chars. int is 64-bit. Overflow raises a catchable error, or wraps in a build that defines JAISCRIPT_WRAP_ON_OVERFLOW, and is never undefined behavior; shifts operate on the 64-bit pattern. char is a byte that enters arithmetic as 0..255 and converts only through cast. Operators
  • Evaluation order. A call evaluates its callee, then its arguments left to right, and a by-value argument keeps the value it had when it was evaluated. A binary operator evaluates its left operand first. Errors surface in the same order. Evaluation order
  • Captures. [] captures nothing; named lists are exhaustive; an anonymous function(...) expression is the JavaScript form and captures as [&] does. A lambda captures the enclosing function's locals and parameters when it is created: a copy under [=] or [x], the variable itself under [&] or [&x], and a shared capture outlives its frame; [this] captures the instance itself. A global is never captured implicitly; the lambda reads it when it runs. A named function or coroutine declared inside a function body captures nothing: naming an enclosing local in it is a parse error. Lambdas and captures
  • Names and scopes. Blocks scope as in C++. Inside a method a bare name resolves to a local, then a member, then a global. A namespace is a scope, and a catch variable belongs to its block. Name resolution
  • Modules. include is textual and runs each time it is reached. import runs a module once per engine and may appear wherever a statement may; reached again, it re-runs a module whose file has changed on disk. Modules
  • Access control. Two levels: public: reaches everyone, private: reaches the declaring class and every class derived from it, and protected: is a synonym of private:. Access control
  • Statements and expressions. switch breaks by default and fallthrough; continues. ++ and -- work on every lvalue. An assignment expression's value is the value the destination stored. A declaration without an initializer takes its type's default. == compares arrays, maps and instances member by member (a user operator== first), and same_object asks identity. Maps iterate and serialize in key order.
  • Strings. A string is bytes; utf8_string holds the same bytes and counts characters. UTF-8 text
  • Coroutines. coroutine prefixes any function or method spelling; a coroutine runs to each yield and is driven by a range-for or advanced with next()/next(n). Coroutines
  • Hot reload. Redefining a class migrates its live instances in place, and migration never fails. A source that does not parse or check does not load, and the previous definition keeps running. Hot Reload
  • Library and edge rules. contains, index_of, has and a switch case compare with the language's == (switch (1) matches case 1.0). to_string(x) uses a class's own to_string(). 0.0 and -0.0 are one map key. A compound assignment on a missing map key starts from the value type's default. A JSON round trip keeps chars and non-string map keys, and from_json propagates an error from the class's constructor. A bare throw; with no active exception raises the catchable Bare throw with no active exception thrown. A compound assignment with an operand of the wrong type raises one text at every destination. A derived value in a base-typed slot keeps its class (no slicing). Language rules
  • Operators and methods. A script class overloads binary + - * / % & | ^ << >> < > <= >= == != <=>, unary - + ~, and =, [] and (): C++'s set less && || , -> new delete, !, ++, -- and the compound assignments, and a op= b is a = a op b through operator op. Truthiness is bool to_bool(), and !v is !to_bool(v). A method is called or bound: obj.method without a call is an error, and reflect::bind(obj, "name") makes the callable. Operators
  • Two backends, one behavior. The interpreter and the VM produce the same values, output and error text for every program. The one exception is recursion depth: where non-tail recursion meets the recursion error, and the VM reusing a bare self tail call's frame. The parity contract

Settled: embedding​

  • An engine is an object. jai::engine::make() returns one, and there is no global state: engines share nothing and run on separate threads without locks. Quickstart
  • Registration is the allowlist. Beyond the core builtins, a script reaches only the host functions, globals and classes registered on its engine. Three doors
  • Per-engine choices. The backend is chosen before an engine's first execute; the check mode can change at any time. Backends
  • Limits a script cannot catch. A script cannot catch its way past the execution budget or the memory cap. Safety Rails
  • Script errors reach the host. Each way of calling script has one failure route: a call that returns a checked_result (a script function called through as_function(), a coroutine handle's next) reports every failure in the result, while execute and execute_file throw (jai::parse_error, jai::static_check_error or jai::runtime_error) and a std::function from convert_script_function throws a jai::runtime_error; a signal receiver's error throws out of the emit. Calling script from C++
  • Precompiled equals source. Running a .jaibite behaves exactly like executing the source it was parsed from. jaibite

May change​

  • The C++ API's exact shape. Signatures, header paths and helper type names can change between 0.x releases. The model in the list above stays. The documented C++ API is the contract: a call the embedding pages or the reference describe keeps its meaning, and the engine's public members are exactly the calls they describe. What lives in a detail namespace, jai::detail::engine_access (the library's door to the engine's own hooks) among it, is the library's own and can change or go in any release. Embedding
  • Performance. What each construct costs, which shapes the VM compiles to its fastest forms, and every figure on the Performance page. A performance change never changes what a program computes. Performance
  • The .jaibite format version. It moves whenever the parser's output changes, and a bite loads only in a build with the same version. Ship bites built by the release that loads them; the automatic disk cache re-parses a stale cache file by itself. Shipping bites
  • What the checker reports at load. A mistake that raises at run time can become a load error in a later release. A program that runs clean keeps loading clean. Precision map
  • Error text wording. The texts in the diagnostics catalog can be reworded. Both backends always raise the same text. Diagnostics
  • Serialized data. The layout to_json, to_binary and the C++ archives write. Data saved by one 0.x release is not promised to load in the next. Serialization
  • Parallel admission. Which parallel_for, parallel_transform and parallel_reduce bodies the engine admits; a body it does not admit is refused with a catchable error, never run serially. Results stay deterministic at any worker count. Parallel Loops
  • String mutators. to_upper, trim and the other mutating string methods change their receiver in place. Observers vs mutators
  • Script-extensible objects. extend(obj, Class) and its host side. extend
  • Absent syntax. There are no coroutine lambdas. They may be added.