|
| 1 | +# v0.1.0 compatibility |
| 2 | + |
| 3 | +This is a selected ECMAScript-compatible runtime API implemented in Python. |
| 4 | +Method names and the tested behaviors below follow the |
| 5 | +[ECMAScript specification](https://tc39.es/ecma262/). It is not a complete language |
| 6 | +implementation. The native Node.js fixture checks the adopted subset, rather |
| 7 | +than asserting general Test262 conformance. |
| 8 | + |
| 9 | +## Supported API |
| 10 | + |
| 11 | +| Surface | Implemented members | |
| 12 | +| --- | --- | |
| 13 | +| Values | `undefined`, `null`, `NaN`, `Infinity`, `typeof`, `truthy`, `strictEqual`, global `isNaN`, global `isFinite` | |
| 14 | +| `Object` | Mapping/empty construction, dot/bracket data properties, deletion, missing `undefined`, `keys`, `values`, `entries`, `assign`, `fromEntries`, `hasOwn`, `is_`, `toString` | |
| 15 | +| `Array` | Numeric-length/element construction, `of`, iterable `from_`, `isArray`, `length`, bracket access/deletion, `at`, `push`, `pop`, `shift`, `unshift`, `slice`, `splice`, `concat`, `join`, `toString`, `includes`, `indexOf`, `lastIndexOf` | |
| 16 | +| Array callbacks | `map`, `filter`, `forEach`, `some`, `every`, `find`, `findIndex`, `findLast`, `findLastIndex`, `reduce`, `reduceRight` | |
| 17 | +| Array mutation/iteration | `reverse`, `fill`, `sort`, `keys`, `values`, `entries`, Python iteration | |
| 18 | +| `String` | Conversion, `length`, bracket code units, `at`, `charAt`, `charCodeAt`, `codePointAt`, `slice`, `substring`, `indexOf`, `lastIndexOf`, `includes`, `startsWith`, `endsWith`, `split`, `concat`, `repeat`, `trim`, `trimStart`, `trimEnd`, `toLowerCase`, `toUpperCase`, `toString`, `valueOf`, `fromCharCode`, `fromCodePoint` | |
| 19 | +| `Number` | Conversion, `isNaN`, `isFinite`, `isInteger`, `isSafeInteger`, `parseInt`, `parseFloat`, decimal `toString`, `valueOf`, `EPSILON`, `MAX_SAFE_INTEGER`, `MIN_SAFE_INTEGER`, `MAX_VALUE`, `MIN_VALUE`, `NaN`, `POSITIVE_INFINITY`, `NEGATIVE_INFINITY` | |
| 20 | +| `Boolean` | Primitive conversion returning a Python bool | |
| 21 | +| `Math` | `abs`, `floor`, `ceil`, `trunc`, `round`, `sign`, `min`, `max`, `sqrt`, `pow`, `random`; `PI`, `E`, `LN2`, `LN10`, `LOG2E`, `LOG10E`, `SQRT1_2`, `SQRT2` | |
| 22 | +| `JSON` | `parse(text)`, `stringify(value, replacer=undefined, space=undefined)`, `JSONDecodeError` | |
| 23 | +| Global parsing | `parseInt`, `parseFloat` | |
| 24 | +| Companions | `fetch`, `FetchError`, `Headers`, `Request`, `Response`, `Promise`, re-exported unchanged from `fetch_api` | |
| 25 | + |
| 26 | +## Data model and Python boundaries |
| 27 | + |
| 28 | +- `Object` stores own enumerable writable data properties. Keys convert to |
| 29 | + strings. Array-index keys enumerate numerically before other keys in insertion |
| 30 | + order. Deleting and re-adding a non-index key moves it to the end. |
| 31 | +- `Object()`/`Object(None)` create empty objects; `Object(existing)` returns the |
| 32 | + existing Object. Other construction accepts a Python mapping. Primitive |
| 33 | + boxing and prototype/descriptor operations are unsupported. |
| 34 | +- Object-to-primitive conversion uses the built-in data-object representation. |
| 35 | + Custom `valueOf`/`toString` conversion hooks and Symbol.toPrimitive are not |
| 36 | + implemented. |
| 37 | +- Objects preserve supplied child values; passing a nested Python dict/list |
| 38 | + does not recursively convert it. JSON parsing creates runtime containers. |
| 39 | +- Own Object keys shadow ordinary Python attributes. Use `Object.keys(obj)` |
| 40 | + for static methods and brackets for special names such as `__class__`. |
| 41 | + Missing ordinary attributes return `undefined`; Python `hasattr` therefore |
| 42 | + cannot test property presence. Use `Object.hasOwn` or `key in obj`. |
| 43 | +- Arrays store holes separately from explicit undefined values. Length changes |
| 44 | + truncate or expand sparsely. Callback methods skip holes where ECMAScript does; |
| 45 | + find methods and includes observe holes as undefined. `Object.hasOwn(array, i)` |
| 46 | + distinguishes them. `length` is an own, non-enumerable property. |
| 47 | +- Use bracket notation for named Array properties; arbitrary dot property |
| 48 | + assignment and overriding Array methods are outside this subset. Python `in` |
| 49 | + tests property presence, matching JavaScript `in`; use `includes` for values. |
| 50 | +- `Array.from_` accepts Python iterables, including runtime arrays and strings. |
| 51 | + JavaScript array-like objects, custom iterators/Symbol protocols, `thisArg`, |
| 52 | + subclass species and `Symbol.isConcatSpreadable` are not implemented. `concat` |
| 53 | + spreads runtime Array values; ordinary Python lists remain single elements. |
| 54 | +- Callbacks receive JavaScript's arguments, truncated to their inspectable Python |
| 55 | + positional arity. Zero-argument and variadic callbacks work. Uninspectable |
| 56 | + callables receive all arguments; wrap them in a lambda if needed. Keyword-only |
| 57 | + callback parameters and JavaScript `this` binding are unsupported. Exceptions |
| 58 | + propagate once without retrying the callback. |
| 59 | +- `sort` is stable and uses UTF-16 lexicographic order by default. Undefined |
| 60 | + elements follow defined elements; holes follow undefined. Comparators may |
| 61 | + return a number; mutating the sorted array from its comparator is unsupported. |
| 62 | +- String methods and brackets index UTF-16 code units. String iteration visits |
| 63 | + code points. Python `len(string)` and inherited str methods/operators retain |
| 64 | + Python semantics; use `.length` for JavaScript length. Case conversion uses |
| 65 | + Python's Unicode tables, which can differ from a JavaScript engine's version. |
| 66 | +- `Number` is a float subclass with binary64 conversion. Plain Python integers, |
| 67 | + arithmetic, division, modulo, comparisons, `and`/`or`, `==`, container literals |
| 68 | + and inherited methods retain Python behavior. Use explicit helpers for |
| 69 | + JavaScript semantics. No operator rewriting or automatic primitive boxing is |
| 70 | + provided. `Boolean` does not create boxed Boolean objects. |
| 71 | +- Methods can return native Python primitives, including Array `join` and Math |
| 72 | + results. Wrap such results with `String` or `Number` to call their JavaScript |
| 73 | + methods; Python does not automatically box primitive values. |
| 74 | +- Decimal number strings use Python's shortest float representation adjusted to |
| 75 | + JavaScript exponent/fixed-format thresholds. Tested ordinary values agree; |
| 76 | + rare shortest-decimal tie choices may differ between Python and ECMAScript. |
| 77 | + Other number radices and `toFixed`/`toPrecision` are not implemented. |
| 78 | +- Errors use Python `TypeError`, `ValueError` and `JSONDecodeError`; no full |
| 79 | + ECMAScript Error hierarchy or matching message text is promised. Invalid array |
| 80 | + lengths, repeat counts and code points raise ValueError. Invalid JSON numeric |
| 81 | + constants raise ValueError. Unsupported overloads raise TypeError or |
| 82 | + NotImplementedError. |
| 83 | + |
| 84 | +## JSON boundaries |
| 85 | + |
| 86 | +`JavaScript.JSON.parse` creates Object, Array and String values, and binary64 |
| 87 | +Python floats for numbers. Negative zero and integer rounding follow the number |
| 88 | +model. It uses the standard-library decoder's numeric hooks because standalone |
| 89 | +`JSON.parse` intentionally preserves Python integers. The companion's serializer |
| 90 | +provides string escaping; its public decoder exception type is shared. |
| 91 | + |
| 92 | +`stringify` emits compact JSON by default, orders object keys, writes non-finite |
| 93 | +numbers as null, omits undefined/callable object properties, and writes them as |
| 94 | +null in arrays. Top-level undefined/callables return undefined. Shared references |
| 95 | +are allowed; cycles raise TypeError. A numeric/string `space` is capped at ten |
| 96 | +characters. Lone surrogates are escaped. Plain Python mappings/lists/tuples are |
| 97 | +accepted as a convenience. |
| 98 | + |
| 99 | +Revivers, replacer functions/property lists, `toJSON` hooks, symbols and BigInt |
| 100 | +are unsupported. A non-null replacer or callable own toJSON hook raises |
| 101 | +NotImplementedError. Byte input is a standalone JSON extension and is rejected |
| 102 | +by the runtime facade. The facade does not change standalone `import JSON` or |
| 103 | +Fetch's `response.json()` result types. |
| 104 | + |
| 105 | +## Fetch and Promise boundaries |
| 106 | + |
| 107 | +The [standalone Fetch documentation](https://github.com/outside-labs/JavaScript-Fetch-API) |
| 108 | +is the complete contract. Core HTTP(S) requests, buffered bodies, headers, |
| 109 | +redirects, promises and await work. Browser policies, streaming, abort, cookies |
| 110 | +and browser globals are not provided. Promise callbacks use a background Python |
| 111 | +thread, not JavaScript microtask scheduling. Rejections use Python exceptions; |
| 112 | +handlers return values or Promise objects, rather than raw Python coroutines. |
| 113 | + |
| 114 | +## Future translation |
| 115 | + |
| 116 | +The runtime deliberately keeps recognizable call shapes, but a Python program |
| 117 | +using it is not automatically safe to translate. The future translator must |
| 118 | +resolve bindings and validate an approved Python AST subset. It must diagnose |
| 119 | +unsupported operators and Python-only constructs before emitting JavaScript. |
0 commit comments