|
|
|
=====================
|
|
|
|
API design guidelines
|
|
|
|
=====================
|
|
|
|
|
|
|
|
Introduction
|
|
|
|
============
|
|
|
|
|
|
|
|
A consistent API design makes it easier to write applications: it should be
|
|
|
|
easy to remember API call naming, argument order, and other conventions, to
|
|
|
|
make it as easy as possible to write error-free code.
|
|
|
|
|
|
|
|
API design decisions involve several important concerns:
|
|
|
|
|
|
|
|
* Consistency: easy to remember API calls and arguments.
|
|
|
|
|
|
|
|
* Versioning: easy to extend the API, avoid dead ends in design.
|
|
|
|
|
|
|
|
* Footprint: minimize code footprint of Duktape internals and application
|
|
|
|
call site.
|
|
|
|
|
|
|
|
* Performance: some API designs allow better performance than others.
|
|
|
|
|
|
|
|
This document provides some notes on common API design issues and trade-offs.
|
|
|
|
The document is not intended to be complete, but just cover practical issues
|
|
|
|
as they come up.
|
|
|
|
|
|
|
|
Naming
|
|
|
|
======
|
|
|
|
|
|
|
|
API calls
|
|
|
|
---------
|
|
|
|
|
|
|
|
All API calls are lowercase, underscore separated, and begin with the prefix
|
|
|
|
``duk_``. Duktape internals, not part of the public API, also have calls
|
|
|
|
matching this convention. The public API calls are documented in the public
|
|
|
|
API header (actually a segment of ``duktape.h`` in the distributable) and the
|
|
|
|
API documentation.
|
|
|
|
|
|
|
|
Existing convention for operating on value stack items:
|
|
|
|
|
|
|
|
* ``duk_get_XXX()`` gets a value without modifying the value on the stack.
|
|
|
|
If the value doesn't match expected type, some default value (like NULL
|
|
|
|
or zero) is returned.
|
|
|
|
|
|
|
|
* ``duk_require_XXX()`` gets a value without modifying the value on the stack.
|
|
|
|
If the value doesn't match expected type, an error is thrown.
|
|
|
|
|
|
|
|
* ``duk_to_XXX()`` coerces a value in-place into desired type.
|
|
|
|
|
|
|
|
Varargs API calls:
|
|
|
|
|
|
|
|
* Named with a trailing ``_va()``, for example ``duk_error_va()``.
|
|
|
|
|
|
|
|
- Existing exception: ``duk_push_sprintf()``.
|
|
|
|
|
|
|
|
Existing convention for safe/protected calls:
|
|
|
|
|
|
|
|
* All calls are unsafe by default, i.e. may throw errors.
|
|
|
|
|
|
|
|
* ``duk_pXXX()`` calls are protected, i.e. they catch errors. API call
|
|
|
|
return value indicates success or error, and error is typically passed
|
|
|
|
via the value stack.
|
|
|
|
|
|
|
|
* ``duk_safe_XXX()`` calls are safe, i.e. don't throw, but they don't
|
|
|
|
provide a success/error indication.
|
|
|
|
|
|
|
|
- Existing exception: ``duk_safe_call()`` doesn't currently match this
|
|
|
|
convention: it's a protected call (catches an error and returns a
|
|
|
|
success/failure indicator).
|
|
|
|
|
|
|
|
Arguments
|
|
|
|
---------
|
|
|
|
|
|
|
|
Lowercase, underscore separated. No other strict guidelines, some current
|
|
|
|
names:
|
|
|
|
|
|
|
|
* ``ctx``
|
|
|
|
|
|
|
|
* ``idx``, ``obj_index``, ``from_index``, ``to_index``, ``index1``, ``index2``
|
|
|
|
|
|
|
|
* ``arr_index``
|
|
|
|
|
|
|
|
* ``flags``, ``enum_flags``
|
|
|
|
|
|
|
|
* ``len``, ``ptr``, ``key``
|
|
|
|
|
|
|
|
Avoid the argument names:
|
|
|
|
|
|
|
|
* ``index``: conflicts with a function from strings.h.
|
|
|
|
|
|
|
|
Typing
|
|
|
|
======
|
|
|
|
|
|
|
|
Return values
|
|
|
|
-------------
|
|
|
|
|
|
|
|
* ``duk_bool_t`` for true/false return values.
|
|
|
|
|
|
|
|
* ``duk_idx_t`` for value stack indices.
|
|
|
|
|
|
|
|
* ``duk_int_t`` for general return values which need to express both signed
|
|
|
|
and unsigned values.
|
|
|
|
|
|
|
|
* ``duk_ret_t`` for Duktape/C function return values.
|
|
|
|
|
|
|
|
Arguments
|
|
|
|
---------
|
|
|
|
|
|
|
|
* ``duk_idx_t`` for value stack indices.
|
|
|
|
|
|
|
|
* ``duk_uarridx_t`` for ECMAScript array indices.
|
|
|
|
|
|
|
|
* ``duk_size_t`` for byte lengths (strings, buffers, etc).
|
|
|
|
|
|
|
|
* ``void *`` for generic void pointers.
|
|
|
|
|
|
|
|
* ``const char *`` for string data pointers.
|
|
|
|
|
|
|
|
* ``void *`` for buffer data pointers.
|
|
|
|
|
|
|
|
Varargs API calls
|
|
|
|
-----------------
|
|
|
|
|
|
|
|
Vararg API call variants are provided when it's awkward for an application to
|
|
|
|
use non-vararg API call variants. For example, it'd be awkward to construct a
|
|
|
|
formatted string first and then push it using ``duk_push_string()``, which is
|
|
|
|
why ``duk_push_sprintf()`` is provided.
|
|
|
|
|
|
|
|
Flags vs. variants
|
|
|
|
==================
|
|
|
|
|
|
|
|
A common issue is whether there should be an API call which provides a wide
|
|
|
|
variety of behavior controlled via flags, or a corresponding set of invididual
|
|
|
|
API calls.
|
|
|
|
|
|
|
|
Individual API calls are generally favored in the API:
|
|
|
|
|
|
|
|
* They make call sites easier in applications as there's no need to remember
|
|
|
|
and combine flag defines.
|
|
|
|
|
|
|
|
* Individual API calls can be easily mapped to either individual internal
|
|
|
|
implementations, or to internal helpers which take flags, whichever is most
|
|
|
|
appropriate for the internal implementation. In contrast convering an API
|
|
|
|
call taking flags into individual internal calls is difficult: while it's
|
|
|
|
possible to check the flags in a macro call site, the macro will quite easily
|
|
|
|
end up evaluating flags multiple times which violates API macro guarantees.
|
|
|
|
|
|
|
|
* They enable best performance because no flags field is built or decoded.
|
|
|
|
|
|
|
|
* Even when improved calls are added to the API, individual calls are easy to
|
|
|
|
map to new providers, so versioning is easy even though some API clutter is
|
|
|
|
then easily left behind.
|
|
|
|
|
|
|
|
There are situations where fewer calls with behavioral flags are preferable:
|
|
|
|
|
|
|
|
* If the API call provides a lot of functionality, so that the set of
|
|
|
|
individual call variants would be very large, flags may be more appropriate.
|
|
|
|
|
|
|
|
* If the API call behavior is likely to evolve new control flags over time,
|
|
|
|
flags may be more appropriate.
|
|
|
|
|
|
|
|
For example, ``duk_def_prop()`` fits both of these criteria. The downside of
|
|
|
|
using flags is that:
|
|
|
|
|
|
|
|
* It's not easy to decode the flags in an API macro and call invididual
|
|
|
|
internal implementations if that would otherwise be convenient.
|
|
|
|
|
|
|
|
* Passing in flags has a small performance cost, and decoding the flags in
|
|
|
|
the call target has a relatively larger performance cost.
|