Merge branch 'improve_compile_error'
This commit is contained in:
144
analyze.cm
Normal file
144
analyze.cm
Normal file
@@ -0,0 +1,144 @@
|
||||
// analyze.cm — Static analysis over index data.
|
||||
//
|
||||
// All functions take an index object (from index.cm) and return structured results.
|
||||
// Does not depend on streamline — operates purely on source-semantic data.
|
||||
|
||||
var analyze = {}
|
||||
|
||||
// Find all references to a name, with optional scope filter.
|
||||
// scope: "top" (enclosing == null), "fn" (enclosing != null), null (all)
|
||||
analyze.find_refs = function(idx, name, scope) {
|
||||
var hits = []
|
||||
var i = 0
|
||||
var ref = null
|
||||
while (i < length(idx.references)) {
|
||||
ref = idx.references[i]
|
||||
if (ref.name == name) {
|
||||
if (scope == null) {
|
||||
hits[] = ref
|
||||
} else if (scope == "top" && ref.enclosing == null) {
|
||||
hits[] = ref
|
||||
} else if (scope == "fn" && ref.enclosing != null) {
|
||||
hits[] = ref
|
||||
}
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
return hits
|
||||
}
|
||||
|
||||
// Find all <name>.<property> usage patterns (channel analysis).
|
||||
// Only counts unshadowed uses (name not declared as local var in scope).
|
||||
analyze.channels = function(idx, name) {
|
||||
var channels = {}
|
||||
var summary = {}
|
||||
var i = 0
|
||||
var cs = null
|
||||
var callee = null
|
||||
var prop = null
|
||||
var prefix_dot = name + "."
|
||||
while (i < length(idx.call_sites)) {
|
||||
cs = idx.call_sites[i]
|
||||
callee = cs.callee
|
||||
if (callee != null && starts_with(callee, prefix_dot)) {
|
||||
prop = text(callee, length(prefix_dot), length(callee))
|
||||
if (channels[prop] == null) {
|
||||
channels[prop] = []
|
||||
}
|
||||
channels[prop][] = {span: cs.span}
|
||||
if (summary[prop] == null) {
|
||||
summary[prop] = 0
|
||||
}
|
||||
summary[prop] = summary[prop] + 1
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
return {channels: channels, summary: summary}
|
||||
}
|
||||
|
||||
// Find declarations by name, with optional kind filter.
|
||||
// kind: "var", "def", "fn", "param", or null (any)
|
||||
analyze.find_decls = function(idx, name, kind) {
|
||||
var hits = []
|
||||
var i = 0
|
||||
var sym = null
|
||||
while (i < length(idx.symbols)) {
|
||||
sym = idx.symbols[i]
|
||||
if (sym.name == name) {
|
||||
if (kind == null || sym.kind == kind) {
|
||||
hits[] = sym
|
||||
}
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
return hits
|
||||
}
|
||||
|
||||
// Find intrinsic usage by name.
|
||||
analyze.find_intrinsic = function(idx, name) {
|
||||
var hits = []
|
||||
var i = 0
|
||||
var ref = null
|
||||
if (idx.intrinsic_refs == null) return hits
|
||||
while (i < length(idx.intrinsic_refs)) {
|
||||
ref = idx.intrinsic_refs[i]
|
||||
if (ref.name == name) {
|
||||
hits[] = ref
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
return hits
|
||||
}
|
||||
|
||||
// Call sites with >4 args — always a compile error (max arity is 4).
|
||||
analyze.excess_args = function(idx) {
|
||||
var hits = []
|
||||
var i = 0
|
||||
var cs = null
|
||||
while (i < length(idx.call_sites)) {
|
||||
cs = idx.call_sites[i]
|
||||
if (cs.args_count > 4) {
|
||||
hits[] = {span: cs.span, callee: cs.callee, args_count: cs.args_count}
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
return hits
|
||||
}
|
||||
|
||||
// Extract module export shape from index data (for cross-module analysis).
|
||||
analyze.module_summary = function(idx) {
|
||||
var exports = {}
|
||||
var i = 0
|
||||
var j = 0
|
||||
var exp = null
|
||||
var sym = null
|
||||
var found = false
|
||||
if (idx.exports == null) return {exports: exports}
|
||||
while (i < length(idx.exports)) {
|
||||
exp = idx.exports[i]
|
||||
found = false
|
||||
if (exp.symbol_id != null) {
|
||||
j = 0
|
||||
while (j < length(idx.symbols)) {
|
||||
sym = idx.symbols[j]
|
||||
if (sym.symbol_id == exp.symbol_id) {
|
||||
if (sym.kind == "fn" && sym.params != null) {
|
||||
exports[exp.name] = {type: "function", arity: length(sym.params)}
|
||||
} else {
|
||||
exports[exp.name] = {type: sym.kind}
|
||||
}
|
||||
found = true
|
||||
break
|
||||
}
|
||||
j = j + 1
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
exports[exp.name] = {type: "unknown"}
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
return {exports: exports}
|
||||
}
|
||||
|
||||
return analyze
|
||||
2
bench.ce
2
bench.ce
@@ -6,7 +6,7 @@ var fd = use('fd')
|
||||
var time = use('time')
|
||||
var json = use('json')
|
||||
var blob = use('blob')
|
||||
var os = use('os')
|
||||
var os = use('internal/os')
|
||||
var testlib = use('internal/testlib')
|
||||
var math = use('math/radians')
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
// Compiles (if needed) and benchmarks a module via both VM and native dylib.
|
||||
// Reports median/mean timing per benchmark + speedup ratio.
|
||||
|
||||
var os = use('os')
|
||||
var os = use('internal/os')
|
||||
var fd = use('fd')
|
||||
|
||||
if (length(args) < 1) {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// encoders.cm — nota/wota/json encode+decode benchmark
|
||||
// Isolates per-type bottlenecks across all three serializers.
|
||||
|
||||
var nota = use('nota')
|
||||
var wota = use('wota')
|
||||
var nota = use('internal/nota')
|
||||
var wota = use('internal/wota')
|
||||
var json = use('json')
|
||||
|
||||
// --- Test data shapes ---
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
var nota = use('nota')
|
||||
var os = use('os')
|
||||
var nota = use('internal/nota')
|
||||
var os = use('internal/os')
|
||||
var io = use('fd')
|
||||
var json = use('json')
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
var wota = use('wota');
|
||||
var os = use('os');
|
||||
var wota = use('internal/wota');
|
||||
var os = use('internal/os');
|
||||
|
||||
var i = 0
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
var wota = use('wota');
|
||||
var nota = use('nota');
|
||||
var wota = use('internal/wota');
|
||||
var nota = use('internal/nota');
|
||||
var json = use('json');
|
||||
var jswota = use('jswota')
|
||||
var os = use('os');
|
||||
var os = use('internal/os');
|
||||
|
||||
if (length(arg) != 2) {
|
||||
log.console('Usage: cell benchmark_wota_nota_json.ce <LibraryName> <ScenarioName>');
|
||||
|
||||
@@ -1486,7 +1486,7 @@
|
||||
["setarg", 11, 1, 9, 10, 16],
|
||||
["invoke", 11, 9, 10, 16],
|
||||
["move", 11, 9, 10, 16],
|
||||
["access", 9, "crypto", 11, 24],
|
||||
["access", 9, "internal_crypto", 11, 24],
|
||||
["frame", 12, 2, 1, 11, 14],
|
||||
["null", 13, 11, 14],
|
||||
["setarg", 12, 0, 13, 11, 14],
|
||||
|
||||
8
build.cm
8
build.cm
@@ -7,9 +7,9 @@
|
||||
// Build.build_static(packages, target, output) - Build static binary
|
||||
|
||||
var fd = use('fd')
|
||||
var crypto = use('crypto')
|
||||
var crypto = use('internal/crypto')
|
||||
var blob = use('blob')
|
||||
var os = use('os')
|
||||
var os = use('internal/os')
|
||||
var toolchains = use('toolchains')
|
||||
var shop = use('internal/shop')
|
||||
var pkg_tools = use('package')
|
||||
@@ -718,7 +718,7 @@ Build.compile_native = function(src_path, target, buildtype, pkg) {
|
||||
// Compile QBE runtime stubs if needed
|
||||
var rc = null
|
||||
if (!fd.is_file(rt_o_path)) {
|
||||
qbe_rt_path = shop.get_package_dir('core') + '/qbe_rt.c'
|
||||
qbe_rt_path = shop.get_package_dir('core') + '/src/qbe_rt.c'
|
||||
rc = os.system(cc + san_flags + ' -c ' + qbe_rt_path + ' -o ' + rt_o_path + ' -fPIC')
|
||||
if (rc != 0) {
|
||||
print('QBE runtime stubs compilation failed'); disrupt
|
||||
@@ -790,7 +790,7 @@ Build.compile_native_ir = function(optimized, src_path, opts) {
|
||||
// Compile QBE runtime stubs if needed
|
||||
var rc = null
|
||||
if (!fd.is_file(rt_o_path)) {
|
||||
qbe_rt_path = shop.get_package_dir('core') + '/qbe_rt.c'
|
||||
qbe_rt_path = shop.get_package_dir('core') + '/src/qbe_rt.c'
|
||||
rc = os.system(cc + san_flags + ' -c ' + qbe_rt_path + ' -o ' + rt_o_path + ' -fPIC')
|
||||
if (rc != 0) {
|
||||
print('QBE runtime stubs compilation failed'); disrupt
|
||||
|
||||
@@ -2,8 +2,8 @@ var cellfs = {}
|
||||
|
||||
var fd = use('fd')
|
||||
var miniz = use('miniz')
|
||||
var qop = use('qop')
|
||||
var wildstar = use('wildstar')
|
||||
var qop = use('internal/qop')
|
||||
var wildstar = use('internal/wildstar')
|
||||
|
||||
var mounts = []
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
var build = use('build')
|
||||
var fd_mod = use('fd')
|
||||
var os = use('os')
|
||||
var os = use('internal/os')
|
||||
var json = use('json')
|
||||
var time = use('time')
|
||||
|
||||
|
||||
6
diff.ce
6
diff.ce
@@ -11,9 +11,9 @@ var time = use('time')
|
||||
|
||||
var _args = args == null ? [] : args
|
||||
|
||||
var analyze = use('os').analyze
|
||||
var run_ast_fn = use('os').run_ast_fn
|
||||
var run_ast_noopt_fn = use('os').run_ast_noopt_fn
|
||||
var analyze = use('internal/os').analyze
|
||||
var run_ast_fn = use('internal/os').run_ast_fn
|
||||
var run_ast_noopt_fn = use('internal/os').run_ast_noopt_fn
|
||||
|
||||
if (!run_ast_noopt_fn) {
|
||||
log.console("error: run_ast_noopt_fn not available (rebuild bootstrap)")
|
||||
|
||||
@@ -52,10 +52,13 @@ Where:
|
||||
Examples:
|
||||
- `mypackage/math.c` -> `js_mypackage_math_use`
|
||||
- `gitea.pockle.world/john/lib/render.c` -> `js_gitea_pockle_world_john_lib_render_use`
|
||||
- `mypackage/internal/helpers.c` -> `js_mypackage_internal_helpers_use`
|
||||
- `mypackage/game.ce` (AOT actor) -> `js_mypackage_game_program`
|
||||
|
||||
Actor files (`.ce`) use the `_program` suffix instead of `_use`.
|
||||
|
||||
Internal modules (in `internal/` subdirectories) follow the same convention — the `internal` directory name becomes part of the symbol. For example, `internal/os.c` in the core package has the symbol `js_core_internal_os_use`.
|
||||
|
||||
**Note:** Having both a `.cm` and `.c` file with the same stem at the same scope is a build error.
|
||||
|
||||
## Required Headers
|
||||
|
||||
@@ -72,6 +72,7 @@ cell streamline --stats <file.ce|file.cm> # summary stats per function
|
||||
cell streamline --ir <file.ce|file.cm> # human-readable IR
|
||||
cell streamline --check <file.ce|file.cm> # warnings only
|
||||
cell streamline --types <file.ce|file.cm> # IR with type annotations
|
||||
cell streamline --diagnose <file.ce|file.cm> # compile-time diagnostics
|
||||
```
|
||||
|
||||
| Flag | Description |
|
||||
@@ -81,6 +82,7 @@ cell streamline --types <file.ce|file.cm> # IR with type annotations
|
||||
| `--ir` | Human-readable canonical IR (same format as `ir_report.ce`) |
|
||||
| `--check` | Warnings only (e.g. `nr_slots > 200` approaching 255 limit) |
|
||||
| `--types` | Optimized IR with inferred type annotations per slot |
|
||||
| `--diagnose` | Run compile-time diagnostics (type errors and warnings) |
|
||||
|
||||
Flags can be combined.
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ weight: 85
|
||||
type: "docs"
|
||||
---
|
||||
|
||||
Nota is a binary message format developed for use in the Procession Protocol. It provides a compact, JSON-like encoding that supports blobs, text, arrays, records, numbers, and symbols.
|
||||
Nota is a binary message format developed for use in the Procession Protocol. It provides a compact, JSON-like encoding that supports blobs, text, arrays, records, numbers, and symbols. Nota is an internal module: `use('internal/nota')`.
|
||||
|
||||
Nota stands for Network Object Transfer Arrangement.
|
||||
|
||||
|
||||
@@ -230,4 +230,4 @@ If `shop.toml` is missing or has no `[policy]` section, all methods are enabled
|
||||
| `internal/os.c` | OS intrinsics: dylib ops, internal symbol lookup, embedded modules |
|
||||
| `package.cm` | Package directory detection, alias resolution, file listing |
|
||||
| `link.cm` | Development link management (link.toml read/write) |
|
||||
| `boot/*.cm.mcode` | Pre-compiled pipeline seeds (tokenize, parse, fold, mcode, bootstrap) |
|
||||
| `boot/*.cm.mcode` | Pre-compiled pipeline seeds (tokenize, parse, fold, mcode, streamline, bootstrap) |
|
||||
|
||||
@@ -69,6 +69,7 @@ Optimizes the Mcode IR through a series of independent passes. Operates per-func
|
||||
6. **Move elimination**: Removes self-moves (`move a, a`).
|
||||
7. **Unreachable elimination**: Nops dead code after `return` until the next label.
|
||||
8. **Dead jump elimination**: Removes jumps to the immediately following label.
|
||||
9. **Compile-time diagnostics** (optional): When `_warn` is set on the mcode input, emits errors for provably wrong operations (storing named property on array, invoking null, etc.) and warnings for suspicious patterns (named property access on array/text). The engine aborts compilation if any error-severity diagnostics are emitted.
|
||||
|
||||
See [Streamline Optimizer](streamline.md) for detailed pass descriptions.
|
||||
|
||||
@@ -134,6 +135,7 @@ Seeds are used during cold start (empty cache) to compile the pipeline modules f
|
||||
| `mcode.ce --pretty` | Print raw Mcode IR before streamlining |
|
||||
| `streamline.ce --types` | Print streamlined IR with type annotations |
|
||||
| `streamline.ce --stats` | Print IR after streamlining with before/after stats |
|
||||
| `streamline.ce --diagnose` | Print compile-time diagnostics (type errors and warnings) |
|
||||
|
||||
## Test Files
|
||||
|
||||
@@ -146,3 +148,4 @@ Seeds are used during cold start (empty cache) to compile the pipeline modules f
|
||||
| `qbe_test.ce` | End-to-end QBE IL generation |
|
||||
| `test_intrinsics.cm` | Inlined intrinsic opcodes (is_array, length, push, etc.) |
|
||||
| `test_backward.cm` | Backward type propagation for parameters |
|
||||
| `tests/compile.cm` | Compile-time diagnostics (type errors and warnings) |
|
||||
|
||||
@@ -178,6 +178,36 @@ Removes `jump L` instructions where `L` is the immediately following label (skip
|
||||
|
||||
**Nop prefix:** `_nop_dj_`
|
||||
|
||||
### 9. diagnose_function (compile-time diagnostics)
|
||||
|
||||
Optional pass that runs when `_warn` is set on the mcode input. Performs a forward type-tracking scan and emits diagnostics for provably wrong operations. Diagnostics are collected in `ir._diagnostics` as `{severity, file, line, col, message}` records.
|
||||
|
||||
This pass does not modify instructions — it only emits diagnostics.
|
||||
|
||||
**Errors** (compilation is aborted):
|
||||
|
||||
| Pattern | Message |
|
||||
|---------|---------|
|
||||
| `store_field` on T_ARRAY | storing named property on array |
|
||||
| `store_index` on T_RECORD | storing numeric index on record |
|
||||
| `store_field` / `store_index` on T_TEXT | storing property/index on text |
|
||||
| `push` on T_TEXT / T_RECORD | push on text/record |
|
||||
| `invoke` on T_NULL / T_INT / T_FLOAT / T_NUM / T_TEXT / T_BOOL / T_ARRAY | invoking null/number/text/bool/array |
|
||||
| arity mismatch (module imports only) | function expects N arguments, got M |
|
||||
|
||||
**Warnings** (compilation continues):
|
||||
|
||||
| Pattern | Message |
|
||||
|---------|---------|
|
||||
| `load_field` on T_ARRAY | named property access on array |
|
||||
| `load_field` on T_TEXT | named property access on text |
|
||||
| `load_dynamic` with T_TEXT key on T_RECORD | text key on record |
|
||||
| `load_dynamic` with T_RECORD / T_ARRAY / T_BOOL / T_NULL key on T_RECORD | record/array/bool/null key on record |
|
||||
|
||||
The engine (`internal/engine.cm`) prints all diagnostics and aborts compilation if any have severity `"error"`. Warnings are printed but do not block compilation.
|
||||
|
||||
**Nop prefix:** none (diagnostics only, does not modify instructions)
|
||||
|
||||
## Pass Composition
|
||||
|
||||
All passes run in sequence in `optimize_function`:
|
||||
@@ -191,6 +221,7 @@ simplify_booleans
|
||||
eliminate_moves
|
||||
eliminate_unreachable
|
||||
eliminate_dead_jumps
|
||||
diagnose_function → optional, when _warn is set
|
||||
```
|
||||
|
||||
Each pass is independent and can be commented out for testing or benchmarking.
|
||||
|
||||
@@ -192,6 +192,36 @@ Failures saved to tests/fuzz_failures/
|
||||
|
||||
Saved failure files are valid `.cm` modules that can be run directly or added to the test suite.
|
||||
|
||||
## Compile-Time Diagnostics Tests
|
||||
|
||||
The `tests/compile.cm` test suite verifies that the type checker catches provably wrong operations at compile time. It works by compiling source snippets through the pipeline with `_warn` enabled and checking that the expected diagnostics are emitted.
|
||||
|
||||
```javascript
|
||||
var shop = use('internal/shop')
|
||||
var streamline = use('streamline')
|
||||
|
||||
function get_diagnostics(src) {
|
||||
fd.slurpwrite(tmpfile, stone(blob(src)))
|
||||
var compiled = shop.mcode_file(tmpfile)
|
||||
compiled._warn = true
|
||||
var optimized = streamline(compiled)
|
||||
if (optimized._diagnostics == null) return []
|
||||
return optimized._diagnostics
|
||||
}
|
||||
```
|
||||
|
||||
The suite covers:
|
||||
- **Store errors**: storing named property on array, numeric index on record, property/index on text, push on text/record
|
||||
- **Invoke errors**: invoking null, number, text
|
||||
- **Warnings**: named property access on array/text, record key on record
|
||||
- **Clean code**: valid operations produce no diagnostics
|
||||
|
||||
Run the compile diagnostics tests with:
|
||||
|
||||
```bash
|
||||
pit test compile
|
||||
```
|
||||
|
||||
## Test File Organization
|
||||
|
||||
Tests live in the `tests/` directory of a package:
|
||||
|
||||
@@ -5,7 +5,7 @@ weight: 86
|
||||
type: "docs"
|
||||
---
|
||||
|
||||
Wota is a binary message format for local inter-process communication. It is similar to Nota but works at word granularity (64-bit words) rather than byte granularity. Wota arrangements are less compact than Nota but faster to arrange and consume.
|
||||
Wota is a binary message format for local inter-process communication. It is similar to Nota but works at word granularity (64-bit words) rather than byte granularity. Wota arrangements are less compact than Nota but faster to arrange and consume. Wota is an internal module: `use('internal/wota')`.
|
||||
|
||||
Wota stands for Word Object Transfer Arrangement.
|
||||
|
||||
|
||||
2
fd.cm
2
fd.cm
@@ -1,5 +1,5 @@
|
||||
var fd = use('internal/fd')
|
||||
var wildstar = use('wildstar')
|
||||
var wildstar = use('internal/wildstar')
|
||||
|
||||
function last_pos(str, sep) {
|
||||
var last = null
|
||||
|
||||
2
fuzz.ce
2
fuzz.ce
@@ -13,7 +13,7 @@ var fd = use('fd')
|
||||
var time = use('time')
|
||||
var json = use('json')
|
||||
|
||||
var os_ref = use('os')
|
||||
var os_ref = use('internal/os')
|
||||
var analyze = os_ref.analyze
|
||||
var run_ast_fn = os_ref.run_ast_fn
|
||||
var run_ast_noopt_fn = os_ref.run_ast_noopt_fn
|
||||
|
||||
4
index.cm
4
index.cm
@@ -201,7 +201,9 @@ var index_ast = function(ast, tokens, filename) {
|
||||
if (node.expression.left != null && node.expression.left.kind == "name") {
|
||||
callee_name = node.expression.left.name
|
||||
}
|
||||
if (node.expression.right != null && node.expression.right.name != null) {
|
||||
if (is_text(node.expression.right)) {
|
||||
callee_name = (callee_name != null ? callee_name + "." : "") + node.expression.right
|
||||
} else if (node.expression.right != null && node.expression.right.name != null) {
|
||||
callee_name = (callee_name != null ? callee_name + "." : "") + node.expression.right.name
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ function use_embed(name) {
|
||||
|
||||
var fd = use_embed('internal_fd')
|
||||
var json_mod = use_embed('json')
|
||||
var crypto = use_embed('crypto')
|
||||
var crypto = use_embed('internal_crypto')
|
||||
|
||||
function content_hash(content) {
|
||||
var data = content
|
||||
|
||||
@@ -238,7 +238,7 @@ static const JSCFunctionListEntry js_crypto_funcs[] = {
|
||||
JS_CFUNC_DEF("unlock", 3, js_crypto_unlock),
|
||||
};
|
||||
|
||||
JSValue js_core_crypto_use(JSContext *js)
|
||||
JSValue js_core_internal_crypto_use(JSContext *js)
|
||||
{
|
||||
JS_FRAME(js);
|
||||
JS_ROOT(mod, JS_NewObject(js));
|
||||
@@ -25,7 +25,7 @@ function use_embed(name) {
|
||||
|
||||
var fd = use_embed('internal_fd')
|
||||
var js = use_embed('js')
|
||||
var crypto = use_embed('crypto')
|
||||
var crypto = use_embed('internal_crypto')
|
||||
|
||||
// core_path and shop_path come from env (C runtime passes them through)
|
||||
// shop_path may be null if --core was used without --shop
|
||||
@@ -182,9 +182,110 @@ function analyze(src, filename) {
|
||||
// Lazy-loaded verify_ir module (loaded on first use)
|
||||
var _verify_ir_mod = null
|
||||
|
||||
// Module summary extraction for cross-program analysis.
|
||||
// Scans mcode IR for use() call patterns and attaches summaries.
|
||||
// _summary_resolver is set after shop loads (null during bootstrap).
|
||||
var _summary_resolver = null
|
||||
|
||||
function extract_module_summaries(compiled) {
|
||||
if (_summary_resolver == null) return null
|
||||
var instrs = null
|
||||
var summaries = []
|
||||
var unresolved = []
|
||||
var i = 0
|
||||
var j = 0
|
||||
var n = 0
|
||||
var instr = null
|
||||
var prev = null
|
||||
var op = null
|
||||
var use_slots = {}
|
||||
var frame_map = {}
|
||||
var arg_map = {}
|
||||
var val_slot = 0
|
||||
var f_slot = 0
|
||||
var path = null
|
||||
var result_slot = 0
|
||||
var summary = null
|
||||
var inv_n = 0
|
||||
|
||||
if (compiled.main == null) return null
|
||||
instrs = compiled.main.instructions
|
||||
if (instrs == null) return null
|
||||
n = length(instrs)
|
||||
|
||||
// Pass 1: find access(slot, {make:"intrinsic", name:"use"})
|
||||
i = 0
|
||||
while (i < n) {
|
||||
instr = instrs[i]
|
||||
if (is_array(instr) && instr[0] == "access") {
|
||||
if (is_object(instr[2]) && instr[2].make == "intrinsic" && instr[2].name == "use") {
|
||||
use_slots[text(instr[1])] = true
|
||||
}
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
|
||||
// Pass 2: find frame(frame_slot, use_slot), setarg with string, invoke
|
||||
i = 0
|
||||
while (i < n) {
|
||||
instr = instrs[i]
|
||||
if (is_array(instr)) {
|
||||
op = instr[0]
|
||||
if (op == "frame" || op == "goframe") {
|
||||
if (use_slots[text(instr[2])] == true) {
|
||||
frame_map[text(instr[1])] = true
|
||||
}
|
||||
} else if (op == "setarg") {
|
||||
if (frame_map[text(instr[1])] == true) {
|
||||
val_slot = instr[3]
|
||||
j = i - 1
|
||||
while (j >= 0) {
|
||||
prev = instrs[j]
|
||||
if (is_array(prev) && prev[0] == "access" && prev[1] == val_slot && is_text(prev[2])) {
|
||||
arg_map[text(instr[1])] = prev[2]
|
||||
break
|
||||
}
|
||||
j = j - 1
|
||||
}
|
||||
}
|
||||
} else if (op == "invoke" || op == "tail_invoke") {
|
||||
f_slot = instr[1]
|
||||
path = arg_map[text(f_slot)]
|
||||
if (path != null) {
|
||||
result_slot = instr[2]
|
||||
summary = _summary_resolver(path)
|
||||
if (summary != null) {
|
||||
if (summary._native != true) {
|
||||
summaries[] = {slot: result_slot, summary: summary}
|
||||
}
|
||||
} else {
|
||||
inv_n = length(instr)
|
||||
unresolved[] = {path: path, line: instr[inv_n - 2], col: instr[inv_n - 1]}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
|
||||
if (length(summaries) > 0 || length(unresolved) > 0) {
|
||||
return {summaries: summaries, unresolved: unresolved}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
// Run AST through mcode pipeline -> register VM
|
||||
function run_ast_fn(name, ast, env) {
|
||||
var compiled = mcode_mod(ast)
|
||||
var ms = null
|
||||
var _ui = 0
|
||||
var _ur = null
|
||||
var optimized = null
|
||||
var _di = 0
|
||||
var _diag = null
|
||||
var _has_errors = false
|
||||
var mcode_json = null
|
||||
var mach_blob = null
|
||||
if (os._verify_ir) {
|
||||
if (_verify_ir_mod == null) {
|
||||
_verify_ir_mod = load_pipeline_module('verify_ir', pipeline_env)
|
||||
@@ -192,13 +293,45 @@ function run_ast_fn(name, ast, env) {
|
||||
compiled._verify = true
|
||||
compiled._verify_mod = _verify_ir_mod
|
||||
}
|
||||
var optimized = streamline_mod(compiled)
|
||||
if (!_no_warn) {
|
||||
compiled._warn = true
|
||||
ms = extract_module_summaries(compiled)
|
||||
if (ms != null) {
|
||||
if (length(ms.summaries) > 0) {
|
||||
compiled._module_summaries = ms.summaries
|
||||
}
|
||||
if (length(ms.unresolved) > 0) {
|
||||
compiled._unresolved_imports = ms.unresolved
|
||||
}
|
||||
}
|
||||
}
|
||||
if (compiled._unresolved_imports != null) {
|
||||
_ui = 0
|
||||
while (_ui < length(compiled._unresolved_imports)) {
|
||||
_ur = compiled._unresolved_imports[_ui]
|
||||
print(`${name}:${text(_ur.line)}:${text(_ur.col)}: error: cannot resolve module '${_ur.path}'\n`)
|
||||
_ui = _ui + 1
|
||||
}
|
||||
disrupt
|
||||
}
|
||||
optimized = streamline_mod(compiled)
|
||||
if (optimized._verify) {
|
||||
delete optimized._verify
|
||||
delete optimized._verify_mod
|
||||
}
|
||||
var mcode_json = json.encode(optimized)
|
||||
var mach_blob = mach_compile_mcode_bin(name, mcode_json)
|
||||
if (optimized._diagnostics != null && length(optimized._diagnostics) > 0) {
|
||||
_di = 0
|
||||
_has_errors = false
|
||||
while (_di < length(optimized._diagnostics)) {
|
||||
_diag = optimized._diagnostics[_di]
|
||||
print(`${_diag.file}:${text(_diag.line)}:${text(_diag.col)}: ${_diag.severity}: ${_diag.message}\n`)
|
||||
if (_diag.severity == "error") _has_errors = true
|
||||
_di = _di + 1
|
||||
}
|
||||
if (_has_errors) disrupt
|
||||
}
|
||||
mcode_json = json.encode(optimized)
|
||||
mach_blob = mach_compile_mcode_bin(name, mcode_json)
|
||||
return mach_load(mach_blob, env)
|
||||
}
|
||||
|
||||
@@ -217,6 +350,52 @@ function compile_to_blob(name, ast) {
|
||||
return mach_compile_mcode_bin(name, json.encode(optimized))
|
||||
}
|
||||
|
||||
// Compile user program AST to blob with diagnostics
|
||||
function compile_user_blob(name, ast) {
|
||||
var compiled = mcode_mod(ast)
|
||||
var ms = null
|
||||
var _ui = 0
|
||||
var _ur = null
|
||||
var optimized = null
|
||||
var _di = 0
|
||||
var _diag = null
|
||||
var _has_errors = false
|
||||
if (!_no_warn) {
|
||||
compiled._warn = true
|
||||
ms = extract_module_summaries(compiled)
|
||||
if (ms != null) {
|
||||
if (length(ms.summaries) > 0) {
|
||||
compiled._module_summaries = ms.summaries
|
||||
}
|
||||
if (length(ms.unresolved) > 0) {
|
||||
compiled._unresolved_imports = ms.unresolved
|
||||
}
|
||||
}
|
||||
}
|
||||
if (compiled._unresolved_imports != null) {
|
||||
_ui = 0
|
||||
while (_ui < length(compiled._unresolved_imports)) {
|
||||
_ur = compiled._unresolved_imports[_ui]
|
||||
print(`${name}:${text(_ur.line)}:${text(_ur.col)}: error: cannot resolve module '${_ur.path}'\n`)
|
||||
_ui = _ui + 1
|
||||
}
|
||||
disrupt
|
||||
}
|
||||
optimized = streamline_mod(compiled)
|
||||
if (optimized._diagnostics != null && length(optimized._diagnostics) > 0) {
|
||||
_di = 0
|
||||
_has_errors = false
|
||||
while (_di < length(optimized._diagnostics)) {
|
||||
_diag = optimized._diagnostics[_di]
|
||||
print(`${_diag.file}:${text(_diag.line)}:${text(_diag.col)}: ${_diag.severity}: ${_diag.message}\n`)
|
||||
if (_diag.severity == "error") _has_errors = true
|
||||
_di = _di + 1
|
||||
}
|
||||
if (_has_errors) disrupt
|
||||
}
|
||||
return mach_compile_mcode_bin(name, json.encode(optimized))
|
||||
}
|
||||
|
||||
// If loaded directly by C runtime (not via bootstrap), convert args -> init
|
||||
var _program = null
|
||||
var _user_args = []
|
||||
@@ -227,6 +406,9 @@ var _init = init
|
||||
if (_init != null && _init.native_mode)
|
||||
native_mode = true
|
||||
|
||||
// Inherit warn mode from init (set by C for --no-warn)
|
||||
var _no_warn = (_init != null && _init.no_warn) ? true : false
|
||||
|
||||
// CLI path: convert args to init record
|
||||
if (args != null && (_init == null || !_init.program)) {
|
||||
_program = args[0]
|
||||
@@ -242,7 +424,7 @@ if (args != null && (_init == null || !_init.program)) {
|
||||
}
|
||||
}
|
||||
|
||||
use_cache['core/os'] = os
|
||||
use_cache['core/internal/os'] = os
|
||||
|
||||
// Extra env properties added as engine initializes (log, runtime fns, etc.)
|
||||
var core_extras = {}
|
||||
@@ -314,8 +496,8 @@ function actor() {
|
||||
}
|
||||
|
||||
var actor_mod = use_core('actor')
|
||||
var wota = use_core('wota')
|
||||
var nota = use_core('nota')
|
||||
var wota = use_core('internal/wota')
|
||||
var nota = use_core('internal/nota')
|
||||
|
||||
|
||||
var ENETSERVICE = 0.1
|
||||
@@ -424,6 +606,22 @@ core_extras.native_mode = native_mode
|
||||
// NOW load shop -- it receives all of the above via env
|
||||
var shop = use_core('internal/shop')
|
||||
use_core('build')
|
||||
|
||||
// Wire up module summary resolver now that shop is available
|
||||
_summary_resolver = function(path) {
|
||||
var info = shop.resolve_import_info(path, null)
|
||||
if (info == null) return null
|
||||
if (info.type == 'native') return {_native: true}
|
||||
var resolved = info.resolved_path
|
||||
if (resolved == null) return null
|
||||
var summary_fn = function() {
|
||||
return shop.summary_file(resolved)
|
||||
} disruption {
|
||||
return null
|
||||
}
|
||||
return summary_fn()
|
||||
}
|
||||
|
||||
var time = use_core('time')
|
||||
var toml = use_core('toml')
|
||||
|
||||
@@ -1306,7 +1504,7 @@ $_.clock(_ => {
|
||||
} else {
|
||||
script = text(source_blob)
|
||||
ast = analyze(script, prog_path)
|
||||
mach_blob = compile_to_blob(prog, ast)
|
||||
mach_blob = compile_user_blob(prog, ast)
|
||||
if (cached_path) {
|
||||
ensure_build_dir()
|
||||
fd.slurpwrite(cached_path, mach_blob)
|
||||
|
||||
@@ -72,7 +72,7 @@ static const JSCFunctionListEntry js_kim_funcs[] = {
|
||||
MIST_FUNC_DEF(kim, decode, 1),
|
||||
};
|
||||
|
||||
JSValue js_core_kim_use(JSContext *js)
|
||||
JSValue js_core_internal_kim_use(JSContext *js)
|
||||
{
|
||||
JS_FRAME(js);
|
||||
JS_ROOT(mod, JS_NewObject(js));
|
||||
|
||||
431
internal/nota.c
Normal file
431
internal/nota.c
Normal file
@@ -0,0 +1,431 @@
|
||||
#define NOTA_IMPLEMENTATION
|
||||
#include "quickjs-internal.h"
|
||||
#include "cell.h"
|
||||
|
||||
static int nota_get_arr_len (JSContext *ctx, JSValue arr) {
|
||||
int64_t len;
|
||||
JS_GetLength (ctx, arr, &len);
|
||||
return (int)len;
|
||||
}
|
||||
|
||||
typedef struct NotaVisitedNode {
|
||||
JSGCRef ref;
|
||||
struct NotaVisitedNode *next;
|
||||
} NotaVisitedNode;
|
||||
|
||||
typedef struct NotaEncodeContext {
|
||||
JSContext *ctx;
|
||||
NotaVisitedNode *visited_list;
|
||||
NotaBuffer nb;
|
||||
int cycle;
|
||||
JSGCRef *replacer_ref; /* pointer to GC-rooted ref */
|
||||
} NotaEncodeContext;
|
||||
|
||||
static void nota_stack_push (NotaEncodeContext *enc, JSValueConst val) {
|
||||
NotaVisitedNode *node = (NotaVisitedNode *)sys_malloc (sizeof (NotaVisitedNode));
|
||||
JS_PushGCRef (enc->ctx, &node->ref);
|
||||
node->ref.val = JS_DupValue (enc->ctx, val);
|
||||
node->next = enc->visited_list;
|
||||
enc->visited_list = node;
|
||||
}
|
||||
|
||||
static void nota_stack_pop (NotaEncodeContext *enc) {
|
||||
NotaVisitedNode *node = enc->visited_list;
|
||||
enc->visited_list = node->next;
|
||||
JS_FreeValue (enc->ctx, node->ref.val);
|
||||
JS_PopGCRef (enc->ctx, &node->ref);
|
||||
sys_free (node);
|
||||
}
|
||||
|
||||
static int nota_stack_has (NotaEncodeContext *enc, JSValueConst val) {
|
||||
NotaVisitedNode *node = enc->visited_list;
|
||||
while (node) {
|
||||
if (JS_StrictEq (enc->ctx, node->ref.val, val))
|
||||
return 1;
|
||||
node = node->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static JSValue nota_apply_replacer (NotaEncodeContext *enc, JSValueConst holder, JSValueConst key, JSValueConst val) {
|
||||
if (!enc->replacer_ref || JS_IsNull (enc->replacer_ref->val)) return JS_DupValue (enc->ctx, val);
|
||||
|
||||
JSValue args[2] = { JS_DupValue (enc->ctx, key), JS_DupValue (enc->ctx, val) };
|
||||
JSValue result = JS_Call (enc->ctx, enc->replacer_ref->val, holder, 2, args);
|
||||
JS_FreeValue (enc->ctx, args[0]);
|
||||
JS_FreeValue (enc->ctx, args[1]);
|
||||
|
||||
if (JS_IsException (result)) return JS_DupValue (enc->ctx, val);
|
||||
return result;
|
||||
}
|
||||
|
||||
static char *js_do_nota_decode (JSContext *js, JSValue *tmp, char *nota, JSValue holder, JSValue key, JSValue reviver) {
|
||||
int type = nota_type (nota);
|
||||
JSValue ret2;
|
||||
long long n;
|
||||
double d;
|
||||
int b;
|
||||
char *str;
|
||||
uint8_t *blob;
|
||||
|
||||
switch (type) {
|
||||
case NOTA_BLOB:
|
||||
nota = nota_read_blob (&n, (char **)&blob, nota);
|
||||
*tmp = js_new_blob_stoned_copy (js, blob, n);
|
||||
sys_free (blob);
|
||||
break;
|
||||
case NOTA_TEXT:
|
||||
nota = nota_read_text (&str, nota);
|
||||
*tmp = JS_NewString (js, str);
|
||||
sys_free (str);
|
||||
break;
|
||||
case NOTA_ARR:
|
||||
nota = nota_read_array (&n, nota);
|
||||
*tmp = JS_NewArrayLen (js, n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
nota = js_do_nota_decode (js, &ret2, nota, *tmp, JS_NewInt32 (js, i), reviver);
|
||||
JS_SetPropertyNumber (js, *tmp, i, ret2);
|
||||
}
|
||||
break;
|
||||
case NOTA_REC:
|
||||
nota = nota_read_record (&n, nota);
|
||||
*tmp = JS_NewObject (js);
|
||||
for (int i = 0; i < n; i++) {
|
||||
JSGCRef prop_key_ref, sub_val_ref;
|
||||
JS_PushGCRef (js, &prop_key_ref);
|
||||
JS_PushGCRef (js, &sub_val_ref);
|
||||
nota = nota_read_text (&str, nota);
|
||||
prop_key_ref.val = JS_NewString (js, str);
|
||||
sub_val_ref.val = JS_NULL;
|
||||
nota = js_do_nota_decode (js, &sub_val_ref.val, nota, *tmp, prop_key_ref.val, reviver);
|
||||
JS_SetPropertyStr (js, *tmp, str, sub_val_ref.val);
|
||||
JS_PopGCRef (js, &sub_val_ref);
|
||||
JS_PopGCRef (js, &prop_key_ref);
|
||||
sys_free (str);
|
||||
}
|
||||
break;
|
||||
case NOTA_INT:
|
||||
nota = nota_read_int (&n, nota);
|
||||
*tmp = JS_NewInt64 (js, n);
|
||||
break;
|
||||
case NOTA_SYM:
|
||||
nota = nota_read_sym (&b, nota);
|
||||
if (b == NOTA_PRIVATE) {
|
||||
JSGCRef inner_ref, obj_ref2;
|
||||
JS_PushGCRef (js, &inner_ref);
|
||||
JS_PushGCRef (js, &obj_ref2);
|
||||
inner_ref.val = JS_NULL;
|
||||
nota = js_do_nota_decode (js, &inner_ref.val, nota, holder, JS_NULL, reviver);
|
||||
obj_ref2.val = JS_NewObject (js);
|
||||
if (!JS_IsNull (js->actor_sym))
|
||||
JS_SetPropertyKey (js, obj_ref2.val, js->actor_sym, inner_ref.val);
|
||||
JS_CellStone (js, obj_ref2.val);
|
||||
*tmp = obj_ref2.val;
|
||||
JS_PopGCRef (js, &obj_ref2);
|
||||
JS_PopGCRef (js, &inner_ref);
|
||||
} else {
|
||||
switch (b) {
|
||||
case NOTA_NULL: *tmp = JS_NULL; break;
|
||||
case NOTA_FALSE: *tmp = JS_NewBool (js, 0); break;
|
||||
case NOTA_TRUE: *tmp = JS_NewBool (js, 1); break;
|
||||
default: *tmp = JS_NULL; break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
case NOTA_FLOAT:
|
||||
nota = nota_read_float (&d, nota);
|
||||
*tmp = JS_NewFloat64 (js, d);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!JS_IsNull (reviver)) {
|
||||
JSValue args[2] = { JS_DupValue (js, key), JS_DupValue (js, *tmp) };
|
||||
JSValue revived = JS_Call (js, reviver, holder, 2, args);
|
||||
JS_FreeValue (js, args[0]);
|
||||
JS_FreeValue (js, args[1]);
|
||||
if (!JS_IsException (revived)) {
|
||||
JS_FreeValue (js, *tmp);
|
||||
*tmp = revived;
|
||||
} else {
|
||||
JS_FreeValue (js, revived);
|
||||
}
|
||||
}
|
||||
|
||||
return nota;
|
||||
}
|
||||
|
||||
static void nota_encode_value (NotaEncodeContext *enc, JSValueConst val, JSValueConst holder, JSValueConst key) {
|
||||
JSContext *ctx = enc->ctx;
|
||||
JSGCRef replaced_ref, keys_ref, elem_ref, prop_ref;
|
||||
JS_PushGCRef (ctx, &replaced_ref);
|
||||
replaced_ref.val = nota_apply_replacer (enc, holder, key, val);
|
||||
int tag = JS_VALUE_GET_TAG (replaced_ref.val);
|
||||
|
||||
switch (tag) {
|
||||
case JS_TAG_INT:
|
||||
case JS_TAG_FLOAT64: {
|
||||
double d;
|
||||
JS_ToFloat64 (ctx, &d, replaced_ref.val);
|
||||
nota_write_number (&enc->nb, d);
|
||||
break;
|
||||
}
|
||||
case JS_TAG_STRING: {
|
||||
const char *str = JS_ToCString (ctx, replaced_ref.val);
|
||||
nota_write_text (&enc->nb, str);
|
||||
JS_FreeCString (ctx, str);
|
||||
break;
|
||||
}
|
||||
case JS_TAG_BOOL:
|
||||
if (JS_VALUE_GET_BOOL (replaced_ref.val)) nota_write_sym (&enc->nb, NOTA_TRUE);
|
||||
else nota_write_sym (&enc->nb, NOTA_FALSE);
|
||||
break;
|
||||
case JS_TAG_NULL:
|
||||
nota_write_sym (&enc->nb, NOTA_NULL);
|
||||
break;
|
||||
case JS_TAG_PTR: {
|
||||
if (JS_IsText (replaced_ref.val)) {
|
||||
const char *str = JS_ToCString (ctx, replaced_ref.val);
|
||||
nota_write_text (&enc->nb, str);
|
||||
JS_FreeCString (ctx, str);
|
||||
break;
|
||||
}
|
||||
|
||||
if (js_is_blob (ctx, replaced_ref.val)) {
|
||||
size_t buf_len;
|
||||
void *buf_data = js_get_blob_data (ctx, &buf_len, replaced_ref.val);
|
||||
if (buf_data == (void *)-1) {
|
||||
JS_PopGCRef (ctx, &replaced_ref);
|
||||
return;
|
||||
}
|
||||
nota_write_blob (&enc->nb, (unsigned long long)buf_len * 8, (const char *)buf_data);
|
||||
break;
|
||||
}
|
||||
|
||||
if (JS_IsArray (replaced_ref.val)) {
|
||||
if (nota_stack_has (enc, replaced_ref.val)) {
|
||||
enc->cycle = 1;
|
||||
break;
|
||||
}
|
||||
nota_stack_push (enc, replaced_ref.val);
|
||||
int arr_len = nota_get_arr_len (ctx, replaced_ref.val);
|
||||
nota_write_array (&enc->nb, arr_len);
|
||||
JS_PushGCRef (ctx, &elem_ref);
|
||||
for (int i = 0; i < arr_len; i++) {
|
||||
elem_ref.val = JS_GetPropertyNumber (ctx, replaced_ref.val, i);
|
||||
JSValue elem_key = JS_NewInt32 (ctx, i);
|
||||
nota_encode_value (enc, elem_ref.val, replaced_ref.val, elem_key);
|
||||
}
|
||||
JS_PopGCRef (ctx, &elem_ref);
|
||||
nota_stack_pop (enc);
|
||||
break;
|
||||
}
|
||||
|
||||
JSValue adata = JS_NULL;
|
||||
if (!JS_IsNull (ctx->actor_sym)) {
|
||||
int has = JS_HasPropertyKey (ctx, replaced_ref.val, ctx->actor_sym);
|
||||
if (has > 0) adata = JS_GetPropertyKey (ctx, replaced_ref.val, ctx->actor_sym);
|
||||
}
|
||||
if (!JS_IsNull (adata)) {
|
||||
nota_write_sym (&enc->nb, NOTA_PRIVATE);
|
||||
nota_encode_value (enc, adata, replaced_ref.val, JS_NULL);
|
||||
JS_FreeValue (ctx, adata);
|
||||
break;
|
||||
}
|
||||
JS_FreeValue (ctx, adata);
|
||||
if (nota_stack_has (enc, replaced_ref.val)) {
|
||||
enc->cycle = 1;
|
||||
break;
|
||||
}
|
||||
nota_stack_push (enc, replaced_ref.val);
|
||||
|
||||
JSValue to_json = JS_GetPropertyStr (ctx, replaced_ref.val, "toJSON");
|
||||
if (JS_IsFunction (to_json)) {
|
||||
JSValue result = JS_Call (ctx, to_json, replaced_ref.val, 0, NULL);
|
||||
if (!JS_IsException (result)) {
|
||||
nota_encode_value (enc, result, holder, key);
|
||||
} else {
|
||||
nota_write_sym (&enc->nb, NOTA_NULL);
|
||||
}
|
||||
nota_stack_pop (enc);
|
||||
break;
|
||||
}
|
||||
|
||||
JS_PushGCRef (ctx, &keys_ref);
|
||||
keys_ref.val = JS_GetOwnPropertyNames (ctx, replaced_ref.val);
|
||||
if (JS_IsException (keys_ref.val)) {
|
||||
nota_write_sym (&enc->nb, NOTA_NULL);
|
||||
nota_stack_pop (enc);
|
||||
JS_PopGCRef (ctx, &keys_ref);
|
||||
break;
|
||||
}
|
||||
int64_t plen64;
|
||||
if (JS_GetLength (ctx, keys_ref.val, &plen64) < 0) {
|
||||
nota_write_sym (&enc->nb, NOTA_NULL);
|
||||
nota_stack_pop (enc);
|
||||
JS_PopGCRef (ctx, &keys_ref);
|
||||
break;
|
||||
}
|
||||
uint32_t plen = (uint32_t)plen64;
|
||||
|
||||
JS_PushGCRef (ctx, &prop_ref);
|
||||
JS_PushGCRef (ctx, &elem_ref);
|
||||
uint32_t non_function_count = 0;
|
||||
for (uint32_t i = 0; i < plen; i++) {
|
||||
elem_ref.val = JS_GetPropertyNumber (ctx, keys_ref.val, i);
|
||||
prop_ref.val = JS_GetProperty (ctx, replaced_ref.val, elem_ref.val);
|
||||
if (!JS_IsFunction (prop_ref.val)) non_function_count++;
|
||||
}
|
||||
|
||||
nota_write_record (&enc->nb, non_function_count);
|
||||
for (uint32_t i = 0; i < plen; i++) {
|
||||
elem_ref.val = JS_GetPropertyNumber (ctx, keys_ref.val, i);
|
||||
prop_ref.val = JS_GetProperty (ctx, replaced_ref.val, elem_ref.val);
|
||||
if (!JS_IsFunction (prop_ref.val)) {
|
||||
const char *prop_name = JS_ToCString (ctx, elem_ref.val);
|
||||
nota_write_text (&enc->nb, prop_name ? prop_name : "");
|
||||
nota_encode_value (enc, prop_ref.val, replaced_ref.val, elem_ref.val);
|
||||
JS_FreeCString (ctx, prop_name);
|
||||
}
|
||||
}
|
||||
JS_PopGCRef (ctx, &elem_ref);
|
||||
JS_PopGCRef (ctx, &prop_ref);
|
||||
JS_PopGCRef (ctx, &keys_ref);
|
||||
nota_stack_pop (enc);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
nota_write_sym (&enc->nb, NOTA_NULL);
|
||||
break;
|
||||
}
|
||||
JS_PopGCRef (ctx, &replaced_ref);
|
||||
}
|
||||
|
||||
void *value2nota (JSContext *ctx, JSValue v) {
|
||||
JSGCRef val_ref, key_ref;
|
||||
JS_PushGCRef (ctx, &val_ref);
|
||||
JS_PushGCRef (ctx, &key_ref);
|
||||
val_ref.val = v;
|
||||
|
||||
NotaEncodeContext enc_s, *enc = &enc_s;
|
||||
enc->ctx = ctx;
|
||||
enc->visited_list = NULL;
|
||||
enc->cycle = 0;
|
||||
enc->replacer_ref = NULL;
|
||||
|
||||
nota_buffer_init (&enc->nb, 128);
|
||||
key_ref.val = JS_NewString (ctx, "");
|
||||
nota_encode_value (enc, val_ref.val, JS_NULL, key_ref.val);
|
||||
|
||||
if (enc->cycle) {
|
||||
JS_PopGCRef (ctx, &key_ref);
|
||||
JS_PopGCRef (ctx, &val_ref);
|
||||
nota_buffer_free (&enc->nb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JS_PopGCRef (ctx, &key_ref);
|
||||
JS_PopGCRef (ctx, &val_ref);
|
||||
void *data_ptr = enc->nb.data;
|
||||
enc->nb.data = NULL;
|
||||
nota_buffer_free (&enc->nb);
|
||||
return data_ptr;
|
||||
}
|
||||
|
||||
JSValue nota2value (JSContext *js, void *nota) {
|
||||
if (!nota) return JS_NULL;
|
||||
JSGCRef holder_ref, key_ref, ret_ref;
|
||||
JS_PushGCRef (js, &holder_ref);
|
||||
JS_PushGCRef (js, &key_ref);
|
||||
JS_PushGCRef (js, &ret_ref);
|
||||
holder_ref.val = JS_NewObject (js);
|
||||
key_ref.val = JS_NewString (js, "");
|
||||
ret_ref.val = JS_NULL;
|
||||
js_do_nota_decode (js, &ret_ref.val, nota, holder_ref.val, key_ref.val, JS_NULL);
|
||||
JSValue result = ret_ref.val;
|
||||
JS_PopGCRef (js, &ret_ref);
|
||||
JS_PopGCRef (js, &key_ref);
|
||||
JS_PopGCRef (js, &holder_ref);
|
||||
return result;
|
||||
}
|
||||
|
||||
static JSValue js_nota_encode (JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
if (argc < 1) return JS_RaiseDisrupt (ctx, "nota.encode requires at least 1 argument");
|
||||
|
||||
JSGCRef val_ref, replacer_ref, key_ref;
|
||||
JS_PushGCRef (ctx, &val_ref);
|
||||
JS_PushGCRef (ctx, &replacer_ref);
|
||||
JS_PushGCRef (ctx, &key_ref);
|
||||
val_ref.val = argv[0];
|
||||
replacer_ref.val = (argc > 1 && JS_IsFunction (argv[1])) ? argv[1] : JS_NULL;
|
||||
|
||||
NotaEncodeContext enc_s, *enc = &enc_s;
|
||||
enc->ctx = ctx;
|
||||
enc->visited_list = NULL;
|
||||
enc->cycle = 0;
|
||||
enc->replacer_ref = &replacer_ref;
|
||||
|
||||
nota_buffer_init (&enc->nb, 128);
|
||||
key_ref.val = JS_NewString (ctx, "");
|
||||
nota_encode_value (enc, val_ref.val, JS_NULL, key_ref.val);
|
||||
|
||||
JSValue ret;
|
||||
if (enc->cycle) {
|
||||
nota_buffer_free (&enc->nb);
|
||||
ret = JS_RaiseDisrupt (ctx, "Tried to encode something to nota with a cycle.");
|
||||
} else {
|
||||
size_t total_len = enc->nb.size;
|
||||
void *data_ptr = enc->nb.data;
|
||||
ret = js_new_blob_stoned_copy (ctx, (uint8_t *)data_ptr, total_len);
|
||||
nota_buffer_free (&enc->nb);
|
||||
}
|
||||
|
||||
JS_PopGCRef (ctx, &key_ref);
|
||||
JS_PopGCRef (ctx, &replacer_ref);
|
||||
JS_PopGCRef (ctx, &val_ref);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static JSValue js_nota_decode (JSContext *js, JSValueConst self, int argc, JSValueConst *argv) {
|
||||
if (argc < 1) return JS_NULL;
|
||||
|
||||
size_t len;
|
||||
unsigned char *nota = js_get_blob_data (js, &len, argv[0]);
|
||||
if (nota == (unsigned char *)-1) return JS_EXCEPTION;
|
||||
if (!nota) return JS_NULL;
|
||||
|
||||
JSGCRef holder_ref, key_ref, ret_ref, reviver_ref;
|
||||
JS_PushGCRef (js, &holder_ref);
|
||||
JS_PushGCRef (js, &key_ref);
|
||||
JS_PushGCRef (js, &ret_ref);
|
||||
JS_PushGCRef (js, &reviver_ref);
|
||||
|
||||
reviver_ref.val = (argc > 1 && JS_IsFunction (argv[1])) ? argv[1] : JS_NULL;
|
||||
holder_ref.val = JS_NewObject (js);
|
||||
key_ref.val = JS_NewString (js, "");
|
||||
ret_ref.val = JS_NULL;
|
||||
|
||||
js_do_nota_decode (js, &ret_ref.val, (char *)nota, holder_ref.val, key_ref.val, reviver_ref.val);
|
||||
|
||||
JSValue result = ret_ref.val;
|
||||
JS_PopGCRef (js, &reviver_ref);
|
||||
JS_PopGCRef (js, &ret_ref);
|
||||
JS_PopGCRef (js, &key_ref);
|
||||
JS_PopGCRef (js, &holder_ref);
|
||||
return result;
|
||||
}
|
||||
|
||||
static const JSCFunctionListEntry js_nota_funcs[] = {
|
||||
JS_CFUNC_DEF ("encode", 1, js_nota_encode),
|
||||
JS_CFUNC_DEF ("decode", 1, js_nota_decode),
|
||||
};
|
||||
|
||||
JSValue js_core_internal_nota_use (JSContext *js) {
|
||||
JSGCRef export_ref;
|
||||
JS_PushGCRef (js, &export_ref);
|
||||
export_ref.val = JS_NewObject (js);
|
||||
JS_SetPropertyFunctionList (js, export_ref.val, js_nota_funcs, sizeof (js_nota_funcs) / sizeof (JSCFunctionListEntry));
|
||||
JSValue result = export_ref.val;
|
||||
JS_PopGCRef (js, &export_ref);
|
||||
return result;
|
||||
}
|
||||
@@ -733,7 +733,7 @@ static const JSCFunctionListEntry js_os_funcs[] = {
|
||||
MIST_FUNC_DEF(os, stack, 1),
|
||||
};
|
||||
|
||||
JSValue js_core_os_use(JSContext *js) {
|
||||
JSValue js_core_internal_os_use(JSContext *js) {
|
||||
JS_NewClassID(&js_dylib_class_id);
|
||||
JS_NewClass(js, js_dylib_class_id, &js_dylib_class);
|
||||
|
||||
|
||||
@@ -449,7 +449,7 @@ static const JSCFunctionListEntry js_qop_funcs[] = {
|
||||
JS_PROP_INT32_DEF("FLAG_ENCRYPTED", QOP_FLAG_ENCRYPTED, 0),
|
||||
};
|
||||
|
||||
JSValue js_core_qop_use(JSContext *js) {
|
||||
JSValue js_core_internal_qop_use(JSContext *js) {
|
||||
JS_FRAME(js);
|
||||
JS_NewClassID(&js_qop_archive_class_id);
|
||||
JS_NewClass(js, js_qop_archive_class_id, &js_qop_archive_class);
|
||||
@@ -5,11 +5,11 @@ var fd = use('fd')
|
||||
var http = use('http')
|
||||
var miniz = use('miniz')
|
||||
var time = use('time')
|
||||
var crypto = use('crypto')
|
||||
var crypto = use('internal/crypto')
|
||||
var blob = use('blob')
|
||||
|
||||
var pkg_tools = use('package')
|
||||
var os = use('os')
|
||||
var os = use('internal/os')
|
||||
var link = use('link')
|
||||
|
||||
// These come from env (via core_extras in engine.cm):
|
||||
@@ -519,6 +519,7 @@ var _ast_cache = {}
|
||||
var _analyze_cache = {}
|
||||
var _compile_cache = {}
|
||||
var _index_cache = {}
|
||||
var _summary_cache = {}
|
||||
|
||||
var get_tokenize = function() {
|
||||
if (!_tokenize_mod) _tokenize_mod = use_cache['core/tokenize'] || use_cache['tokenize']
|
||||
@@ -547,6 +548,14 @@ var get_index = function() {
|
||||
}
|
||||
return _index_mod
|
||||
}
|
||||
var _analyze_mod = null
|
||||
var get_analyze = function() {
|
||||
if (!_analyze_mod) {
|
||||
_analyze_mod = use_cache['core/analyze'] || use_cache['analyze']
|
||||
if (!_analyze_mod) _analyze_mod = Shop.use('analyze', 'core')
|
||||
}
|
||||
return _analyze_mod
|
||||
}
|
||||
|
||||
Shop.tokenize_file = function(path) {
|
||||
var src = text(fd.slurp(path))
|
||||
@@ -611,6 +620,16 @@ Shop.index_file = function(path) {
|
||||
return idx
|
||||
}
|
||||
|
||||
Shop.summary_file = function(path) {
|
||||
var src = text(fd.slurp(path))
|
||||
var key = content_hash(stone(blob(src)))
|
||||
if (_summary_cache[key]) return _summary_cache[key]
|
||||
var idx = Shop.index_file(path)
|
||||
var summary = get_analyze().module_summary(idx)
|
||||
_summary_cache[key] = summary
|
||||
return summary
|
||||
}
|
||||
|
||||
Shop.mcode_file = function(path) {
|
||||
var folded = Shop.analyze_file(path)
|
||||
return get_mcode()(folded)
|
||||
|
||||
@@ -28,7 +28,7 @@ static const JSCFunctionListEntry js_wildstar_funcs[] = {
|
||||
JS_PROP_INT32_DEF("WM_WILDSTAR", WM_WILDSTAR, 0),
|
||||
};
|
||||
|
||||
JSValue js_core_wildstar_use(JSContext *js) {
|
||||
JSValue js_core_internal_wildstar_use(JSContext *js) {
|
||||
JS_FRAME(js);
|
||||
JS_ROOT(mod, JS_NewObject(js));
|
||||
JS_SetPropertyFunctionList(js, mod.val, js_wildstar_funcs, countof(js_wildstar_funcs));
|
||||
461
internal/wota.c
Normal file
461
internal/wota.c
Normal file
@@ -0,0 +1,461 @@
|
||||
#define WOTA_IMPLEMENTATION
|
||||
#include "quickjs-internal.h"
|
||||
#include "cell.h"
|
||||
|
||||
typedef struct ObjectRef {
|
||||
void *ptr;
|
||||
struct ObjectRef *next;
|
||||
} ObjectRef;
|
||||
|
||||
typedef struct WotaEncodeContext {
|
||||
JSContext *ctx;
|
||||
ObjectRef *visited_stack;
|
||||
WotaBuffer wb;
|
||||
int cycle;
|
||||
JSValue replacer;
|
||||
} WotaEncodeContext;
|
||||
|
||||
static void wota_stack_push (WotaEncodeContext *enc, JSValueConst val) {
|
||||
(void)enc; (void)val;
|
||||
/* Cycle detection disabled for performance */
|
||||
}
|
||||
|
||||
static void wota_stack_pop (WotaEncodeContext *enc) {
|
||||
if (!enc->visited_stack) return;
|
||||
|
||||
ObjectRef *top = enc->visited_stack;
|
||||
enc->visited_stack = top->next;
|
||||
sys_free (top);
|
||||
}
|
||||
|
||||
static int wota_stack_has (WotaEncodeContext *enc, JSValueConst val) {
|
||||
(void)enc; (void)val;
|
||||
return 0;
|
||||
/* Cycle detection disabled for performance */
|
||||
}
|
||||
|
||||
static void wota_stack_free (WotaEncodeContext *enc) {
|
||||
while (enc->visited_stack) {
|
||||
wota_stack_pop (enc);
|
||||
}
|
||||
}
|
||||
|
||||
static JSValue wota_apply_replacer (WotaEncodeContext *enc, JSValueConst holder, JSValue key, JSValueConst val) {
|
||||
if (JS_IsNull (enc->replacer)) return JS_DupValue (enc->ctx, val);
|
||||
JSValue key_val = JS_IsNull (key) ? JS_NULL : JS_DupValue (enc->ctx, key);
|
||||
JSValue args[2] = { key_val, JS_DupValue (enc->ctx, val) };
|
||||
JSValue result = JS_Call (enc->ctx, enc->replacer, holder, 2, args);
|
||||
JS_FreeValue (enc->ctx, args[0]);
|
||||
JS_FreeValue (enc->ctx, args[1]);
|
||||
if (JS_IsException (result)) return JS_DupValue (enc->ctx, val);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void wota_encode_value (WotaEncodeContext *enc, JSValueConst val, JSValueConst holder, JSValue key);
|
||||
|
||||
static void encode_object_properties (WotaEncodeContext *enc, JSValueConst val, JSValueConst holder) {
|
||||
JSContext *ctx = enc->ctx;
|
||||
|
||||
/* Root the input value to protect it during property enumeration */
|
||||
JSGCRef val_ref, keys_ref;
|
||||
JS_PushGCRef (ctx, &val_ref);
|
||||
JS_PushGCRef (ctx, &keys_ref);
|
||||
val_ref.val = JS_DupValue (ctx, val);
|
||||
|
||||
keys_ref.val = JS_GetOwnPropertyNames (ctx, val_ref.val);
|
||||
if (JS_IsException (keys_ref.val)) {
|
||||
wota_write_sym (&enc->wb, WOTA_NULL);
|
||||
JS_FreeValue (ctx, val_ref.val);
|
||||
JS_PopGCRef (ctx, &keys_ref);
|
||||
JS_PopGCRef (ctx, &val_ref);
|
||||
return;
|
||||
}
|
||||
int64_t plen64;
|
||||
if (JS_GetLength (ctx, keys_ref.val, &plen64) < 0) {
|
||||
JS_FreeValue (ctx, keys_ref.val);
|
||||
JS_FreeValue (ctx, val_ref.val);
|
||||
wota_write_sym (&enc->wb, WOTA_NULL);
|
||||
JS_PopGCRef (ctx, &keys_ref);
|
||||
JS_PopGCRef (ctx, &val_ref);
|
||||
return;
|
||||
}
|
||||
uint32_t plen = (uint32_t)plen64;
|
||||
uint32_t non_function_count = 0;
|
||||
|
||||
/* Allocate GC-rooted arrays for props and keys */
|
||||
JSGCRef *prop_refs = sys_malloc (sizeof (JSGCRef) * plen);
|
||||
JSGCRef *key_refs = sys_malloc (sizeof (JSGCRef) * plen);
|
||||
for (uint32_t i = 0; i < plen; i++) {
|
||||
JS_PushGCRef (ctx, &prop_refs[i]);
|
||||
JS_PushGCRef (ctx, &key_refs[i]);
|
||||
prop_refs[i].val = JS_NULL;
|
||||
key_refs[i].val = JS_NULL;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < plen; i++) {
|
||||
/* Store key into its GCRef slot immediately so it's rooted before
|
||||
JS_GetProperty can trigger GC and relocate the string. */
|
||||
key_refs[i].val = JS_GetPropertyNumber (ctx, keys_ref.val, i);
|
||||
JSValue prop_val = JS_GetProperty (ctx, val_ref.val, key_refs[i].val);
|
||||
if (!JS_IsFunction (prop_val)) {
|
||||
if (i != non_function_count) {
|
||||
key_refs[non_function_count].val = key_refs[i].val;
|
||||
key_refs[i].val = JS_NULL;
|
||||
}
|
||||
prop_refs[non_function_count].val = prop_val;
|
||||
non_function_count++;
|
||||
} else {
|
||||
JS_FreeValue (ctx, prop_val);
|
||||
JS_FreeValue (ctx, key_refs[i].val);
|
||||
key_refs[i].val = JS_NULL;
|
||||
}
|
||||
}
|
||||
JS_FreeValue (ctx, keys_ref.val);
|
||||
wota_write_record (&enc->wb, non_function_count);
|
||||
for (uint32_t i = 0; i < non_function_count; i++) {
|
||||
size_t klen;
|
||||
const char *prop_name = JS_ToCStringLen (ctx, &klen, key_refs[i].val);
|
||||
wota_write_text_len (&enc->wb, prop_name ? prop_name : "", prop_name ? klen : 0);
|
||||
wota_encode_value (enc, prop_refs[i].val, val_ref.val, key_refs[i].val);
|
||||
JS_FreeCString (ctx, prop_name);
|
||||
JS_FreeValue (ctx, prop_refs[i].val);
|
||||
JS_FreeValue (ctx, key_refs[i].val);
|
||||
}
|
||||
/* Pop all GC refs in reverse order */
|
||||
for (int i = plen - 1; i >= 0; i--) {
|
||||
JS_PopGCRef (ctx, &key_refs[i]);
|
||||
JS_PopGCRef (ctx, &prop_refs[i]);
|
||||
}
|
||||
sys_free (prop_refs);
|
||||
sys_free (key_refs);
|
||||
JS_FreeValue (ctx, val_ref.val);
|
||||
JS_PopGCRef (ctx, &keys_ref);
|
||||
JS_PopGCRef (ctx, &val_ref);
|
||||
}
|
||||
|
||||
static void wota_encode_value (WotaEncodeContext *enc, JSValueConst val, JSValueConst holder, JSValue key) {
|
||||
JSContext *ctx = enc->ctx;
|
||||
JSValue replaced;
|
||||
if (!JS_IsNull (enc->replacer) && !JS_IsNull (key))
|
||||
replaced = wota_apply_replacer (enc, holder, key, val);
|
||||
else
|
||||
replaced = JS_DupValue (enc->ctx, val);
|
||||
|
||||
int tag = JS_VALUE_GET_TAG (replaced);
|
||||
switch (tag) {
|
||||
case JS_TAG_INT: {
|
||||
int32_t d;
|
||||
JS_ToInt32 (ctx, &d, replaced);
|
||||
wota_write_int_word (&enc->wb, d);
|
||||
break;
|
||||
}
|
||||
case JS_TAG_FLOAT64: {
|
||||
double d;
|
||||
if (JS_ToFloat64 (ctx, &d, replaced) < 0) {
|
||||
wota_write_sym (&enc->wb, WOTA_NULL);
|
||||
break;
|
||||
}
|
||||
wota_write_float_word (&enc->wb, d);
|
||||
break;
|
||||
}
|
||||
case JS_TAG_STRING: {
|
||||
size_t plen;
|
||||
const char *str = JS_ToCStringLen (ctx, &plen, replaced);
|
||||
wota_write_text_len (&enc->wb, str ? str : "", str ? plen : 0);
|
||||
JS_FreeCString (ctx, str);
|
||||
break;
|
||||
}
|
||||
case JS_TAG_BOOL:
|
||||
wota_write_sym (&enc->wb, JS_VALUE_GET_BOOL (replaced) ? WOTA_TRUE : WOTA_FALSE);
|
||||
break;
|
||||
case JS_TAG_NULL:
|
||||
wota_write_sym (&enc->wb, WOTA_NULL);
|
||||
break;
|
||||
case JS_TAG_PTR: {
|
||||
if (JS_IsText (replaced)) {
|
||||
size_t plen;
|
||||
const char *str = JS_ToCStringLen (ctx, &plen, replaced);
|
||||
wota_write_text_len (&enc->wb, str ? str : "", str ? plen : 0);
|
||||
JS_FreeCString (ctx, str);
|
||||
break;
|
||||
}
|
||||
if (js_is_blob (ctx, replaced)) {
|
||||
size_t buf_len;
|
||||
void *buf_data = js_get_blob_data (ctx, &buf_len, replaced);
|
||||
if (buf_data == (void *)-1) {
|
||||
JS_FreeValue (ctx, replaced);
|
||||
return;
|
||||
}
|
||||
if (buf_len == 0) {
|
||||
wota_write_blob (&enc->wb, 0, "");
|
||||
} else {
|
||||
wota_write_blob (&enc->wb, (unsigned long long)buf_len * 8, (const char *)buf_data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (JS_IsArray (replaced)) {
|
||||
if (wota_stack_has (enc, replaced)) {
|
||||
enc->cycle = 1;
|
||||
break;
|
||||
}
|
||||
wota_stack_push (enc, replaced);
|
||||
int64_t arr_len;
|
||||
JS_GetLength (ctx, replaced, &arr_len);
|
||||
wota_write_array (&enc->wb, arr_len);
|
||||
for (int64_t i = 0; i < arr_len; i++) {
|
||||
JSValue elem_val = JS_GetPropertyNumber (ctx, replaced, i);
|
||||
wota_encode_value (enc, elem_val, replaced, JS_NewInt32 (ctx, (int32_t)i));
|
||||
JS_FreeValue (ctx, elem_val);
|
||||
}
|
||||
wota_stack_pop (enc);
|
||||
break;
|
||||
}
|
||||
JSValue adata = JS_NULL;
|
||||
if (!JS_IsNull (ctx->actor_sym)) {
|
||||
int has = JS_HasPropertyKey (ctx, replaced, ctx->actor_sym);
|
||||
if (has > 0) adata = JS_GetPropertyKey (ctx, replaced, ctx->actor_sym);
|
||||
}
|
||||
if (!JS_IsNull (adata)) {
|
||||
wota_write_sym (&enc->wb, WOTA_PRIVATE);
|
||||
wota_encode_value (enc, adata, replaced, JS_NULL);
|
||||
JS_FreeValue (ctx, adata);
|
||||
break;
|
||||
}
|
||||
JS_FreeValue (ctx, adata);
|
||||
if (wota_stack_has (enc, replaced)) {
|
||||
enc->cycle = 1;
|
||||
break;
|
||||
}
|
||||
wota_stack_push (enc, replaced);
|
||||
JSValue to_json = JS_GetPropertyStr (ctx, replaced, "toJSON");
|
||||
if (JS_IsFunction (to_json)) {
|
||||
JSValue result = JS_Call (ctx, to_json, replaced, 0, NULL);
|
||||
JS_FreeValue (ctx, to_json);
|
||||
if (!JS_IsException (result)) {
|
||||
wota_encode_value (enc, result, holder, key);
|
||||
JS_FreeValue (ctx, result);
|
||||
} else
|
||||
wota_write_sym (&enc->wb, WOTA_NULL);
|
||||
wota_stack_pop (enc);
|
||||
break;
|
||||
}
|
||||
JS_FreeValue (ctx, to_json);
|
||||
encode_object_properties (enc, replaced, holder);
|
||||
wota_stack_pop (enc);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
wota_write_sym (&enc->wb, WOTA_NULL);
|
||||
break;
|
||||
}
|
||||
JS_FreeValue (ctx, replaced);
|
||||
}
|
||||
|
||||
static char *decode_wota_value (JSContext *ctx, char *data_ptr, JSValue *out_val, JSValue holder, JSValue key, JSValue reviver) {
|
||||
uint64_t first_word = *(uint64_t *)data_ptr;
|
||||
int type = (int)(first_word & 0xffU);
|
||||
switch (type) {
|
||||
case WOTA_INT: {
|
||||
long long val;
|
||||
data_ptr = wota_read_int (&val, data_ptr);
|
||||
*out_val = JS_NewInt64 (ctx, val);
|
||||
break;
|
||||
}
|
||||
case WOTA_FLOAT: {
|
||||
double d;
|
||||
data_ptr = wota_read_float (&d, data_ptr);
|
||||
*out_val = JS_NewFloat64 (ctx, d);
|
||||
break;
|
||||
}
|
||||
case WOTA_SYM: {
|
||||
int scode;
|
||||
data_ptr = wota_read_sym (&scode, data_ptr);
|
||||
if (scode == WOTA_PRIVATE) {
|
||||
JSGCRef inner_ref, obj_ref2;
|
||||
JS_PushGCRef (ctx, &inner_ref);
|
||||
JS_PushGCRef (ctx, &obj_ref2);
|
||||
inner_ref.val = JS_NULL;
|
||||
data_ptr = decode_wota_value (ctx, data_ptr, &inner_ref.val, holder, JS_NULL, reviver);
|
||||
obj_ref2.val = JS_NewObject (ctx);
|
||||
if (!JS_IsNull (ctx->actor_sym))
|
||||
JS_SetPropertyKey (ctx, obj_ref2.val, ctx->actor_sym, inner_ref.val);
|
||||
JS_CellStone (ctx, obj_ref2.val);
|
||||
*out_val = obj_ref2.val;
|
||||
JS_PopGCRef (ctx, &obj_ref2);
|
||||
JS_PopGCRef (ctx, &inner_ref);
|
||||
} else if (scode == WOTA_NULL) *out_val = JS_NULL;
|
||||
else if (scode == WOTA_FALSE) *out_val = JS_NewBool (ctx, 0);
|
||||
else if (scode == WOTA_TRUE) *out_val = JS_NewBool (ctx, 1);
|
||||
else *out_val = JS_NULL;
|
||||
break;
|
||||
}
|
||||
case WOTA_BLOB: {
|
||||
long long blen;
|
||||
char *bdata = NULL;
|
||||
data_ptr = wota_read_blob (&blen, &bdata, data_ptr);
|
||||
*out_val = bdata ? js_new_blob_stoned_copy (ctx, (uint8_t *)bdata, (size_t)blen) : js_new_blob_stoned_copy (ctx, NULL, 0);
|
||||
if (bdata) sys_free (bdata);
|
||||
break;
|
||||
}
|
||||
case WOTA_TEXT: {
|
||||
char *utf8 = NULL;
|
||||
data_ptr = wota_read_text (&utf8, data_ptr);
|
||||
*out_val = JS_NewString (ctx, utf8 ? utf8 : "");
|
||||
if (utf8) sys_free (utf8);
|
||||
break;
|
||||
}
|
||||
case WOTA_ARR: {
|
||||
long long c;
|
||||
data_ptr = wota_read_array (&c, data_ptr);
|
||||
JSGCRef arr_ref;
|
||||
JS_PushGCRef (ctx, &arr_ref);
|
||||
arr_ref.val = JS_NewArrayLen (ctx, c);
|
||||
for (long long i = 0; i < c; i++) {
|
||||
JSGCRef elem_ref;
|
||||
JS_PushGCRef (ctx, &elem_ref);
|
||||
elem_ref.val = JS_NULL;
|
||||
JSValue idx_key = JS_NewInt32 (ctx, (int32_t)i);
|
||||
data_ptr = decode_wota_value (ctx, data_ptr, &elem_ref.val, arr_ref.val, idx_key, reviver);
|
||||
JS_SetPropertyNumber (ctx, arr_ref.val, i, elem_ref.val);
|
||||
JS_PopGCRef (ctx, &elem_ref);
|
||||
}
|
||||
*out_val = arr_ref.val;
|
||||
JS_PopGCRef (ctx, &arr_ref);
|
||||
break;
|
||||
}
|
||||
case WOTA_REC: {
|
||||
long long c;
|
||||
data_ptr = wota_read_record (&c, data_ptr);
|
||||
JSGCRef obj_ref;
|
||||
JS_PushGCRef (ctx, &obj_ref);
|
||||
obj_ref.val = JS_NewObject (ctx);
|
||||
for (long long i = 0; i < c; i++) {
|
||||
char *tkey = NULL;
|
||||
size_t key_len;
|
||||
data_ptr = wota_read_text_len (&key_len, &tkey, data_ptr);
|
||||
if (!tkey) continue;
|
||||
JSGCRef prop_key_ref, sub_val_ref;
|
||||
JS_PushGCRef (ctx, &prop_key_ref);
|
||||
JS_PushGCRef (ctx, &sub_val_ref);
|
||||
prop_key_ref.val = JS_NewStringLen (ctx, tkey, key_len);
|
||||
sub_val_ref.val = JS_NULL;
|
||||
data_ptr = decode_wota_value (ctx, data_ptr, &sub_val_ref.val, obj_ref.val, prop_key_ref.val, reviver);
|
||||
JS_SetPropertyStr (ctx, obj_ref.val, tkey, sub_val_ref.val);
|
||||
JS_PopGCRef (ctx, &sub_val_ref);
|
||||
JS_PopGCRef (ctx, &prop_key_ref);
|
||||
sys_free (tkey);
|
||||
}
|
||||
*out_val = obj_ref.val;
|
||||
JS_PopGCRef (ctx, &obj_ref);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
data_ptr += 8;
|
||||
*out_val = JS_NULL;
|
||||
break;
|
||||
}
|
||||
if (!JS_IsNull (reviver)) {
|
||||
JSValue key_val = JS_IsNull (key) ? JS_NULL : JS_DupValue (ctx, key);
|
||||
JSValue args[2] = { key_val, JS_DupValue (ctx, *out_val) };
|
||||
JSValue revived = JS_Call (ctx, reviver, holder, 2, args);
|
||||
JS_FreeValue (ctx, args[0]);
|
||||
JS_FreeValue (ctx, args[1]);
|
||||
if (!JS_IsException (revived)) {
|
||||
JS_FreeValue (ctx, *out_val);
|
||||
*out_val = revived;
|
||||
} else
|
||||
JS_FreeValue (ctx, revived);
|
||||
}
|
||||
return data_ptr;
|
||||
}
|
||||
|
||||
void *value2wota (JSContext *ctx, JSValue v, JSValue replacer, size_t *bytes) {
|
||||
JSGCRef val_ref, rep_ref;
|
||||
JS_PushGCRef (ctx, &val_ref);
|
||||
JS_PushGCRef (ctx, &rep_ref);
|
||||
val_ref.val = v;
|
||||
rep_ref.val = replacer;
|
||||
|
||||
WotaEncodeContext enc_s, *enc = &enc_s;
|
||||
|
||||
enc->ctx = ctx;
|
||||
enc->visited_stack = NULL;
|
||||
enc->cycle = 0;
|
||||
enc->replacer = rep_ref.val;
|
||||
wota_buffer_init (&enc->wb, 16);
|
||||
wota_encode_value (enc, val_ref.val, JS_NULL, JS_NULL);
|
||||
if (enc->cycle) {
|
||||
wota_stack_free (enc);
|
||||
wota_buffer_free (&enc->wb);
|
||||
JS_PopGCRef (ctx, &rep_ref);
|
||||
JS_PopGCRef (ctx, &val_ref);
|
||||
return NULL;
|
||||
}
|
||||
wota_stack_free (enc);
|
||||
size_t total_bytes = enc->wb.size * sizeof (uint64_t);
|
||||
void *wota = sys_realloc (enc->wb.data, total_bytes);
|
||||
if (bytes) *bytes = total_bytes;
|
||||
JS_PopGCRef (ctx, &rep_ref);
|
||||
JS_PopGCRef (ctx, &val_ref);
|
||||
return wota;
|
||||
}
|
||||
|
||||
JSValue wota2value (JSContext *ctx, void *wota) {
|
||||
JSGCRef holder_ref, result_ref;
|
||||
JS_PushGCRef (ctx, &holder_ref);
|
||||
JS_PushGCRef (ctx, &result_ref);
|
||||
result_ref.val = JS_NULL;
|
||||
holder_ref.val = JS_NewObject (ctx);
|
||||
decode_wota_value (ctx, wota, &result_ref.val, holder_ref.val, JS_NULL, JS_NULL);
|
||||
JSValue result = result_ref.val;
|
||||
JS_PopGCRef (ctx, &result_ref);
|
||||
JS_PopGCRef (ctx, &holder_ref);
|
||||
return result;
|
||||
}
|
||||
|
||||
static JSValue js_wota_encode (JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
if (argc < 1) return JS_RaiseDisrupt (ctx, "wota.encode requires at least 1 argument");
|
||||
size_t total_bytes;
|
||||
void *wota = value2wota (ctx, argv[0], JS_IsFunction (argv[1]) ? argv[1] : JS_NULL, &total_bytes);
|
||||
JSValue ret = js_new_blob_stoned_copy (ctx, wota, total_bytes);
|
||||
sys_free (wota);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static JSValue js_wota_decode (JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
if (argc < 1) return JS_NULL;
|
||||
size_t len;
|
||||
uint8_t *buf = js_get_blob_data (ctx, &len, argv[0]);
|
||||
if (buf == (uint8_t *)-1) return JS_EXCEPTION;
|
||||
if (!buf || len == 0) return JS_RaiseDisrupt (ctx, "No blob data present");
|
||||
JSValue reviver = (argc > 1 && JS_IsFunction (argv[1])) ? argv[1] : JS_NULL;
|
||||
char *data_ptr = (char *)buf;
|
||||
JSGCRef result_ref, holder_ref, empty_key_ref;
|
||||
JS_PushGCRef (ctx, &result_ref);
|
||||
JS_PushGCRef (ctx, &holder_ref);
|
||||
JS_PushGCRef (ctx, &empty_key_ref);
|
||||
result_ref.val = JS_NULL;
|
||||
holder_ref.val = JS_NewObject (ctx);
|
||||
empty_key_ref.val = JS_NewString (ctx, "");
|
||||
decode_wota_value (ctx, data_ptr, &result_ref.val, holder_ref.val, empty_key_ref.val, reviver);
|
||||
JSValue result = result_ref.val;
|
||||
JS_PopGCRef (ctx, &empty_key_ref);
|
||||
JS_PopGCRef (ctx, &holder_ref);
|
||||
JS_PopGCRef (ctx, &result_ref);
|
||||
return result;
|
||||
}
|
||||
|
||||
static const JSCFunctionListEntry js_wota_funcs[] = {
|
||||
JS_CFUNC_DEF ("encode", 2, js_wota_encode),
|
||||
JS_CFUNC_DEF ("decode", 2, js_wota_decode),
|
||||
};
|
||||
|
||||
JSValue js_core_internal_wota_use (JSContext *ctx) {
|
||||
JSGCRef exports_ref;
|
||||
JS_PushGCRef (ctx, &exports_ref);
|
||||
exports_ref.val = JS_NewObject (ctx);
|
||||
JS_SetPropertyFunctionList (ctx, exports_ref.val, js_wota_funcs, sizeof (js_wota_funcs) / sizeof (js_wota_funcs[0]));
|
||||
JSValue result = exports_ref.val;
|
||||
JS_PopGCRef (ctx, &exports_ref);
|
||||
return result;
|
||||
}
|
||||
67
json.c
Normal file
67
json.c
Normal file
@@ -0,0 +1,67 @@
|
||||
#include "cell.h"
|
||||
#include <string.h>
|
||||
|
||||
/* JSON uses JS_KEY_empty for reviver — define locally from public constants */
|
||||
#ifndef JS_KEY_empty
|
||||
#define JS_KEY_empty ((JSValue)JS_TAG_STRING_IMM)
|
||||
#endif
|
||||
|
||||
static JSValue js_cell_json_encode (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
if (argc < 1)
|
||||
return JS_RaiseDisrupt (ctx, "json.encode requires at least 1 argument");
|
||||
|
||||
int pretty = argc <= 1 || JS_ToBool (ctx, argv[1]);
|
||||
JSValue space = pretty ? JS_NewInt32 (ctx, 2) : JS_NULL;
|
||||
JSValue replacer = JS_NULL;
|
||||
if (argc > 2 && JS_IsFunction (argv[2]))
|
||||
replacer = argv[2];
|
||||
else if (argc > 3 && JS_IsArray (argv[3]))
|
||||
replacer = argv[3];
|
||||
JSValue result = JS_JSONStringify (ctx, argv[0], replacer, space, pretty);
|
||||
return result;
|
||||
}
|
||||
|
||||
static JSValue js_cell_json_decode (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
if (argc < 1)
|
||||
return JS_RaiseDisrupt (ctx, "json.decode requires at least 1 argument");
|
||||
|
||||
if (!JS_IsText (argv[0]))
|
||||
return JS_RaiseDisrupt (ctx, "couldn't parse text: not a string");
|
||||
|
||||
const char *str = JS_ToCString (ctx, argv[0]);
|
||||
if (!str) return JS_EXCEPTION;
|
||||
|
||||
size_t len = strlen (str);
|
||||
JSValue result = JS_ParseJSON (ctx, str, len, "<json>");
|
||||
JS_FreeCString (ctx, str);
|
||||
|
||||
/* Apply reviver if provided */
|
||||
if (argc > 1 && JS_IsFunction (argv[1]) && !JS_IsException (result)) {
|
||||
/* Create wrapper object to pass to reviver */
|
||||
JSValue wrapper = JS_NewObject (ctx);
|
||||
JS_SetPropertyStr (ctx, wrapper, "", result);
|
||||
|
||||
JSValue holder = wrapper;
|
||||
JSValue key = JS_KEY_empty;
|
||||
JSValue args[2] = { key, JS_GetProperty (ctx, holder, key) };
|
||||
JSValue final = JS_Call (ctx, argv[1], holder, 2, args);
|
||||
result = final;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static const JSCFunctionListEntry js_cell_json_funcs[] = {
|
||||
JS_CFUNC_DEF ("encode", 1, js_cell_json_encode),
|
||||
JS_CFUNC_DEF ("decode", 1, js_cell_json_decode),
|
||||
};
|
||||
|
||||
JSValue js_core_json_use (JSContext *ctx) {
|
||||
JSGCRef obj_ref;
|
||||
JS_PushGCRef (ctx, &obj_ref);
|
||||
obj_ref.val = JS_NewObject (ctx);
|
||||
JS_SetPropertyFunctionList (ctx, obj_ref.val, js_cell_json_funcs, countof (js_cell_json_funcs));
|
||||
JSValue result = obj_ref.val;
|
||||
JS_PopGCRef (ctx, &obj_ref);
|
||||
return result;
|
||||
}
|
||||
66
math/cycles.c
Normal file
66
math/cycles.c
Normal file
@@ -0,0 +1,66 @@
|
||||
#include "cell.h"
|
||||
#include "cell_math.h"
|
||||
#include <math.h>
|
||||
|
||||
#define TWOPI (2.0 * 3.14159265358979323846264338327950288419716939937510)
|
||||
|
||||
static JSValue js_math_cyc_arc_cosine (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
double x;
|
||||
if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION;
|
||||
return JS_NewFloat64 (ctx, acos (x) / TWOPI);
|
||||
}
|
||||
|
||||
static JSValue js_math_cyc_arc_sine (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
double x;
|
||||
if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION;
|
||||
return JS_NewFloat64 (ctx, asin (x) / TWOPI);
|
||||
}
|
||||
|
||||
static JSValue js_math_cyc_arc_tangent (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
double x;
|
||||
if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION;
|
||||
return JS_NewFloat64 (ctx, atan (x) / TWOPI);
|
||||
}
|
||||
|
||||
static JSValue js_math_cyc_cosine (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
double x;
|
||||
if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION;
|
||||
return JS_NewFloat64 (ctx, cos (x * TWOPI));
|
||||
}
|
||||
|
||||
static JSValue js_math_cyc_sine (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
double x;
|
||||
if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION;
|
||||
return JS_NewFloat64 (ctx, sin (x * TWOPI));
|
||||
}
|
||||
|
||||
static JSValue js_math_cyc_tangent (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
double x;
|
||||
if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION;
|
||||
return JS_NewFloat64 (ctx, tan (x * TWOPI));
|
||||
}
|
||||
|
||||
static const JSCFunctionListEntry js_math_cycles_funcs[]
|
||||
= { JS_CFUNC_DEF ("arc_cosine", 1, js_math_cyc_arc_cosine),
|
||||
JS_CFUNC_DEF ("arc_sine", 1, js_math_cyc_arc_sine),
|
||||
JS_CFUNC_DEF ("arc_tangent", 1, js_math_cyc_arc_tangent),
|
||||
JS_CFUNC_DEF ("cosine", 1, js_math_cyc_cosine),
|
||||
JS_CFUNC_DEF ("sine", 1, js_math_cyc_sine),
|
||||
JS_CFUNC_DEF ("tangent", 1, js_math_cyc_tangent),
|
||||
JS_CFUNC_DEF ("ln", 1, js_math_ln),
|
||||
JS_CFUNC_DEF ("log", 1, js_math_log10),
|
||||
JS_CFUNC_DEF ("log2", 1, js_math_log2),
|
||||
JS_CFUNC_DEF ("power", 2, js_math_power),
|
||||
JS_CFUNC_DEF ("root", 2, js_math_root),
|
||||
JS_CFUNC_DEF ("sqrt", 1, js_math_sqrt),
|
||||
JS_CFUNC_DEF ("e", 1, js_math_e) };
|
||||
|
||||
JSValue js_core_math_cycles_use (JSContext *ctx) {
|
||||
JSGCRef obj_ref;
|
||||
JS_PushGCRef (ctx, &obj_ref);
|
||||
obj_ref.val = JS_NewObject (ctx);
|
||||
JS_SetPropertyFunctionList (ctx, obj_ref.val, js_math_cycles_funcs, countof (js_math_cycles_funcs));
|
||||
JSValue result = obj_ref.val;
|
||||
JS_PopGCRef (ctx, &obj_ref);
|
||||
return result;
|
||||
}
|
||||
67
math/degrees.c
Normal file
67
math/degrees.c
Normal file
@@ -0,0 +1,67 @@
|
||||
#include "cell.h"
|
||||
#include "cell_math.h"
|
||||
#include <math.h>
|
||||
|
||||
#define DEG2RAD (3.14159265358979323846264338327950288419716939937510 / 180.0)
|
||||
#define RAD2DEG (180.0 / 3.14159265358979323846264338327950288419716939937510)
|
||||
|
||||
static JSValue js_math_deg_arc_cosine (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
double x;
|
||||
if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION;
|
||||
return JS_NewFloat64 (ctx, acos (x) * RAD2DEG);
|
||||
}
|
||||
|
||||
static JSValue js_math_deg_arc_sine (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
double x;
|
||||
if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION;
|
||||
return JS_NewFloat64 (ctx, asin (x) * RAD2DEG);
|
||||
}
|
||||
|
||||
static JSValue js_math_deg_arc_tangent (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
double x;
|
||||
if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION;
|
||||
return JS_NewFloat64 (ctx, atan (x) * RAD2DEG);
|
||||
}
|
||||
|
||||
static JSValue js_math_deg_cosine (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
double x;
|
||||
if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION;
|
||||
return JS_NewFloat64 (ctx, cos (x * DEG2RAD));
|
||||
}
|
||||
|
||||
static JSValue js_math_deg_sine (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
double x;
|
||||
if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION;
|
||||
return JS_NewFloat64 (ctx, sin (x * DEG2RAD));
|
||||
}
|
||||
|
||||
static JSValue js_math_deg_tangent (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
double x;
|
||||
if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION;
|
||||
return JS_NewFloat64 (ctx, tan (x * DEG2RAD));
|
||||
}
|
||||
|
||||
static const JSCFunctionListEntry js_math_degrees_funcs[]
|
||||
= { JS_CFUNC_DEF ("arc_cosine", 1, js_math_deg_arc_cosine),
|
||||
JS_CFUNC_DEF ("arc_sine", 1, js_math_deg_arc_sine),
|
||||
JS_CFUNC_DEF ("arc_tangent", 1, js_math_deg_arc_tangent),
|
||||
JS_CFUNC_DEF ("cosine", 1, js_math_deg_cosine),
|
||||
JS_CFUNC_DEF ("sine", 1, js_math_deg_sine),
|
||||
JS_CFUNC_DEF ("tangent", 1, js_math_deg_tangent),
|
||||
JS_CFUNC_DEF ("ln", 1, js_math_ln),
|
||||
JS_CFUNC_DEF ("log", 1, js_math_log10),
|
||||
JS_CFUNC_DEF ("log2", 1, js_math_log2),
|
||||
JS_CFUNC_DEF ("power", 2, js_math_power),
|
||||
JS_CFUNC_DEF ("root", 2, js_math_root),
|
||||
JS_CFUNC_DEF ("sqrt", 1, js_math_sqrt),
|
||||
JS_CFUNC_DEF ("e", 1, js_math_e) };
|
||||
|
||||
JSValue js_core_math_degrees_use (JSContext *ctx) {
|
||||
JSGCRef obj_ref;
|
||||
JS_PushGCRef (ctx, &obj_ref);
|
||||
obj_ref.val = JS_NewObject (ctx);
|
||||
JS_SetPropertyFunctionList (ctx, obj_ref.val, js_math_degrees_funcs, countof (js_math_degrees_funcs));
|
||||
JSValue result = obj_ref.val;
|
||||
JS_PopGCRef (ctx, &obj_ref);
|
||||
return result;
|
||||
}
|
||||
64
math/radians.c
Normal file
64
math/radians.c
Normal file
@@ -0,0 +1,64 @@
|
||||
#include "cell.h"
|
||||
#include "cell_math.h"
|
||||
#include <math.h>
|
||||
|
||||
static JSValue js_math_rad_arc_cosine (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
double x;
|
||||
if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION;
|
||||
return JS_NewFloat64 (ctx, acos (x));
|
||||
}
|
||||
|
||||
static JSValue js_math_rad_arc_sine (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
double x;
|
||||
if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION;
|
||||
return JS_NewFloat64 (ctx, asin (x));
|
||||
}
|
||||
|
||||
static JSValue js_math_rad_arc_tangent (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
double x;
|
||||
if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION;
|
||||
return JS_NewFloat64 (ctx, atan (x));
|
||||
}
|
||||
|
||||
static JSValue js_math_rad_cosine (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
double x;
|
||||
if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION;
|
||||
return JS_NewFloat64 (ctx, cos (x));
|
||||
}
|
||||
|
||||
static JSValue js_math_rad_sine (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
double x;
|
||||
if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION;
|
||||
return JS_NewFloat64 (ctx, sin (x));
|
||||
}
|
||||
|
||||
static JSValue js_math_rad_tangent (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
double x;
|
||||
if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION;
|
||||
return JS_NewFloat64 (ctx, tan (x));
|
||||
}
|
||||
|
||||
static const JSCFunctionListEntry js_math_radians_funcs[]
|
||||
= { JS_CFUNC_DEF ("arc_cosine", 1, js_math_rad_arc_cosine),
|
||||
JS_CFUNC_DEF ("arc_sine", 1, js_math_rad_arc_sine),
|
||||
JS_CFUNC_DEF ("arc_tangent", 1, js_math_rad_arc_tangent),
|
||||
JS_CFUNC_DEF ("cosine", 1, js_math_rad_cosine),
|
||||
JS_CFUNC_DEF ("sine", 1, js_math_rad_sine),
|
||||
JS_CFUNC_DEF ("tangent", 1, js_math_rad_tangent),
|
||||
JS_CFUNC_DEF ("ln", 1, js_math_ln),
|
||||
JS_CFUNC_DEF ("log", 1, js_math_log10),
|
||||
JS_CFUNC_DEF ("log2", 1, js_math_log2),
|
||||
JS_CFUNC_DEF ("power", 2, js_math_power),
|
||||
JS_CFUNC_DEF ("root", 2, js_math_root),
|
||||
JS_CFUNC_DEF ("sqrt", 1, js_math_sqrt),
|
||||
JS_CFUNC_DEF ("e", 1, js_math_e) };
|
||||
|
||||
JSValue js_core_math_radians_use (JSContext *ctx) {
|
||||
JSGCRef obj_ref;
|
||||
JS_PushGCRef (ctx, &obj_ref);
|
||||
obj_ref.val = JS_NewObject (ctx);
|
||||
JS_SetPropertyFunctionList (ctx, obj_ref.val, js_math_radians_funcs, countof (js_math_radians_funcs));
|
||||
JSValue result = obj_ref.val;
|
||||
JS_PopGCRef (ctx, &obj_ref);
|
||||
return result;
|
||||
}
|
||||
16
meson.build
16
meson.build
@@ -64,11 +64,18 @@ src += ['qbe_helpers.c']
|
||||
src += ['qbe_backend.c']
|
||||
|
||||
scripts = [
|
||||
'json.c',
|
||||
'internal/nota.c',
|
||||
'internal/wota.c',
|
||||
'math/radians.c',
|
||||
'math/degrees.c',
|
||||
'math/cycles.c',
|
||||
'src/cell_math.c',
|
||||
'debug/js.c',
|
||||
'qop.c',
|
||||
'wildstar.c',
|
||||
'internal/qop.c',
|
||||
'internal/wildstar.c',
|
||||
'fit.c',
|
||||
'crypto.c',
|
||||
'internal/crypto.c',
|
||||
'internal/kim.c',
|
||||
'internal/time.c',
|
||||
'debug/debug.c',
|
||||
@@ -76,7 +83,6 @@ scripts = [
|
||||
'internal/fd.c',
|
||||
'net/http.c',
|
||||
'net/enet.c',
|
||||
'wildstar.c',
|
||||
'archive/miniz.c',
|
||||
'source/cJSON.c'
|
||||
]
|
||||
@@ -86,7 +92,7 @@ foreach file: scripts
|
||||
endforeach
|
||||
|
||||
srceng = 'source'
|
||||
includes = [srceng, 'internal', 'debug', 'net', 'archive', 'src/qbe']
|
||||
includes = [srceng, 'internal', 'debug', 'net', 'archive', 'src/qbe', 'src']
|
||||
|
||||
foreach file : src
|
||||
full_path = join_paths(srceng, file)
|
||||
|
||||
7
parse.cm
7
parse.cm
@@ -1727,6 +1727,13 @@ var parse = function(tokens, src, filename, tokenizer) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (kind == "this") {
|
||||
if (scope.function_nr == 0) {
|
||||
sem_error(expr, "'this' cannot be used at the top level of a program")
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
if (kind == "[") {
|
||||
sem_check_expr(scope, expr.left)
|
||||
sem_check_expr(scope, expr.right)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// cell qopconv - Convert QOP archive formats
|
||||
|
||||
var fd = use('fd')
|
||||
var qop = use('qop')
|
||||
var qop = use('internal/qop')
|
||||
|
||||
function print_usage() {
|
||||
log.console("Usage: qopconv [OPTION...] FILE...")
|
||||
|
||||
137
query.ce
137
query.ce
@@ -1,29 +1,39 @@
|
||||
// cell query — Semantic queries across packages.
|
||||
//
|
||||
// Usage:
|
||||
// cell query --this [--top|--fn] [<package>] this references
|
||||
// cell query --intrinsic <name> [<package>] Find built-in intrinsic usage
|
||||
// cell query --decl <name> [<package>] Variable declarations by name
|
||||
// cell query --help Show usage
|
||||
// cell query <name> Find all references to <name>
|
||||
// cell query <name> --top Top-level references only
|
||||
// cell query <name> --fn Inside-function references only
|
||||
// cell query <name> --channels Show <name>.<prop> usage summary
|
||||
// cell query --decl <name> Find declarations of <name>
|
||||
// cell query --decl <name> --fn Only function declarations
|
||||
// cell query --intrinsic <name> Find intrinsic usage
|
||||
// cell query --excess-args Find call sites with >4 args
|
||||
// cell query --help
|
||||
|
||||
var shop = use('internal/shop')
|
||||
var query_mod = use('query')
|
||||
var analyze_mod = use('analyze')
|
||||
var fd = use('fd')
|
||||
|
||||
var mode = null
|
||||
var name = null
|
||||
var this_scope = null
|
||||
var scope_filter = null
|
||||
var kind_filter = null
|
||||
var pkg_filter = null
|
||||
var show_help = false
|
||||
var i = 0
|
||||
|
||||
for (i = 0; i < length(args); i++) {
|
||||
if (args[i] == '--this') {
|
||||
mode = "this"
|
||||
} else if (args[i] == '--top') {
|
||||
this_scope = "top"
|
||||
if (args[i] == '--top') {
|
||||
scope_filter = "top"
|
||||
} else if (args[i] == '--fn') {
|
||||
this_scope = "fn"
|
||||
if (mode == "decl") {
|
||||
kind_filter = "fn"
|
||||
} else {
|
||||
scope_filter = "fn"
|
||||
}
|
||||
} else if (args[i] == '--channels') {
|
||||
mode = "channels"
|
||||
} else if (args[i] == '--intrinsic') {
|
||||
mode = "intrinsic"
|
||||
if (i + 1 < length(args) && !starts_with(args[i + 1], '-')) {
|
||||
@@ -42,13 +52,26 @@ for (i = 0; i < length(args); i++) {
|
||||
log.error('--decl requires a name')
|
||||
mode = "error"
|
||||
}
|
||||
} else if (args[i] == '--excess-args') {
|
||||
mode = "excess_args"
|
||||
} else if (args[i] == '--help' || args[i] == '-h') {
|
||||
show_help = true
|
||||
} else if (!starts_with(args[i], '-')) {
|
||||
pkg_filter = args[i]
|
||||
if (name == null && mode == null) {
|
||||
name = args[i]
|
||||
mode = "refs"
|
||||
} else {
|
||||
pkg_filter = args[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --channels requires a name from positional arg
|
||||
if (mode == "channels" && name == null) {
|
||||
log.error('--channels requires a name (e.g., cell query log --channels)')
|
||||
mode = "error"
|
||||
}
|
||||
|
||||
var all_files = null
|
||||
var files = []
|
||||
var j = 0
|
||||
@@ -56,6 +79,10 @@ var idx = null
|
||||
var hits = null
|
||||
var hit = null
|
||||
var k = 0
|
||||
var ch_result = null
|
||||
var props = null
|
||||
var prop = null
|
||||
var parts = null
|
||||
|
||||
// Use return pattern to avoid closure-over-object issue with disruption.
|
||||
var safe_index = function(path) {
|
||||
@@ -65,21 +92,24 @@ var safe_index = function(path) {
|
||||
}
|
||||
|
||||
if (show_help) {
|
||||
log.console("Usage: cell query [options] [<package>]")
|
||||
log.console("Usage: cell query [options] [<name>] [<package>]")
|
||||
log.console("")
|
||||
log.console("Semantic queries across packages.")
|
||||
log.console("")
|
||||
log.console("Options:")
|
||||
log.console(" --this All this references")
|
||||
log.console(" --this --top Top-level this only (not inside functions)")
|
||||
log.console(" --this --fn this inside functions only")
|
||||
log.console(" --intrinsic <name> Find built-in intrinsic usage (e.g., print)")
|
||||
log.console(" --decl <name> Variable declarations by name")
|
||||
log.console("Commands:")
|
||||
log.console(" <name> Find all references to <name>")
|
||||
log.console(" <name> --top Top-level references only")
|
||||
log.console(" <name> --fn Inside-function references only")
|
||||
log.console(" <name> --channels Show <name>.<prop> usage summary")
|
||||
log.console(" --decl <name> Find declarations of <name>")
|
||||
log.console(" --decl <name> --fn Only function declarations")
|
||||
log.console(" --intrinsic <name> Find intrinsic usage")
|
||||
log.console(" --excess-args Find call sites with >4 args")
|
||||
log.console("")
|
||||
log.console("Without a package argument, searches all installed packages.")
|
||||
} else if (mode == null || mode == "error") {
|
||||
if (mode != "error") {
|
||||
log.error('Specify --this, --intrinsic, or --decl. Use --help for usage.')
|
||||
log.error('Specify a name or --decl/--intrinsic/--excess-args. Use --help for usage.')
|
||||
}
|
||||
} else {
|
||||
all_files = shop.all_script_paths()
|
||||
@@ -98,22 +128,61 @@ if (show_help) {
|
||||
idx = safe_index(files[j].full_path)
|
||||
if (idx == null) continue
|
||||
|
||||
hits = null
|
||||
if (mode == "this") {
|
||||
hits = query_mod.find_this(idx, this_scope)
|
||||
if (mode == "refs") {
|
||||
hits = analyze_mod.find_refs(idx, name, scope_filter)
|
||||
if (hits != null && length(hits) > 0) {
|
||||
for (k = 0; k < length(hits); k++) {
|
||||
hit = hits[k]
|
||||
if (hit.span != null) {
|
||||
if (hit.enclosing != null) {
|
||||
log.console(`${files[j].package}:${files[j].rel_path}:${text(hit.span.from_row)}:${text(hit.span.from_col)}: ${hit.name} (in: ${hit.enclosing})`)
|
||||
} else {
|
||||
log.console(`${files[j].package}:${files[j].rel_path}:${text(hit.span.from_row)}:${text(hit.span.from_col)}: ${hit.name} (top-level)`)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (mode == "channels") {
|
||||
ch_result = analyze_mod.channels(idx, name)
|
||||
if (ch_result != null && ch_result.summary != null) {
|
||||
props = array(ch_result.summary)
|
||||
if (length(props) > 0) {
|
||||
parts = []
|
||||
for (k = 0; k < length(props); k++) {
|
||||
prop = props[k]
|
||||
parts[] = `${prop}(${text(ch_result.summary[prop])})`
|
||||
}
|
||||
log.console(`${files[j].package}:${files[j].rel_path}: ${text(parts, " ")}`)
|
||||
}
|
||||
}
|
||||
} else if (mode == "intrinsic") {
|
||||
hits = query_mod.intrinsic(idx, name)
|
||||
hits = analyze_mod.find_intrinsic(idx, name)
|
||||
if (hits != null && length(hits) > 0) {
|
||||
for (k = 0; k < length(hits); k++) {
|
||||
hit = hits[k]
|
||||
if (hit.span != null) {
|
||||
log.console(`${files[j].package}:${files[j].rel_path}:${text(hit.span.from_row)}:${text(hit.span.from_col)}: ${hit.name}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (mode == "decl") {
|
||||
hits = query_mod.find_decl(idx, name, null)
|
||||
}
|
||||
|
||||
if (hits != null && length(hits) > 0) {
|
||||
for (k = 0; k < length(hits); k++) {
|
||||
hit = hits[k]
|
||||
if (hit.span != null) {
|
||||
log.console(`${files[j].package}:${files[j].rel_path}:${text(hit.span.from_row)}:${text(hit.span.from_col)}: ${hit.name}`)
|
||||
} else if (hit.decl_span != null) {
|
||||
log.console(`${files[j].package}:${files[j].rel_path}:${text(hit.decl_span.from_row)}:${text(hit.decl_span.from_col)}: ${hit.kind} ${hit.name}`)
|
||||
hits = analyze_mod.find_decls(idx, name, kind_filter)
|
||||
if (hits != null && length(hits) > 0) {
|
||||
for (k = 0; k < length(hits); k++) {
|
||||
hit = hits[k]
|
||||
if (hit.decl_span != null) {
|
||||
log.console(`${files[j].package}:${files[j].rel_path}:${text(hit.decl_span.from_row)}:${text(hit.decl_span.from_col)}: ${hit.kind} ${hit.name}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (mode == "excess_args") {
|
||||
hits = analyze_mod.excess_args(idx)
|
||||
if (hits != null && length(hits) > 0) {
|
||||
for (k = 0; k < length(hits); k++) {
|
||||
hit = hits[k]
|
||||
if (hit.span != null) {
|
||||
log.console(`${files[j].package}:${files[j].rel_path}:${text(hit.span.from_row)}:${text(hit.span.from_col)}: ${hit.callee}() called with ${text(hit.args_count)} args (max 4)`)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
55
query.cm
55
query.cm
@@ -1,62 +1,19 @@
|
||||
// query.cm — Semantic queries over index data.
|
||||
//
|
||||
// All functions take an index object (from index.cm) and return arrays of hits.
|
||||
// query.cm — Backward-compatible wrapper delegating to analyze.cm.
|
||||
|
||||
var analyze = use('analyze')
|
||||
|
||||
var query = {}
|
||||
|
||||
// Find this references. scope: "top" (top-level only), "fn" (in functions), null (all).
|
||||
query.find_this = function(idx, scope) {
|
||||
var hits = []
|
||||
var i = 0
|
||||
var ref = null
|
||||
while (i < length(idx.references)) {
|
||||
ref = idx.references[i]
|
||||
if (ref.name == "this") {
|
||||
if (scope == null) {
|
||||
hits[] = ref
|
||||
} else if (scope == "top" && ref.enclosing == null) {
|
||||
hits[] = ref
|
||||
} else if (scope == "fn" && ref.enclosing != null) {
|
||||
hits[] = ref
|
||||
}
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
return hits
|
||||
return analyze.find_refs(idx, "this", scope)
|
||||
}
|
||||
|
||||
// Intrinsic usage: find refs to a built-in name (e.g., print).
|
||||
query.intrinsic = function(idx, name) {
|
||||
var hits = []
|
||||
var i = 0
|
||||
var ref = null
|
||||
if (idx.intrinsic_refs == null) return hits
|
||||
while (i < length(idx.intrinsic_refs)) {
|
||||
ref = idx.intrinsic_refs[i]
|
||||
if (ref.name == name) {
|
||||
hits[] = ref
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
return hits
|
||||
return analyze.find_intrinsic(idx, name)
|
||||
}
|
||||
|
||||
// Variable declarations matching a name and optional kind filter.
|
||||
// kind is one of "var", "def", "fn", "param", or null (any).
|
||||
query.find_decl = function(idx, name, kind) {
|
||||
var hits = []
|
||||
var i = 0
|
||||
var sym = null
|
||||
while (i < length(idx.symbols)) {
|
||||
sym = idx.symbols[i]
|
||||
if (sym.name == name) {
|
||||
if (kind == null || sym.kind == kind) {
|
||||
hits[] = sym
|
||||
}
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
return hits
|
||||
return analyze.find_decls(idx, name, kind)
|
||||
}
|
||||
|
||||
return query
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
var rnd = {}
|
||||
|
||||
var os = use('os')
|
||||
var os = use('internal/os')
|
||||
|
||||
rnd.random = function()
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
// Loads <module>.cm via use() (interpreted) and via shop.use_native() (native),
|
||||
// runs both and compares results and timing.
|
||||
|
||||
var os = use('os')
|
||||
var os = use('internal/os')
|
||||
var fd = use('fd')
|
||||
var shop = use('internal/shop')
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Usage: ./cell --dev --seed run_native_seed benches/fibonacci
|
||||
|
||||
var fd = use("fd")
|
||||
var os = use("os")
|
||||
var os = use("internal/os")
|
||||
|
||||
if (length(args) < 1) {
|
||||
print("usage: cell --dev --seed run_native_seed <module>")
|
||||
|
||||
2
seed.ce
2
seed.ce
@@ -17,7 +17,7 @@
|
||||
|
||||
var fd = use("fd")
|
||||
var json = use("json")
|
||||
var os = use("os")
|
||||
var os = use("internal/os")
|
||||
var shop = use("internal/shop")
|
||||
var tokenize = use("tokenize")
|
||||
var parse = use("parse")
|
||||
|
||||
@@ -30,6 +30,7 @@ cell_rt *root_cell = NULL;
|
||||
static char *shop_path = NULL;
|
||||
static char *core_path = NULL;
|
||||
static int native_mode = 0;
|
||||
static int warn_mode = 1;
|
||||
static JSRuntime *g_runtime = NULL;
|
||||
|
||||
// Compute blake2b hash of data and return hex string (caller must free)
|
||||
@@ -260,7 +261,7 @@ void actor_disrupt(cell_rt *crt)
|
||||
actor_free(crt);
|
||||
}
|
||||
|
||||
JSValue js_core_os_use(JSContext *js);
|
||||
JSValue js_core_internal_os_use(JSContext *js);
|
||||
JSValue js_core_json_use(JSContext *js);
|
||||
|
||||
void script_startup(cell_rt *prt)
|
||||
@@ -316,7 +317,7 @@ void script_startup(cell_rt *prt)
|
||||
JS_AddGCRef(js, &boot_env_ref);
|
||||
boot_env_ref.val = JS_NewObject(js);
|
||||
JSValue btmp;
|
||||
btmp = js_core_os_use(js);
|
||||
btmp = js_core_internal_os_use(js);
|
||||
JS_SetPropertyStr(js, boot_env_ref.val, "os", btmp);
|
||||
if (core_path) {
|
||||
btmp = JS_NewString(js, core_path);
|
||||
@@ -346,7 +347,7 @@ void script_startup(cell_rt *prt)
|
||||
JS_AddGCRef(js, &env_ref);
|
||||
env_ref.val = JS_NewObject(js);
|
||||
JSValue tmp;
|
||||
tmp = js_core_os_use(js);
|
||||
tmp = js_core_internal_os_use(js);
|
||||
JS_SetPropertyStr(js, env_ref.val, "os", tmp);
|
||||
tmp = js_core_json_use(js);
|
||||
JS_SetPropertyStr(js, env_ref.val, "json", tmp);
|
||||
@@ -523,6 +524,9 @@ int cell_init(int argc, char **argv)
|
||||
} else if (strcmp(argv[arg_start], "--native") == 0) {
|
||||
native_mode = 1;
|
||||
arg_start++;
|
||||
} else if (strcmp(argv[arg_start], "--no-warn") == 0) {
|
||||
warn_mode = 0;
|
||||
arg_start++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@@ -608,7 +612,7 @@ int cell_init(int argc, char **argv)
|
||||
JS_AddGCRef(ctx, &boot_env_ref);
|
||||
boot_env_ref.val = JS_NewObject(ctx);
|
||||
JSValue btmp;
|
||||
btmp = js_core_os_use(ctx);
|
||||
btmp = js_core_internal_os_use(ctx);
|
||||
JS_SetPropertyStr(ctx, boot_env_ref.val, "os", btmp);
|
||||
btmp = JS_NewString(ctx, core_path);
|
||||
JS_SetPropertyStr(ctx, boot_env_ref.val, "core_path", btmp);
|
||||
@@ -652,7 +656,7 @@ int cell_init(int argc, char **argv)
|
||||
JS_AddGCRef(ctx, &env_ref);
|
||||
env_ref.val = JS_NewObject(ctx);
|
||||
JSValue tmp;
|
||||
tmp = js_core_os_use(ctx);
|
||||
tmp = js_core_internal_os_use(ctx);
|
||||
JS_SetPropertyStr(ctx, env_ref.val, "os", tmp);
|
||||
tmp = JS_NewString(ctx, core_path);
|
||||
JS_SetPropertyStr(ctx, env_ref.val, "core_path", tmp);
|
||||
@@ -661,11 +665,14 @@ int cell_init(int argc, char **argv)
|
||||
JS_SetPropertyStr(ctx, env_ref.val, "actorsym", JS_DupValue(ctx, cli_rt->actor_sym_ref.val));
|
||||
tmp = js_core_json_use(ctx);
|
||||
JS_SetPropertyStr(ctx, env_ref.val, "json", tmp);
|
||||
if (native_mode) {
|
||||
if (native_mode || !warn_mode) {
|
||||
JSGCRef init_ref;
|
||||
JS_AddGCRef(ctx, &init_ref);
|
||||
init_ref.val = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, init_ref.val, "native_mode", JS_NewBool(ctx, 1));
|
||||
if (native_mode)
|
||||
JS_SetPropertyStr(ctx, init_ref.val, "native_mode", JS_NewBool(ctx, 1));
|
||||
if (!warn_mode)
|
||||
JS_SetPropertyStr(ctx, init_ref.val, "no_warn", JS_NewBool(ctx, 1));
|
||||
JS_SetPropertyStr(ctx, env_ref.val, "init", init_ref.val);
|
||||
JS_DeleteGCRef(ctx, &init_ref);
|
||||
} else {
|
||||
|
||||
@@ -1197,6 +1197,8 @@ int js_string_compare_value_nocase (JSContext *ctx, JSValue op1, JSValue op2);
|
||||
JSValue js_regexp_constructor (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
|
||||
int JS_HasProperty (JSContext *ctx, JSValue obj, JSValue prop);
|
||||
int JS_HasPropertyKey (JSContext *ctx, JSValue obj, JSValue key);
|
||||
JSValue JS_GetPropertyKey (JSContext *ctx, JSValue this_obj, JSValue key);
|
||||
int JS_SetPropertyKey (JSContext *ctx, JSValue this_obj, JSValue key, JSValue val);
|
||||
void *js_realloc_rt (void *ptr, size_t size);
|
||||
char *js_strdup_rt (const char *str);
|
||||
JSValue JS_ConcatString (JSContext *ctx, JSValue op1, JSValue op2);
|
||||
|
||||
1218
source/runtime.c
1218
source/runtime.c
File diff suppressed because it is too large
Load Diff
50
src/cell_math.c
Normal file
50
src/cell_math.c
Normal file
@@ -0,0 +1,50 @@
|
||||
#include "cell_math.h"
|
||||
#include <math.h>
|
||||
|
||||
JSValue js_math_e (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
double power = 1.0;
|
||||
if (argc > 0 && !JS_IsNull (argv[0])) {
|
||||
if (JS_ToFloat64 (ctx, &power, argv[0]) < 0) return JS_EXCEPTION;
|
||||
}
|
||||
return JS_NewFloat64 (ctx, exp (power));
|
||||
}
|
||||
|
||||
JSValue js_math_ln (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
double x;
|
||||
if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION;
|
||||
return JS_NewFloat64 (ctx, log (x));
|
||||
}
|
||||
|
||||
JSValue js_math_log10 (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
double x;
|
||||
if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION;
|
||||
return JS_NewFloat64 (ctx, log10 (x));
|
||||
}
|
||||
|
||||
JSValue js_math_log2 (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
double x;
|
||||
if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION;
|
||||
return JS_NewFloat64 (ctx, log2 (x));
|
||||
}
|
||||
|
||||
JSValue js_math_power (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
double x, y;
|
||||
if (argc < 2) return JS_NULL;
|
||||
if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION;
|
||||
if (JS_ToFloat64 (ctx, &y, argv[1]) < 0) return JS_EXCEPTION;
|
||||
return JS_NewFloat64 (ctx, pow (x, y));
|
||||
}
|
||||
|
||||
JSValue js_math_root (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
double x, n;
|
||||
if (argc < 2) return JS_NULL;
|
||||
if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION;
|
||||
if (JS_ToFloat64 (ctx, &n, argv[1]) < 0) return JS_EXCEPTION;
|
||||
return JS_NewFloat64 (ctx, pow (x, 1.0 / n));
|
||||
}
|
||||
|
||||
JSValue js_math_sqrt (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
double x;
|
||||
if (JS_ToFloat64 (ctx, &x, argv[0]) < 0) return JS_EXCEPTION;
|
||||
return JS_NewFloat64 (ctx, sqrt (x));
|
||||
}
|
||||
14
src/cell_math.h
Normal file
14
src/cell_math.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef CELL_MATH_H
|
||||
#define CELL_MATH_H
|
||||
|
||||
#include "cell.h"
|
||||
|
||||
JSValue js_math_e (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
|
||||
JSValue js_math_ln (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
|
||||
JSValue js_math_log10 (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
|
||||
JSValue js_math_log2 (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
|
||||
JSValue js_math_power (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
|
||||
JSValue js_math_root (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
|
||||
JSValue js_math_sqrt (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
|
||||
|
||||
#endif
|
||||
@@ -6,6 +6,7 @@
|
||||
// cell streamline --ir <file> Human-readable IR
|
||||
// cell streamline --check <file> Warnings only (e.g. high slot count)
|
||||
// cell streamline --types <file> Optimized IR with type annotations
|
||||
// cell streamline --diagnose <file> Run diagnostics (type errors/warnings)
|
||||
|
||||
var fd = use("fd")
|
||||
var json = use("json")
|
||||
@@ -15,8 +16,11 @@ var show_stats = false
|
||||
var show_ir = false
|
||||
var show_check = false
|
||||
var show_types = false
|
||||
var show_diagnose = false
|
||||
var filename = null
|
||||
var i = 0
|
||||
var di = 0
|
||||
var diag = null
|
||||
|
||||
for (i = 0; i < length(args); i++) {
|
||||
if (args[i] == '--stats') {
|
||||
@@ -27,8 +31,10 @@ for (i = 0; i < length(args); i++) {
|
||||
show_check = true
|
||||
} else if (args[i] == '--types') {
|
||||
show_types = true
|
||||
} else if (args[i] == '--diagnose') {
|
||||
show_diagnose = true
|
||||
} else if (args[i] == '--help' || args[i] == '-h') {
|
||||
log.console("Usage: cell streamline [--stats] [--ir] [--check] [--types] <file>")
|
||||
log.console("Usage: cell streamline [--stats] [--ir] [--check] [--types] [--diagnose] <file>")
|
||||
$stop()
|
||||
} else if (!starts_with(args[i], '-')) {
|
||||
filename = args[i]
|
||||
@@ -36,7 +42,7 @@ for (i = 0; i < length(args); i++) {
|
||||
}
|
||||
|
||||
if (!filename) {
|
||||
print("usage: cell streamline [--stats] [--ir] [--check] [--types] <file>")
|
||||
print("usage: cell streamline [--stats] [--ir] [--check] [--types] [--diagnose] <file>")
|
||||
$stop()
|
||||
}
|
||||
|
||||
@@ -46,10 +52,19 @@ if (show_stats) {
|
||||
before = json.decode(json.encode(shop.mcode_file(filename)))
|
||||
}
|
||||
|
||||
var optimized = shop.compile_file(filename)
|
||||
// For --diagnose, compile with _warn enabled to get diagnostics
|
||||
var optimized = null
|
||||
var compiled = null
|
||||
if (show_diagnose) {
|
||||
compiled = shop.mcode_file(filename)
|
||||
compiled._warn = true
|
||||
optimized = use('streamline')(compiled)
|
||||
} else {
|
||||
optimized = shop.compile_file(filename)
|
||||
}
|
||||
|
||||
// If no flags, default to full JSON output
|
||||
if (!show_stats && !show_ir && !show_check && !show_types) {
|
||||
if (!show_stats && !show_ir && !show_check && !show_types && !show_diagnose) {
|
||||
print(json.encode(optimized, true))
|
||||
$stop()
|
||||
}
|
||||
@@ -343,4 +358,18 @@ if (show_stats) {
|
||||
print('---')
|
||||
}
|
||||
|
||||
if (show_diagnose) {
|
||||
if (optimized._diagnostics != null && length(optimized._diagnostics) > 0) {
|
||||
di = 0
|
||||
while (di < length(optimized._diagnostics)) {
|
||||
diag = optimized._diagnostics[di]
|
||||
print(`${diag.file}:${text(diag.line)}:${text(diag.col)}: ${diag.severity}: ${diag.message}`)
|
||||
di = di + 1
|
||||
}
|
||||
print(`\n${text(length(optimized._diagnostics))} diagnostic(s)`)
|
||||
} else {
|
||||
print("No diagnostics.")
|
||||
}
|
||||
}
|
||||
|
||||
$stop()
|
||||
|
||||
237
streamline.cm
237
streamline.cm
@@ -1829,6 +1829,236 @@ var streamline = function(ir, log) {
|
||||
return null
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// Pass: diagnose_function — emit diagnostics from type info
|
||||
// Runs after dead code elimination on surviving instructions.
|
||||
// =========================================================
|
||||
var diagnose_function = function(func, ctx, ir) {
|
||||
var param_types = ctx.param_types
|
||||
var write_types = ctx.write_types
|
||||
var instructions = func.instructions
|
||||
var nr_args = func.nr_args != null ? func.nr_args : 0
|
||||
var num_instr = 0
|
||||
var base_types = null
|
||||
var cur_types = null
|
||||
var i = 0
|
||||
var j = 0
|
||||
var instr = null
|
||||
var op = null
|
||||
var n = 0
|
||||
var line = 0
|
||||
var col = 0
|
||||
var known = null
|
||||
var filename = ir.filename != null ? ir.filename : "<unknown>"
|
||||
var frame_callee = {}
|
||||
var frame_argc = {}
|
||||
var callee_slot = null
|
||||
var obj_type = null
|
||||
var key_type = null
|
||||
var module_slots = {}
|
||||
var slot_arity = {}
|
||||
var ms_i = 0
|
||||
var ms = null
|
||||
var exp_info = null
|
||||
var f_slot_key = null
|
||||
var cs = null
|
||||
var argc = null
|
||||
var known_arity = null
|
||||
var load_field_null = false
|
||||
|
||||
// Build module_slots map from ir._module_summaries
|
||||
if (ir._module_summaries != null) {
|
||||
ms_i = 0
|
||||
while (ms_i < length(ir._module_summaries)) {
|
||||
ms = ir._module_summaries[ms_i]
|
||||
module_slots[text(ms.slot)] = ms.summary
|
||||
ms_i = ms_i + 1
|
||||
}
|
||||
}
|
||||
|
||||
if (instructions == null || length(instructions) == 0) return null
|
||||
|
||||
num_instr = length(instructions)
|
||||
|
||||
// Pre-compute base types from params + write-invariant types
|
||||
base_types = array(func.nr_slots)
|
||||
j = 1
|
||||
while (j <= nr_args) {
|
||||
if (param_types != null && param_types[j] != null) {
|
||||
base_types[j] = param_types[j]
|
||||
}
|
||||
j = j + 1
|
||||
}
|
||||
if (write_types != null) {
|
||||
j = 0
|
||||
while (j < length(write_types)) {
|
||||
if (write_types[j] != null) {
|
||||
base_types[j] = write_types[j]
|
||||
}
|
||||
j = j + 1
|
||||
}
|
||||
}
|
||||
|
||||
cur_types = array(base_types)
|
||||
|
||||
var emit = function(severity, line, col, message) {
|
||||
ir._diagnostics[] = {
|
||||
severity: severity,
|
||||
file: filename,
|
||||
line: line,
|
||||
col: col,
|
||||
message: message
|
||||
}
|
||||
}
|
||||
|
||||
i = 0
|
||||
while (i < num_instr) {
|
||||
instr = instructions[i]
|
||||
|
||||
if (is_text(instr)) {
|
||||
// Label — reset types to base
|
||||
if (!starts_with(instr, "_nop_")) {
|
||||
cur_types = array(base_types)
|
||||
}
|
||||
i = i + 1
|
||||
continue
|
||||
}
|
||||
|
||||
if (!is_array(instr)) {
|
||||
i = i + 1
|
||||
continue
|
||||
}
|
||||
|
||||
op = instr[0]
|
||||
n = length(instr)
|
||||
line = instr[n - 2]
|
||||
col = instr[n - 1]
|
||||
|
||||
// Track frame/invoke correlation
|
||||
if (op == "frame" || op == "goframe") {
|
||||
frame_callee[text(instr[1])] = instr[2]
|
||||
if (n > 4) {
|
||||
frame_argc[text(instr[1])] = instr[3]
|
||||
}
|
||||
}
|
||||
|
||||
// --- Error checks (proven to always disrupt) ---
|
||||
|
||||
if (op == "frame" || op == "goframe") {
|
||||
callee_slot = instr[2]
|
||||
known = cur_types[callee_slot]
|
||||
if (known == T_NULL) {
|
||||
emit("error", line, col, "invoking null — will always disrupt")
|
||||
} else if (known != null && known != T_UNKNOWN && known != T_FUNCTION && known != T_RECORD) {
|
||||
emit("error", line, col, `invoking ${known} — will always disrupt`)
|
||||
}
|
||||
}
|
||||
|
||||
if (op == "invoke" || op == "tail_invoke") {
|
||||
f_slot_key = text(instr[1])
|
||||
cs = frame_callee[f_slot_key]
|
||||
argc = frame_argc[f_slot_key]
|
||||
if (cs != null && argc != null) {
|
||||
known_arity = slot_arity[text(cs)]
|
||||
if (known_arity != null) {
|
||||
if (argc > known_arity) {
|
||||
emit("error", line, col, `function expects ${text(known_arity)} args, called with ${text(argc)}`)
|
||||
} else if (argc < known_arity) {
|
||||
emit("warning", line, col, `function expects ${text(known_arity)} args, called with ${text(argc)}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (op == "store_field") {
|
||||
obj_type = cur_types[instr[1]]
|
||||
if (obj_type == T_TEXT) {
|
||||
emit("error", line, col, "storing property on text — text is immutable")
|
||||
} else if (obj_type == T_ARRAY) {
|
||||
emit("error", line, col, "storing named property on array — use index or push")
|
||||
}
|
||||
}
|
||||
|
||||
if (op == "store_index") {
|
||||
obj_type = cur_types[instr[1]]
|
||||
if (obj_type == T_TEXT) {
|
||||
emit("error", line, col, "storing index on text — text is immutable")
|
||||
} else if (obj_type == T_RECORD) {
|
||||
emit("error", line, col, "storing numeric index on record — use text key")
|
||||
}
|
||||
}
|
||||
|
||||
if (op == "store_dynamic") {
|
||||
obj_type = cur_types[instr[1]]
|
||||
if (obj_type == T_TEXT) {
|
||||
emit("error", line, col, "storing on text — text is immutable")
|
||||
}
|
||||
}
|
||||
|
||||
if (op == "push") {
|
||||
obj_type = cur_types[instr[1]]
|
||||
if (obj_type != null && obj_type != T_UNKNOWN && obj_type != T_ARRAY) {
|
||||
emit("error", line, col, `push on ${obj_type} — only arrays support push`)
|
||||
}
|
||||
}
|
||||
|
||||
// Note: arithmetic (add/subtract/etc), bitwise, and concat ops are NOT
|
||||
// checked here because the mcode generator emits type-dispatch guards
|
||||
// before these instructions. The guards ensure correct types at runtime.
|
||||
|
||||
// --- Warning checks (likely bug) ---
|
||||
|
||||
load_field_null = false
|
||||
if (op == "load_field") {
|
||||
obj_type = cur_types[instr[2]]
|
||||
if (obj_type == T_ARRAY) {
|
||||
emit("warning", line, col, "named property access on array — always returns null")
|
||||
load_field_null = true
|
||||
} else if (obj_type == T_TEXT) {
|
||||
emit("warning", line, col, "named property access on text — always returns null")
|
||||
load_field_null = true
|
||||
}
|
||||
// Cross-module: check if obj is a module with known exports
|
||||
ms = module_slots[text(instr[2])]
|
||||
if (ms != null && ms.exports != null && is_text(instr[3])) {
|
||||
exp_info = ms.exports[instr[3]]
|
||||
if (exp_info == null) {
|
||||
emit("warning", line, col, `module does not export '${instr[3]}'`)
|
||||
} else if (exp_info.type == "function") {
|
||||
cur_types[instr[1]] = T_FUNCTION
|
||||
slot_arity[text(instr[1])] = exp_info.arity
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (op == "load_dynamic") {
|
||||
obj_type = cur_types[instr[2]]
|
||||
key_type = cur_types[instr[3]]
|
||||
if (obj_type == T_ARRAY && key_type == T_TEXT) {
|
||||
emit("warning", line, col, "text key on array — always returns null")
|
||||
}
|
||||
if (obj_type == T_TEXT && key_type != null && key_type != T_UNKNOWN && key_type != T_INT) {
|
||||
emit("warning", line, col, `${key_type} key on text — requires integer index`)
|
||||
}
|
||||
if (obj_type == T_RECORD && key_type != null && key_type != T_UNKNOWN && key_type != T_TEXT) {
|
||||
emit("warning", line, col, `${key_type} key on record — requires text key`)
|
||||
}
|
||||
}
|
||||
|
||||
// Update types for this instruction
|
||||
track_types(cur_types, instr)
|
||||
|
||||
// Override: load_field on array/text always returns null
|
||||
if (load_field_null) {
|
||||
cur_types[instr[1]] = T_NULL
|
||||
}
|
||||
|
||||
i = i + 1
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// Compose all passes
|
||||
// =========================================================
|
||||
@@ -1905,6 +2135,9 @@ var streamline = function(ir, log) {
|
||||
}
|
||||
|
||||
run_cycle("")
|
||||
if (ir._warn) {
|
||||
diagnose_function(func, {param_types: param_types, write_types: write_types}, ir)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -1914,6 +2147,10 @@ var streamline = function(ir, log) {
|
||||
// eliminator to mis-optimize comparisons on closure-written variables.
|
||||
mark_closure_writes(ir)
|
||||
|
||||
if (ir._warn) {
|
||||
ir._diagnostics = []
|
||||
}
|
||||
|
||||
// Process main function
|
||||
if (ir.main != null) {
|
||||
optimize_function(ir.main, log)
|
||||
|
||||
2
test.ce
2
test.ce
@@ -19,7 +19,7 @@ var gc_after_each_test = false
|
||||
var verify_ir = false
|
||||
var diff_mode = false
|
||||
|
||||
var os_ref = use('os')
|
||||
var os_ref = use('internal/os')
|
||||
var analyze = os_ref.analyze
|
||||
var run_ast_fn = os_ref.run_ast_fn
|
||||
var run_ast_noopt_fn = os_ref.run_ast_noopt_fn
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
var Blob = use('blob');
|
||||
var os = use('os');
|
||||
var os = use('internal/os');
|
||||
|
||||
function assert(condition, message) {
|
||||
if (!condition) disrupt
|
||||
|
||||
129
tests/compile.cm
Normal file
129
tests/compile.cm
Normal file
@@ -0,0 +1,129 @@
|
||||
// Compile-time diagnostics tests — verify the type checker catches errors
|
||||
var fd = use('fd')
|
||||
var shop = use('internal/shop')
|
||||
var streamline = use('streamline')
|
||||
|
||||
var tmpfile = "/tmp/_cell_compile_test.cm"
|
||||
|
||||
function write_source(src) {
|
||||
fd.slurpwrite(tmpfile, stone(blob(src)))
|
||||
}
|
||||
|
||||
function get_diagnostics(src) {
|
||||
write_source(src)
|
||||
var compiled = shop.mcode_file(tmpfile)
|
||||
compiled._warn = true
|
||||
var optimized = streamline(compiled)
|
||||
if (optimized._diagnostics == null) return []
|
||||
return optimized._diagnostics
|
||||
}
|
||||
|
||||
function has_diagnostic(diags, severity, pattern) {
|
||||
var i = 0
|
||||
while (i < length(diags)) {
|
||||
if (diags[i].severity == severity && search(diags[i].message, pattern) != null) {
|
||||
return true
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function expect(diags, severity, pattern) {
|
||||
if (!has_diagnostic(diags, severity, pattern)) {
|
||||
return `expected ${severity} matching '${pattern}', got ${text(length(diags))} diagnostic(s)`
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function expect_clean(diags) {
|
||||
if (length(diags) > 0) {
|
||||
return `expected no diagnostics, got ${text(length(diags))}`
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
// === Store errors ===
|
||||
|
||||
test_store_field_on_array: function() {
|
||||
var d = get_diagnostics("var a = []\na[\"x\"] = 1")
|
||||
return expect(d, "error", "storing named property on array")
|
||||
},
|
||||
|
||||
test_store_index_on_record: function() {
|
||||
var d = get_diagnostics("var a = {}\na[1] = 1")
|
||||
return expect(d, "error", "storing numeric index on record")
|
||||
},
|
||||
|
||||
test_store_field_on_text: function() {
|
||||
var d = get_diagnostics("var s = \"hello\"\ns.x = 1")
|
||||
return expect(d, "error", "storing property on text")
|
||||
},
|
||||
|
||||
test_store_index_on_text: function() {
|
||||
var d = get_diagnostics("var s = \"hello\"\ns[0] = 1")
|
||||
return expect(d, "error", "storing index on text")
|
||||
},
|
||||
|
||||
test_push_on_text: function() {
|
||||
var d = get_diagnostics("var s = \"hello\"\ns[] = 1")
|
||||
return expect(d, "error", "push on text")
|
||||
},
|
||||
|
||||
test_push_on_record: function() {
|
||||
var d = get_diagnostics("var r = {}\nr[] = 1")
|
||||
return expect(d, "error", "push on record")
|
||||
},
|
||||
|
||||
// === Invoke errors ===
|
||||
|
||||
test_invoke_null: function() {
|
||||
var d = get_diagnostics("var x = null\nx()")
|
||||
return expect(d, "error", "invoking null")
|
||||
},
|
||||
|
||||
test_invoke_number: function() {
|
||||
var d = get_diagnostics("var x = 42\nx()")
|
||||
return expect(d, "error", "invoking")
|
||||
},
|
||||
|
||||
test_invoke_text: function() {
|
||||
var d = get_diagnostics("var x = \"hello\"\nx()")
|
||||
return expect(d, "error", "invoking")
|
||||
},
|
||||
|
||||
// === Warnings ===
|
||||
|
||||
test_field_on_array_warns: function() {
|
||||
var d = get_diagnostics("var a = [1, 2]\nvar x = a.name")
|
||||
return expect(d, "warning", "named property access on array")
|
||||
},
|
||||
|
||||
test_field_on_text_warns: function() {
|
||||
var d = get_diagnostics("var s = \"hello\"\nvar x = s.name")
|
||||
return expect(d, "warning", "named property access on text")
|
||||
},
|
||||
|
||||
test_record_key_on_record_warns: function() {
|
||||
var d = get_diagnostics("var r = {a: 1}\nvar k = {}\nvar x = r[k]")
|
||||
return expect(d, "warning", "record key on record")
|
||||
},
|
||||
|
||||
// === Clean code produces no diagnostics ===
|
||||
|
||||
test_clean_array_ops: function() {
|
||||
var d = get_diagnostics("var a = [1, 2, 3]\nvar x = a[0]\na[1] = 5\na[] = 4")
|
||||
return expect_clean(d)
|
||||
},
|
||||
|
||||
test_clean_record_ops: function() {
|
||||
var d = get_diagnostics("var r = {a: 1, b: 2}\nvar x = r.a\nr.c = 3")
|
||||
return expect_clean(d)
|
||||
},
|
||||
|
||||
test_clean_function_call: function() {
|
||||
var d = get_diagnostics("function f(a, b) { return a + b }\nvar x = f(1, 2)")
|
||||
return expect_clean(d)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
var kim = use("kim");
|
||||
var kim = use("internal/kim");
|
||||
var blob = use('blob')
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
var nota = use('nota');
|
||||
var os = use('os');
|
||||
var nota = use('internal/nota');
|
||||
var os = use('internal/os');
|
||||
var blob = use('blob')
|
||||
|
||||
var EPSILON = 1e-12
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
var wota = use('wota')
|
||||
var os = use('os')
|
||||
var wota = use('internal/wota')
|
||||
var os = use('internal/os')
|
||||
var blob = use('blob')
|
||||
var math = use('math/radians')
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
var shop = use('internal/shop')
|
||||
var build = use('build')
|
||||
var fd = use('fd')
|
||||
var os = use('os')
|
||||
var os = use('internal/os')
|
||||
|
||||
var target_pkg = null
|
||||
var run_build = false
|
||||
|
||||
@@ -1927,7 +1927,8 @@ run("array for", function() {
|
||||
// ============================================================================
|
||||
|
||||
run("array string key disrupts", function() {
|
||||
if (!should_disrupt(function() { var a = []; a["a"] = 1 })) fail("array should not use string as key")
|
||||
var f = function(a) { a["a"] = 1 }
|
||||
if (!should_disrupt(function() { f([]) })) fail("array should not use string as key")
|
||||
})
|
||||
|
||||
run("array object key disrupts", function() {
|
||||
@@ -1947,7 +1948,8 @@ run("array array key disrupts", function() {
|
||||
})
|
||||
|
||||
run("obj number key disrupts", function() {
|
||||
if (!should_disrupt(function() { var a = {}; a[1] = 1 })) fail("object should not use number as key")
|
||||
var f = function(a) { a[1] = 1 }
|
||||
if (!should_disrupt(function() { f({}) })) fail("object should not use number as key")
|
||||
})
|
||||
|
||||
run("obj array key disrupts", function() {
|
||||
|
||||
Reference in New Issue
Block a user