79 Commits

Author SHA1 Message Date
Anders Eknert d0350b326e Add array.flatten built-in function (#8232)
Originally meant to be `array.concat_n`, but this name is better
as the behavior of this function differs from `array.concat` —
namely that `array.flatten` accepts any type of valued in the
input array. Only arrays are however flattened, and the rest
are appended directly to the flattened output.

Note that this function only flattens at the topmost level of
the input array — not recursively! A cursory look
at a few other languages suggest a single level is the common case.
But if others feel we should flstten more, I'm happy to make an update.

The C code for a Wasm implementstion here is cowboy coded, and
I did not manage to run the tests on my machine due to some
`docker` <-> `container` differences. I mostly just imitated
the existing code in the array category. I doubt it'll work
on the first try, but only CI can judge me.

Also:
- Remove `opa fmt` step from the Rego CI step, as this is done by
  Regal anyway a little later in the list of tasks.
- Replace some hard-coded `docker` names in the `Makefile` with `$(DOCKER)`
- Added name of built-in function missing to the unsupportedBuiltinErr
  error, as it has happened a few times now that I've used `:=` in a
  query, and had no clue what built-in it referred to.

Fixes #8226

Signed-off-by: Anders Eknert <anders.eknert@apple.com>
2026-01-27 21:46:11 +00:00
Johan Fylling 8e410b830a String interpolation (#8109)
Adding string interpolation support to the Rego language.

An interpolated string is composed of a template-string that can contain zero or more template-expressions that interpolates values into the string generated at eval-time.

Requires the `template_strings` capability feature and `internal.template_string` built-in function.

Implements: #4733
2025-12-16 11:47:04 +01:00
Johan Fylling 34349c5a6c topdown: Adding cap to caches for regex and glob built-in functions (#6846)
Fixing possible memory leak where caches grow uncontrollably when large amounts of regexes or globs are generated or originate from the input document.

Fixes: #6828

Signed-off-by: Johan Fylling <johan.dev@fylling.se>
2024-07-05 11:18:37 +02:00
Chris Telfer d3b8772286 wasm: Don't order small blocks and add bulk free
This patch removes ordered block storage in fixed-sized block freelists
in the OPA WASM memory allocator.  Variable-sized block allocation still
orders blocks so that free() can coalesce them back into larger sized
blocks.  This greatly reduces the runtime of opa_free() for fixed-size
blocks as it turns it from an O(N) operation to an O(1) operation.

This comes at the cost that reducing the heap_ptr implicitly on
opa_free() becomes impractical since reduction will stop at the first
fixed-sied block regardless of whether it is allocated or not. In
practice, what this means is that the allocator can never combine
fixed-size and variable-sized blocks.  However, it was rarely able to do
so previously: only when the two blocks happened to be free at the same
time and line up with the heap_ptr.

This patch also adds support for a new function called opa_free_bulk() that
enables releasing memory objects always in O(1) time per object and
O(N log N) worst case for releasing N objects.  The patch works by
freeing variable-sized objects (which would normally take O(N) time per
free) to a temporary holding list and setting a flag indicating that the
next variable-sized allocation needs to merge said holding list.

When releasing the holding list, the memory allocator first merge-sorts
in address-order the released blocks and then merges and coalesces them
into the variable-sized block list in address order.  This takes at most
O(max(M+N, N log N)) time where M is the number of blocks on the
variable freelist and N is the number of blocks bulk freed.

The patch also updates the __opa_value_free() function to take a new
parameter named 'bulk' which directs the function passes to its various
type-specific subroutines.  Every time one of the type-sepcific
subroutines goes to free an object it invokes either opa_free() or
opa_free_bulk() depending upon the 'bulk' parameter.  (This is
abstracted by a function __opa_free_maybe_bulk() in value.c)
Calls to opa_value_free() or opa_value_free_shallow(), will set the
the 'bulk' parameter to false preserving the existing behavior.
However, the opa_value_add_path() and opa_value_remove_path()
functions will invoke the function with 'bulk' set to true to ensure
that the cascaded free operations on objects each take only O(1) time.

Finally, the patch re-enables the RESTAuthzAllow100Paths benchmark.

Fixes: #5901
Signed-off-by: Chris Telfer <chris.telfer@sophos.com>
2023-05-23 12:29:20 -07:00
Chris Telfer d718975b5d Fix memory leaks in WASM when modifying data doc
This commit fixes several memory leaks in the WASM engine that occur
when a caller mixes incremental calls to opa_value_path_add() /
opa_value_path_remove() with actual policy evaluations.  The issue
occurs due to a combination of lack of deep free of internal data
structures and the fact that eval() and opa_eval() calls reset the heap
to free temporary memory that they previous allocated.

More details about the issues and their fix are described in detail at
https://github.com/open-policy-agent/opa/issues/5785.

The changes in this patch fall into 5 categories:

1. Adding support for both internal WASM functions and external WASM
   callers to perform a "deep" free of OPA values by freeing not only
   the immediate object memory but all the opa values it refers to.
   The opa_value_free() function now does this by default and is
   also an exported function.  The opa_value_free_shallow() is added
   for the few cases where shallow frees are required, primarily in
   eval()-invoked functions.
2. Enable stashing of free blocks prior to eval() and opa_eval() calls.
   Eval calls will always leak free blocks due to the way that
   opa_heap_ptr_get() works.  This patch adds three new exports allowing
   the user to save this memory from leaking.
   * opa_heap_blocks_stash() -- saves free heap blocks to shadow
     freelists.
   * opa_heap_blocks_restore() -- restores the allocated heap blocks from
     shadow freelists.
   * opa_heap_stash_clear() -- discard any saved heap blocks on the shadow
     freelists.  (this is used for resetting VM heap to an initial state)
3. Update the WASM calling conventions in the SDK.  This includes using
   the new APIs to avoid leaking memory when adding or removing data
   from the data doc.  It requires bumping the WASM ABI to 1.3
4. Adding unit tests for the WASM ABI 1.3 functions.
5. Adding documenttion for the WASM ABI 1.3 functions.

Fixes: #5785

Signed-off-by: Chris Telfer <chris.telfer@sophos.com>
2023-04-28 13:38:35 -07:00
Kevin Swiber b6184bd258 builtins: add object.keys (#5392)
The `object.keys` function will return a set of all top-level keys on
a given object.  Since object keys in Rego don't have the same
restrictions as names in JSON name-value pairs, we also ensure
support for non-string key types.

Fixes #5363.

Signed-off-by: Kevin Swiber <kswiber@gmail.com>
2022-11-17 20:55:35 +01:00
Philip Conrad 3d6ad2e716 wasm/tests/test: Fix broken WASM C tests for is_type builtins. (#5075)
This commit fixes an issue around the WASM C tests breaking due to not
getting updated during recent changes to the is_type builtin functions.

Signed-off-by: Philip Conrad <philipaconrad@gmail.com>
2022-09-01 09:24:58 +02:00
Jacob Martin e7130a888d Add any_prefix_match and any_suffix_match functions for bulk prefix and suffix matching. (#4997)
Signed-off-by: Jakub Martin <kubam@spacelift.io>
2022-08-18 21:33:59 +02:00
Philip Conrad 1648bd728a topdown/reachable: Fix missing operand type checks. (#4956)
This commit ensures that the `graph.reachable` and `graph.reachable_paths` builtins check the types of both of their operands, and return type errors instead of default values if a wrong type is provided.

Fixes #4951
2022-08-02 18:45:07 -04:00
Phạm Hữu Vinh da4a10044b topdown: support glob.match without delimiters (#4933)
Using null for delimiters disables delimiters in glob matching. Preferable over regex on some cases for performance reasons.

Fixes #4923.

Signed-off-by: vinhph0906 <vinhph0906@gmail.com>
Co-authored-by: Stephan Renatus <stephan.renatus@gmail.com>
2022-07-25 16:51:28 +02:00
Kristian Svalland 3250a2c858 wasm: Add native support for json.is_valid (#4204)
wasm: Add support for WASM and simple tests.
internal: Add opa_json_is_valid to map of wasm built-ins.
docs: Indicate that WASM support is now available for json.is_valid.

Fixes #4140

Signed-off-by: Kristian Svalland <kristian.svalland@gmail.com>
2022-01-11 07:47:33 +01:00
Kristian Svalland 6f81c4a620 Add array.reverse(array) and strings.reverse(string) built-in functions. (#4161)
The function `array.reverse` takes an array as an argument, and returns an array with a reversed order of elements.
The function `strings.reverse` takes a string as an argument, and returns a string with a reversed order of unicode code points.
WASM support is included for both built-ins.

Fixes #3736

Signed-off-by: Kristian Svalland <kristian.svalland@gmail.com>
2021-12-27 12:47:39 +01:00
Stephan Renatus b186719e84 wasm: put stack first, adjust heap base (#3660)
Putting the stack first is preferrable for how C/C++'s stack is mapped
in to Wasm: it's assigned a memory pointer, and grows up. Without
putting the stack first, it can grow into the data section and over-
write globals, leading to situations best described as weird.

Putting it first means that if the evaluation runs out of stack space,
a memory-out-of-bounds trap will occur: it'll try to access a negative
memory location.

In internal/compiler/wasm, we append segments to the data section.
Since our memory layout is

|  <-- stack | -- data (llvm, opa) -- | heap -->  |
we need to adjust the border between data and heap, i.e., where the heap
starts. When initializing a module, the Start function emitted by the
compiler will call the opa_malloc_init function with the new heap base.


Also:

* run-wasm-rego-tests.sh: bump node image version
* wasm/graph.unreachable: fix "memory access out of bounds" issue

   We've never seen this bug in the wild before, but due to the memory layout
   change, the second branch -- casting to an array, accessing its fields --
   would now attempt to read something well beyond the end of memory, and trap.

* rego/testdata: remove Makefile and input rego policy

   This test bundle is simple enough to recreate if need be.

Signed-off-by: Stephan Renatus <stephan.renatus@gmail.com>
2021-07-21 20:07:58 +02:00
Stephan Renatus 8f91118f8f wasm: remove opa_number_float (#3414)
This is the bare minimum to address #3298.

Little change in the benchmarks, there's much potential in improving our
number handling, but that's something we should do purposefully later.

name                                    old time/op  new time/op  delta
WASMArrayIteration/10-16                 420µs ± 2%   404µs ± 2%   -3.82%  (p=0.008 n=5+5)
WASMArrayIteration/100-16                435µs ± 6%   410µs ± 4%     ~     (p=0.056 n=5+5)
WASMArrayIteration/1000-16               582µs ± 2%   554µs ± 4%   -4.86%  (p=0.016 n=5+5)
WASMArrayIteration/10000-16             2.22ms ± 4%  2.23ms ± 6%     ~     (p=0.690 n=5+5)
WASMSetIteration/10-16                   426µs ± 6%   401µs ± 4%   -5.84%  (p=0.032 n=5+5)
WASMSetIteration/100-16                  442µs ± 3%   427µs ± 3%   -3.45%  (p=0.032 n=5+5)
WASMSetIteration/1000-16                 659µs ± 2%   673µs ± 2%     ~     (p=0.151 n=5+5)
WASMSetIteration/10000-16               3.86ms ± 7%  3.77ms ± 5%     ~     (p=0.310 n=5+5)
WASMObjectIteration/10-16                428µs ± 3%   426µs ± 5%     ~     (p=1.000 n=5+5)
WASMObjectIteration/100-16               433µs ± 6%   447µs ± 4%     ~     (p=0.222 n=5+5)
WASMObjectIteration/1000-16              687µs ± 3%   702µs ± 3%     ~     (p=0.151 n=5+5)
WASMObjectIteration/10000-16            3.88ms ± 3%  4.13ms ± 6%   +6.42%  (p=0.032 n=5+5)
WASMLargeJSON/10x10-16                   461µs ± 5%   448µs ± 4%     ~     (p=0.421 n=5+5)
WASMLargeJSON/10x100-16                  484µs ± 3%   497µs ± 9%     ~     (p=0.548 n=5+5)
WASMLargeJSON/10x1000-16                 831µs ± 4%   917µs ± 2%  +10.29%  (p=0.008 n=5+5)
WASMLargeJSON/10x10000-16               5.63ms ± 3%  6.12ms ± 5%   +8.67%  (p=0.008 n=5+5)
WASMLargeJSON/100x100-16                 688µs ± 2%   730µs ± 3%   +6.17%  (p=0.008 n=5+5)
WASMLargeJSON/100x1000-16               3.20ms ±10%  3.27ms ± 5%     ~     (p=0.548 n=5+5)
WASMVirtualDocs/total=1/hit=1-16         421µs ± 2%   410µs ± 3%     ~     (p=0.095 n=5+5)
WASMVirtualDocs/total=10/hit=1-16        422µs ± 3%   406µs ± 5%     ~     (p=0.095 n=5+5)
WASMVirtualDocs/total=100/hit=1-16       430µs ± 5%   420µs ± 4%     ~     (p=0.548 n=5+5)
WASMVirtualDocs/total=1000/hit=1-16      460µs ± 2%   457µs ± 5%     ~     (p=0.841 n=5+5)
WASMVirtualDocs/total=10/hit=10-16       413µs ± 6%   429µs ± 5%     ~     (p=0.056 n=5+5)
WASMVirtualDocs/total=100/hit=10-16      417µs ± 6%   436µs ± 2%     ~     (p=0.056 n=5+5)
WASMVirtualDocs/total=1000/hit=10-16     473µs ± 5%   480µs ± 2%     ~     (p=0.690 n=5+5)
WASMVirtualDocs/total=100/hit=100-16     453µs ± 2%   454µs ± 8%     ~     (p=0.421 n=5+5)
WASMVirtualDocs/total=1000/hit=100-16    503µs ± 4%   499µs ± 6%     ~     (p=1.000 n=5+5)
WASMVirtualDocs/total=1000/hit=1000-16   843µs ± 1%   823µs ± 5%     ~     (p=0.222 n=5+5)

Signed-off-by: Stephan Renatus <stephan.renatus@gmail.com>
2021-04-28 14:24:46 +02:00
Stephan Renatus d9bbaf4578 wasm/glob.match: fix default delimiter handling (#3296)
Also
- use opa_value_iter in opa_glob_match
- add test cases to `wasm-rego-test`
- adapt existing test cases in `wasm-lib-test`

Fixes #3294.

Signed-off-by: Stephan Renatus <stephan.renatus@gmail.com>
2021-03-19 15:00:53 +01:00
Stephan Renatus bd5c572d0f wasm: misc optimizations (less locals, blocks, interning strings and booleans) (#3179)
* wasm: introduce OPA_STRING_INTERNED for interned strings

opa_value_type will report these as OPA_STRING, so special behaviour
should use node->type to discern OPA_STRING/OPA_STRING_INTERNED:

- shallow copies don't need to copy interned strings
- interned strings aren't free()'ed

* wasm: pass constants along, compile them accordingly
* wasm/src: switch to stdbool.h's bool

I'm not aware of any strong reason not to, it seems to be what's commonly
advised, and the memory use of this type is smaller.

* wasm: intern opa_boolean

The heap allocs for these probably don't amount to much, but interning
them allows for shovelling them through the IR as constants. This lets
us shortcut the evaluation of (n)eq when both operands would be known
at compile-time (not likely). However, it also lets us safe a few more
locals, namely all the ones for MakeBooleanStmt.

* wasm: replace `opa_boolean()` by func returning interned bools

The new function will end up having this body:

    00b742 func[188] <opa_boolean>:
     00b743: 41 8a d0 03                | i32.const 59402
     00b747: 41 8c d0 03                | i32.const 59404
     00b74b: 20 00                      | local.get 0
     00b74d: 1b                         | select
     00b74e: 0b                         | end

Where the addresses correspond to our interned boolean `opa_value *`.

The previous implementation, should anyone need it, is still available
as `opa_boolean_allocated`. It's used in tests, too, where we do not
have the `opa_boolean()` emitted by our Wasm compiler.

* wasm: br_if/br optimizations for constants
* wasm: remove AssignBooleanStmt and opa_value_boolean_set

This could be trouble for our interned opa_boolean_t's, but it's not used.
So, let's just get rid of it.

* wasm: avoid some blocks where possible

Due to how the planner plans functions, any partial rule defining a
set or an object would have a block like this:

    block
      call 208 <opa_object>
      local.set 2
    end

With this change, those will no longer be wrapped.

It's not a big deal, neither in what it gets us, nor in what it takes
to apply the optimization.

* wasm: add one-branched if, use in memoization
* wasm: de-block internal calls

I've been comparing our instructions to what wasm-opt does to them, and
this seems like a reasonable change.

Signed-off-by: Stephan Renatus <stephan.renatus@gmail.com>
2021-03-03 21:00:22 +01:00
Stephan Renatus ac886edebf wasm: allow for unknown data (#3136)
Before, the added/altered test cases would have failed: it came down to the
(undefined) data being a==NULL in opa_value_merge.

Fixes #3130.

Signed-off-by: Stephan Renatus <stephan.renatus@gmail.com>
2021-02-10 15:38:08 +01:00
Stephan Renatus a7d44b6b98 wasm: fix caching of mpd values over multiple runs (#3112)
* wasm: fix caching of mpd values over multiple runs

The minimal test case included would fail because on the second run,
the global `initialized`, used in `mpd.c` to record that `mpd_one` had
been prepared, was true; whereas the value that `mpd_one` pointed to
no longer was a valid mpd_t struct. The addition happening in the loop
of the `numbers.range` implementation would thus fail to add NaN to 1,
and everything goes downhill from there.

To remedy this, we expose the init function as `opa_mpd_init`, and
call it from the module's `Start` function (`_initialize`). As a side
effect, tests in test.c (`make wasm-lib-tests`) that use mpd need to
have opa_mpd_init run, too. Those tests are not randomized, so seems
we're OK with having that executed by the added tests, fairly early on
in test.c

Fixes #3110.

Also: wasm/tests/test.c: add `void`s

A consistency thing. I had removed them earlier, but I've come to
understand it's better with than without.
(https://stackoverflow.com/questions/41803937/func-vs-funcvoid-in-c99)

Signed-off-by: Stephan Renatus <stephan.renatus@gmail.com>
2021-02-02 19:54:54 +01:00
Stephan Renatus 81a774a60d wasm: dynamically dispatch data functions with "seen" ref pieces using call_indirect (#3058)
* wasm: optimize package access with non-ground refs using call_indrect

We now

1. write an object corresponding to data paths into the module data
2. initialize an opa_object_t from that using `_initialize`, called
   as the module's Start function
3. write out CallDynamicStmts in IR when the ref is not all ground,
   but its vars have been seen
4. compile those CallDynamicStmts to call_indirect invocations in
   WASM, preceded by a lookup using the path in the object prepared
   in (2.)
5. if the lookup fails to come up with a result, the eval goes
   undefined

What it looks like:

With t.rego as

    package t

    p {
      data.foo[input.x].bar.p
    }

and foo.rego as

    package foo.a.bar

    p = true

when building policy.wasm using `opa build -t wasm -e t/p t.rego foo.rego`,
the body of function `g0.data.t.p` will contain

    i32.const 5
    call $opa_array_with_cap
    local.set $11
    local.get $11
    local.get $7
    call $opa_array_append
    local.get $11
    local.get $8
    call $opa_array_append
    local.get $11
    local.get $6
    call $opa_array_append
    local.get $11
    local.get $9
    call $opa_array_append
    local.get $11
    local.get $10
    call $opa_array_append
    local.get $0
    local.get $1
    local.get $11
    call $opa_mapping_lookup
    local.tee $12
    i32.eqz
    br_if $block
    local.get $12
    call_indirect $29 (type $1)
    local.tee $13
    i32.eqz
    br_if $block

Where the array-related functions build an array of

    ["g0", "foo", input.x, "bar", "p"]

and pass that to `opa_mapping_lookup` to determine the element index to
pass to `call_indirect`. The lookup function returns 74 from the JSON
blob put into the data section,

    (data $38 (i32.const 56485)
      "{\"g0\": {\"foo\": {\"a\": {\"bar\": {\"p\": 74}}}, \"t\": {\"p\": 75}}}")

iff input.x happens to be "a". Otherwise, it'll return 0, and the result
will end up being undefined.

Element 74 of the modules func table is, of course, $g0.data.foo.a.bar.p:

    (elem $33 (i32.const 74)
      $g0.data.foo.a.bar.p $g0.data.t.p)

($33 is some id of that piece of function table, an artifact of the
`wavm disassemble` output.)

* compiler/wasm: add memoization to call_indirect logic

- adds a data segment for mapping element indices (used with call_indirect)
  to function indices (as used with opa_memoize_{get,insert})
- emits mapping function elem -> func idx that uses that data segment
- wires up memoization lookup and insert in call_indirect code path

The added test case would cause `make wasm-rego-test` to fail like this
if memoization wasn't happening:

    ERROR 019_call_indirect_optimization.json: memoization: should have been memoized

* planner: add debug messages, carry them over into the compiler

Signed-off-by: Stephan Renatus <stephan.renatus@gmail.com>
2021-01-21 18:17:27 +01:00
Stephan Renatus 7fe3ba44f8 wasm: re-enable wasm-lib-test (#3084)
When we've removed the --export-all from the build of opa-test.wasm in
https://github.com/open-policy-agent/opa/pull/3061, we've robbed
wasm/test.js of the ability to see and run them.

Now, the existing tests have been explicitly exported, and the test
runner was adapted to fail if nothing was run at all.

Signed-off-by: Stephan Renatus <stephan.renatus@gmail.com>
2021-01-21 15:08:44 +01:00
Ashutosh Narkar e9cc8551f9 wasm: Add native support for json.filter builtin function
Signed-off-by: Ashutosh Narkar <anarkar4387@gmail.com>
2021-01-19 11:08:32 -08:00
Ashutosh Narkar ba5cda16ee wasm: Add native support for json.remove builtin function
Signed-off-by: Ashutosh Narkar <anarkar4387@gmail.com>
2021-01-15 09:26:27 -08:00
Stephan Renatus 372f07e3b2 wasm: synchronise behaviour with non-strict-builtin-error mode in topdown (#3022)
In topdown, we have two different error modes: strict/non-strict.
In WASM, everything is meant to be non-strict. Thus errors that
only appear in topdown with strict mode are annotated as such,
and checked for an empty result-set in the WASM test runner.

Several WASM builtins that have returned an error where they should
return NULL have been adjusted.

This allows us to fix most of the exceptions brought up in #2954.

Notable pieces:

* wasm sdk: ignore builtin errors

This should be in line with the non-strict builtin error semantics used
in WASM.

Before, when the WASM SDK had called out top a topdown-defined builtin,
and that builtin had returned an error, the WASM caller returned that
error. It's been at odds with how topdown evaluated builtin errors when
run without strict builtin errors.

Now, the errors are properly ignored, except for topdown.Halt. That one
doesn't seem like it's used at the moment, at least from this code base.

* cases: add want_result where non-strict eval yields something

This happens to work for both topdown and wasm:

- in topdown, the test runner checks for expected errors first, and
  ignores the wanted result;
- in wasm, we check for a desired result first, checking the error
  if no result was defined.

Signed-off-by: Stephan Renatus <stephan.renatus@gmail.com>
2021-01-08 15:25:37 +01:00
Stephan Renatus 01c2c628a0 wasm: virtual doc conflict handling (#3017)
Fixes #2926.

This is in line with what topdown does.

Changed opa_value_merge instead of introducing another function since
its seemed to be limited to this one case.

Also removed all object merge conflict test cases, since that wasm runtime error can't happen anymore now.

Signed-off-by: Stephan Renatus <stephan.renatus@gmail.com>
Co-authored-by: Torin Sandall <torinsandall@gmail.com>
2021-01-08 13:43:43 +01:00
Stephan Renatus ca875db4f4 wasm: natively implement ceil() and floor()
Signed-off-by: Stephan Renatus <stephan.renatus@gmail.com>
2021-01-07 16:22:41 -05:00
Ashutosh Narkar f7d8b1ca9b wasm: Add native support for object.remove and object.union builtin function
Signed-off-by: Ashutosh Narkar <anarkar4387@gmail.com>
2021-01-05 16:48:43 -08:00
Ashutosh Narkar 1a708a9c03 wasm: Add native support for graph.reachable builtin function
Signed-off-by: Ashutosh Narkar <anarkar4387@gmail.com>
2020-12-15 09:37:50 -08:00
Stephan Renatus a2cab3f884 wasm: fix number truncation format_int
I'm not exactly sure why we had been rounding here before. Topdown truncates
when formatting a decimal number as int:

    format_int(15.9, 16) == "f"
    format_int(-15.9, 16) == "-f"

Fixes #2923.

Signed-off-by: Stephan Renatus <stephan.renatus@gmail.com>
2020-12-14 09:49:39 -05:00
Stephan Renatus f465b227b4 wasm: fix rounding mode
Same as in topdown, we should have this be true in wasm:

    round(1.5) == 2
    round(2.5) == 3

The default rounding mode for libmpdec's maxcontext was MPD_ROUND_HALF_EVEN,
would would round 2.5 to 2.

See https://www.bytereef.org/mpdecimal/doc/libmpdec/context.html#rounding

Signed-off-by: Stephan Renatus <stephan.renatus@gmail.com>
2020-12-14 09:49:39 -05:00
Ashutosh Narkar a20dd4c3c1 wasm: Add native support for object.get builtin function
Signed-off-by: Ashutosh Narkar <anarkar4387@gmail.com>
2020-12-09 16:00:54 -05:00
Patrick East e58de14c19 wasm: regex.is_valid return false for non string
Previously we returned undefined rather than false, this changes the
c implementation to match the golang one and return false if the
parameter was the wrong type.

Fixes: #2925
Signed-off-by: Patrick East <east.patrick@gmail.com>
2020-11-25 14:04:55 -05:00
Torin Sandall 8d599fcca5 wasm: Memoize planned functions without positional args
This commit updates the C library to expose a global key-value mapping
that can be initialized and supports push/pop operations for
shadowing. The key-value mapping is implemented using opa_object_t to
keep things simple. In the future, this could be replaced with
something more efficient.

The wasm compiler uses the key-value mapping to memoize calls to
planned functions that only depend on input and data. The compiler
uses the function index as the key and the function return value as
the value. The choice to memoize at the call-site as opposed to inside
planned functions was arbitrary.

Signed-off-by: Torin Sandall <torinsandall@gmail.com>
2020-11-10 16:56:57 -05:00
Teemu Koponen d5cad610e9 wasm: Fix memory leaks in the glob builtin.
Signed-off-by: Teemu Koponen <koponen@styra.com>
2020-11-04 14:33:58 -08:00
Torin Sandall f622582e88 wasm: Fix serialization of non-string object keys
Previously the C library was not escaping non-string object
before serializing them--this would break callers that expect
valid JSON output from opa_json_dump. With this change,
opa_json_dump will serialize/escape non-string object
keys. opa_value_dump retains the old behaviour.

Signed-off-by: Torin Sandall <torinsandall@gmail.com>
2020-10-30 16:54:19 -07:00
Torin Sandall 7a4c831523 wasm: Fix split built-in implementation to handle empty strings
split("", <delim>) should return [""]. The implementation was not
handling the empty string case or more generally where the string is
shorter than the delimiter.

Signed-off-by: Torin Sandall <torinsandall@gmail.com>
2020-10-30 16:02:33 -07:00
Teemu Koponen 5293c1d132 wasm: regex and glob builtin support.
Unlike the golang builtin, this does not support caching of compiled
patterns across evaluations.

The glob builtin builds on regex builtins, compiling the glob to regex
and then using regex builtins to execute the actual matching.

Signed-off-by: Teemu Koponen <koponen@styra.com>
2020-10-28 17:20:13 -04:00
Teemu Koponen 64a38f8a11 wasm: Add memchr, memcmp, strchr, wmemchr, wmemcmp, wmemmove, wmemcpy, wmemset, and wcslen.
Signed-off-by: Teemu Koponen <koponen@styra.com>
2020-10-28 17:20:13 -04:00
Teemu Koponen 78d9bf1aed wasm: net.cidr_contains, net.cidr_intersects, and net.cidr_overlap builtins.
Signed-off-by: Teemu Koponen <koponen@styra.com>
2020-10-28 15:28:03 -04:00
Torin Sandall 5fcb3f0451 wasm: Add support for parsing and dumping set literals
This commit adds support into the C library for parsing and dumping
set literals without representing them as arrays (we use the Rego
convention of curly-braces and set() for emptiness.) The parsing and
dumping that supports set literals is exposed via new opa_value_parse
and opa_value_dump functions (respectively).

Fixes #2773

Signed-off-by: Torin Sandall <torinsandall@gmail.com>
2020-10-26 14:57:58 -04:00
Patrick East 539543ed01 wasm: Add data patching API's to wasm helper bin
There are three new API's implemented and exposed from the C code:

```
opa_value_add_path
opa_value_remove_path
```

and a new helper:

```
opa_object_remove
```

The first two provide similar functionality as the OPA stores "add"
and "remove" op (same style of path). The main difference for the add
is that it will create intermediate objects as required, we do not
have a separate `mkdir` operation like the store does.

Signed-off-by: Patrick East <east.patrick@gmail.com>
2020-10-23 12:26:51 -07:00
Anders Eknert e8c5e128ac wasm: unicode fixes for count, indexof, and substring builtins.
These builtins should operate on codepoints, not on characters. This
also improves the string conversion from a memory byte array holding
UTF-8 string to a valid JavaScript string which is UTF-16.

The improved unicode tests were provided by Anders Eknert
<anders.eknert@bisnode.com>.

Signed-off-by: Teemu Koponen <koponen@styra.com>
2020-10-23 10:00:48 -04:00
Teemu Koponen 6ce7a6b01b wasm: Add object.filter builtin.
Signed-off-by: Teemu Koponen <koponen@styra.com>
2020-10-22 13:28:51 -04:00
Teemu Koponen f0095ae0b6 wasm: json marshal and unmarshal builtins.
Signed-off-by: Teemu Koponen <koponen@styra.com>
2020-10-22 13:28:51 -04:00
Teemu Koponen 914aec4686 wasm: base64 builtins.
Signed-off-by: Teemu Koponen <koponen@styra.com>
2020-10-22 13:28:51 -04:00
Torin Sandall fec96c43c4 wasm: Add native support for to_number() built-in function
Fixes #2679

Signed-off-by: Ashutosh Narkar <anarkar4387@gmail.com>
Signed-off-by: Torin Sandall <torinsandall@gmail.com>
2020-10-16 10:45:12 -04:00
Teemu Koponen 813eb4b924 wasm: malloc with multiple free lists.
Split the single free list to many: each list holds chunks of specific
size, and therefore, while searching for a free block, identifying the
right list is cheap. To reduce fragmentation all but the list with
largest chunks holds fixed size blocks.

The number of free lists and chunk sizes (currently 4, 8, 16, 64, and
128+ bytes) warrant proper tuning.

Signed-off-by: Teemu Koponen <koponen@styra.com>
2020-10-15 11:25:33 -07:00
Torin Sandall 4851c4b0e8 wasm: Fix JSON parser to copy memory for strings and numbers
Previously the parser was constructing strings and numbers with
references to the input buffer memory. If that memory was freed after
the opa_json_parse() call, it would corrupt the strings and numbers
returned by the parse.

This commit just updates the parser to create a copy of the
string/number values. If this becomes a performance issue in the
future, we can introduce an optional API for callers that promise NOT
to free the input buffer.

Signed-off-by: Torin Sandall <torinsandall@gmail.com>
2020-10-09 09:08:59 -04:00
Patrick East 02a3b42b80 wasm: "Reset" the heap for malloc unit tests
A lot of the tests assume knowledge about the state of the free block
list and usage of the heap. This gets thrown off when >1 test is
messing around with the heap, and the ordering of tests isn't
guaranteed.

This changes to do a hacky reset to reset the heap to a starting
position at the current heap top. This simulates a "fresh" vm with
no pre-allocated memory but leaks anything up until then.

Signed-off-by: Patrick East <east.patrick@gmail.com>
2020-10-08 13:19:51 -07:00
Patrick East fa2538c784 wasm: Add minimum malloc size and split threshold
To help reduce fragmentation we will now have a minimum of 16 byte
sized allocation block. In testing with JSON parsed objects we
generate a significant amount of 12 and 16 byte allocations for the
container structs for opa values, the size chosen should fit the
majority of them. In addition we will not split free blocks unless
the remainder is big enough to make additional allocations.

Testing with a relatively large (6MB) nested JSON object being parsed
this cuts the length of our free block list from a few thousand down
to zero. The time it takes to parse it (on my machine) goes from about
30 seconds to a few hundred milliseconds.

Signed-off-by: Patrick East <east.patrick@gmail.com>
2020-10-08 13:19:51 -07:00
Torin Sandall b845a8db57 wasm: Add numbers.range built-in function
Signed-off-by: Torin Sandall <torinsandall@gmail.com>
2020-07-14 13:12:56 -07:00