diff --git a/.gitignore b/.gitignore index cff66bae19..9d22d7df90 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ coverage opa_* .Dockerfile_* _release +wasm/_obj site.tar.gz policy.wasm diff --git a/Makefile b/Makefile index 9e982bca06..2b44e2a23c 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,9 @@ GOVERSION := 1.10 GOARCH := $(shell go env GOARCH) GOOS := $(shell go env GOOS) +DOCKER_INSTALLED := $(shell hash docker 2>/dev/null && echo 1 || echo 0) +DOCKER := docker + BIN := opa_$(GOOS)_$(GOARCH) REPOSITORY := openpolicyagent @@ -45,12 +48,24 @@ version: deps: @./build/install-deps.sh +.PHONY: wasm-build +wasm-build: +ifeq ($(DOCKER_INSTALLED), 1) + @$(MAKE) -C wasm build + cp wasm/_obj/opa.wasm internal/compiler/wasm/opa/opa.wasm +else + @echo "Docker not installed. Skipping OPA-WASM library build." +endif + .PHONY: generate -generate: +generate: wasm-build $(GO) generate .PHONY: build -build: generate +build: go-build + +.PHONY: go-build +go-build: generate $(GO) build -o $(BIN) -ldflags $(LDFLAGS) .PHONY: image @@ -95,9 +110,27 @@ install: generate $(GO) install -ldflags $(LDFLAGS) .PHONY: test -test: generate +test: opa-wasm-test go-test wasm-test + +.PHONY: opa-wasm-test +opa-wasm-test: +ifeq ($(DOCKER_INSTALLED), 1) + @$(MAKE) -C wasm test +else + @echo "Docker not installed. Skipping OPA-WASM library test." +endif + +.PHONY: go-test +go-test: generate $(GO) test ./... +.PHONY: wasm-test +wasm-test: +ifeq ($(DOCKER_INSTALLED), 1) +else + @echo "Docker not installed. Skipping WASM-based test execution." +endif + .PHONY: perf perf: generate $(GO) test -run=- -bench=. -benchmem ./... @@ -121,8 +154,12 @@ check-lint: fmt: $(GO) fmt ./... +.PHONY: wasm-clean +wasm-clean: + @$(MAKE) -C wasm clean + .PHONY: clean -clean: +clean: wasm-clean rm -f .Dockerfile_* rm -f opa_*_* rm -fr site.tar.gz docs/_site docs/node_modules docs/book/_book docs/book/node_modules diff --git a/internal/cmd/genopawasm/main.go b/internal/cmd/genopawasm/main.go new file mode 100644 index 0000000000..d9702288a0 --- /dev/null +++ b/internal/cmd/genopawasm/main.go @@ -0,0 +1,135 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package main + +import ( + "bytes" + "compress/gzip" + "fmt" + "io" + "os" + "path" + + "github.com/spf13/cobra" +) + +type params struct { + Output string +} + +func main() { + + var params params + executable := path.Base(os.Args[0]) + + command := &cobra.Command{ + Use: executable, + Short: executable + " ", + RunE: func(_ *cobra.Command, args []string) error { + if len(args) != 1 { + return fmt.Errorf("provide path of opa.wasm file") + } + return run(params, args) + }, + } + + command.Flags().StringVarP(¶ms.Output, "output", "o", "", "set path of output file (default: stdout)") + + if err := command.Execute(); err != nil { + os.Exit(1) + } +} + +func run(params params, args []string) error { + + var out io.Writer + + if params.Output != "" { + f, err := os.Create(params.Output) + if err != nil { + return err + } + defer f.Close() + out = f + } else { + out = os.Stdout + } + + _, err := out.Write([]byte(`// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// THIS FILE IS GENERATED. DO NOT EDIT. + +// Package opa contains bytecode for the OPA-WASM library. +package opa + +import ( + "bytes" + "compress/gzip" + "io/ioutil" +) + +// Bytes returns the OPA-WASM bytecode. +func Bytes() ([]byte, error) { + gr, err := gzip.NewReader(bytes.NewBuffer(gzipped)) + if err != nil { + return nil, err + } + return ioutil.ReadAll(gr) +} + +`)) + + if err != nil { + return err + } + + _, err = out.Write([]byte(`var gzipped = []byte("`)) + if err != nil { + return err + } + + in, err := os.Open(args[0]) + if err != nil { + return err + } + + defer in.Close() + + var buf bytes.Buffer + gw := gzip.NewWriter(&buf) + _, err = io.Copy(gw, in) + if err != nil { + return err + } + + gw.Close() + + for _, b := range buf.Bytes() { + if _, err := out.Write([]byte(`\x`)); err != nil { + return err + } + if _, err := out.Write(asciihex(b)); err != nil { + return err + } + } + + _, err = out.Write([]byte(`")`)) + if err != nil { + return err + } + + _, err = out.Write([]byte("\n")) + return err +} + +var digits = "0123456789ABCDEF" + +func asciihex(b byte) []byte { + lo := digits[(b & 0x0F)] + hi := digits[(b >> 4)] + return []byte{hi, lo} +} diff --git a/internal/compiler/wasm/opa/opa.go b/internal/compiler/wasm/opa/opa.go new file mode 100644 index 0000000000..8118cfca5a --- /dev/null +++ b/internal/compiler/wasm/opa/opa.go @@ -0,0 +1,25 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// THIS FILE IS GENERATED. DO NOT EDIT. + +// Package opa contains bytecode for the OPA-WASM library. +package opa + +import ( + "bytes" + "compress/gzip" + "io/ioutil" +) + +// Bytes returns the OPA-WASM bytecode. +func Bytes() ([]byte, error) { + gr, err := gzip.NewReader(bytes.NewBuffer(gzipped)) + if err != nil { + return nil, err + } + return ioutil.ReadAll(gr) +} + +var gzipped = []byte("\x1F\x8B\x08\x00\x00\x00\x00\x00\x00\xFF\xCC\x7A\x4D\x8C\x5C\xC7\x71\x7F\x55\x77\xBF\x37\x6F\xA6\xE7\xED\x0E\x25\x51\xA2\xB5\xFA\xA8\xF7\x24\xD9\xA4\x49\xCA\xB2\x65\xAD\x2C\xCB\xB6\xB6\xD7\xDC\x25\xD7\x34\xFE\x16\x20\xFD\x13\x84\x48\x34\x1C\x71\x87\xE4\xCE\x7E\x51\xBB\x43\x7D\x00\x8B\x9D\x45\x0C\x23\x0A\x62\x24\x07\x23\x81\x9D\x43\x2C\xD3\x40\x94\x1C\x98\x20\x31\x02\x41\x49\x00\x01\x31\x72\xCE\x21\xC7\x04\x48\x62\x40\xB9\xE4\x92\x4B\x8E\x89\x83\xAA\xEE\xF7\x31\x1F\xAB\x8F\x5B\x76\x81\x79\xF3\xFA\xA3\xBA\xEA\x57\xD5\x55\xD5\x3D\x05\xBD\xFD\x6D\x04\x00\xFC\x8D\x13\x57\xD5\x68\x84\xA3\xAB\x38\x82\xAB\x00\x57\x91\xBF\xAB\xD1\x21\x7F\x1E\x1C\x48\x3B\x1E\xF2\xC7\xC1\x55\xE0\x37\xEE\xC0\x03\x19\x33\x82\xAB\x7A\x54\x7C\xE2\xE8\xAA\x19\xF9\x27\xD3\x3B\x94\xCF\x03\xF5\xA8\xD2\xFD\x9D\xD7\xE3\xED\xFE\xF6\xEE\xDE\x5B\x0A\xE4\xAD\xB9\x7B\xAB\xD7\xED\xBD\xBA\xBB\x37\x04\xD4\x2B\x17\x94\x66\x46\x0C\xF0\x9F\xD6\x5A\x47\xF2\x05\x01\x51\xC7\x0D\x9D\xE8\x66\x0B\x34\x26\x3A\xB1\x16\xA0\xAD\x53\x19\xA3\xF5\x1C\xF2\x67\x7B\xBE\x03\x1A\x34\x98\x08\x6F\x21\x62\xBC\x60\x46\xE8\xDE\x79\xDB\xD8\x11\x14\x8F\xA3\x84\x3F\xFF\x28\xB1\x8D\xFF\x68\xAD\x9E\xE8\x76\xDF\xE8\xED\x6F\x77\xAF\xF5\xB6\xB6\xBA\xD7\x86\xBB\x7B\xFB\x80\xB6\xDB\xBD\xD9\xEF\xDD\xEA\xBE\xDA\xDB\xEF\x6B\x6C\x77\xBB\xEB\xFB\xBB\xDD\x9B\xBD\x9D\xF5\xAD\xBE\x56\xAD\x6E\x77\xBD\x37\xEC\x75\xFB\x3B\xEB\x5A\xB7\x98\xF1\xED\xDE\xD6\xD6\xEE\x35\x50\x09\xBF\x5C\xDF\xEB\xF7\x41\xDF\xC7\x5F\x5F\xEF\x6D\xDD\xEE\x77\x6F\xF4\x87\xDD\xDD\x57\x07\xFD\x6B\x43\x30\x27\xAA\xE6\x6B\xBB\xDB\xB7\x7A\x7B\x7D\x88\xE6\xB8\xCD\x0F\xE0\xB1\x10\x3F\x38\x3E\xB7\xB7\xB7\xD7\x7B\xAB\xBB\xD3\x1B\x6E\xBC\xDE\x87\xC6\xBD\x33\x3A\x21\x49\xC7\x5A\xA1\x79\xB2\x7A\xDF\x18\xF6\xF7\x8A\xF5\x5B\xF7\x4D\xB4\xFB\xE9\x76\x6E\xBC\x19\xDA\x0F\x54\x0D\x5B\xFD\x9D\x1B\xC3\x9B\x05\x85\xF4\xFE\xA9\x1E\x4F\x63\x6E\x7A\xCA\xFE\x70\x6F\x63\xE7\x06\xCC\x77\x26\x7B\xA0\xF3\xC0\x14\x0E\xDD\xEB\x5B\xBB\xBD\x21\x9C\x38\x35\xDD\xB3\x73\x7B\xFB\xD5\xFE\x1E\xDC\x33\xA3\x2B\x2C\x71\xAF\xE5\xAE\xFD\xE1\xDE\xCE\xB5\xED\x5B\xF0\x85\x19\xD4\x3D\x93\xF7\xCD\x20\x11\x04\x3B\x39\x5F\x53\xC3\x66\xFF\xAD\x7D\xB8\x5F\x50\xF1\xE8\x8B\x52\x1F\xA8\x61\xBF\xB3\x3B\xEC\xF6\x5F\xBB\xDD\xDB\x82\x53\x35\xF0\x64\xD8\x67\xEA\x94\xA4\xE5\xC1\x9A\xDA\x5F\xDD\xDD\xDD\xEA\xF7\x76\x60\xA1\xA6\xB2\x8D\x9D\x21\x3C\x34\x5F\x23\x23\x50\x3C\x5C\xC3\x2D\xC8\xF9\x88\x58\xD8\xCE\xED\xAD\x2D\x78\x54\x44\x2E\xA8\xD1\x9C\xEF\x60\xA0\x84\x5C\xD6\xA9\x35\x78\x7A\x79\x2B\x80\xC4\x94\x1E\x3B\x59\xBD\x74\x87\xFD\xBD\xED\x8D\x9D\xDE\xB0\xBF\x0E\x8F\x17\x83\xB6\xFA\x3B\xF0\x64\xA7\xDB\xAD\x30\xB8\xB1\xB7\xFB\x06\x3C\xD1\x2C\x1B\xE0\xB3\xF7\x54\x9D\x6F\x6C\x0C\x6F\x76\xAF\xF5\x6E\xC1\xE7\x5A\x95\xF4\x70\xBA\x53\x8D\xE8\xDD\xBA\xD5\xDF\x59\x87\x33\x35\x54\xF7\x79\xBF\x7F\xBE\x0E\x97\x58\xDF\xD9\x07\xFC\xB2\xA1\xAD\xBF\xD5\xDF\xEE\xFA\x1D\x76\xEE\x44\x7D\xEC\xCE\x7E\x7F\x6F\x08\xE7\x0B\x8E\x59\xF5\x4F\x09\x2A\x1B\xFB\xEB\x1B\x37\x36\x86\xF0\xC5\xF0\xB6\x7F\xAB\x77\xAD\x0F\x5F\x6A\xFA\xB7\x9B\xFD\x37\xE1\x69\x51\xE5\x60\x7F\x77\xA7\xBB\xD5\x7F\xB3\xBB\x7B\xFD\xFA\x7E\x7F\x08\x5F\xBE\x7F\xAC\x75\xAF\xBF\xDD\xDB\xD8\x61\xB4\x9E\xE9\x8C\x75\xF4\x77\xAF\xC3\xE2\xE4\xD8\xDE\x7A\xB7\x37\xDC\xDD\x86\x67\x4F\x4D\x77\x08\x3B\xFB\xF0\x95\xCF\x4C\x77\xDD\xDE\xD9\xB8\xB6\xBB\xDE\x87\xE7\x66\x4C\x0B\x86\xFF\xD5\x19\x5D\x41\x8D\xCF\x9F\x98\xEA\x82\xAF\x8D\xB7\x6D\xEC\x6C\x0C\xE1\xEB\xF7\x94\x6D\xB7\x7A\x7B\xFB\xDE\xE4\xBE\x71\xDF\x44\xA3\xB7\x93\x17\x4E\x4E\x34\x07\x36\x96\x26\x87\x7B\x2B\x70\x93\xC3\x83\xEE\xBF\x39\x37\xDE\x0E\x17\x5A\xFF\xBA\x7A\x41\x81\x9D\x07\x02\xD7\x79\xDD\xE1\x60\x09\x5C\x67\x68\x15\xD8\x45\x50\x4B\x6A\x89\xE0\xB4\x32\x39\xAC\xA4\xA0\xE5\x3B\x10\x76\xBE\x77\x74\x74\x04\x2B\xA9\xE2\xF7\x24\x87\x14\xAC\x75\x30\x6F\x65\xA8\xFD\x07\x13\xEB\x91\x3A\xD0\x23\x0E\x41\xEA\xD0\x8D\x08\x08\xF7\x1C\x5C\x24\x5C\xC8\x14\x13\x5C\x49\x81\x70\x25\x05\x37\xCA\x14\xC1\x79\x80\x5C\x13\xF2\xC3\xAC\xA5\xE0\x30\x53\x64\x48\xF3\x57\xE0\xF1\x93\xFF\xA4\xDD\x68\x90\x6B\x17\x5D\x4E\x81\xDF\xE6\xE2\x16\x28\x6D\x9A\x2D\xBF\x3E\xE1\x69\x65\x36\xE7\xAD\x3B\x7A\xFB\xE8\x08\x3A\x47\xCC\xAA\x30\x87\xE7\x01\xFD\x7A\xB8\x92\x6A\x02\x97\x0C\xCE\x6A\xC8\x22\x52\xEE\x97\xF8\xDA\x4A\xAA\xDC\x88\x22\x42\xDF\x9C\xC7\xEB\x14\x51\x7C\x6D\xC1\x4B\x95\x30\x55\xF9\x30\xB9\xF2\x88\x68\x52\xA4\xD7\x16\x3A\x7F\xCE\x0B\xE4\x2A\x8D\xBD\x34\xCE\x0C\x4E\x2B\xC8\x81\x30\x7C\xC3\xB5\x34\x66\x04\x2E\xCF\x5B\xB5\x24\x64\xF2\x86\xC7\x2D\xA1\x06\x25\x6B\x79\x73\x21\x57\x8C\x88\xBA\x9B\xB5\x64\xF9\xCC\x3A\x93\xE9\x65\xCC\xDA\x94\x64\x46\x2F\xD5\x61\xA0\xF6\xF2\xE8\x20\x4F\xC9\xDC\xBD\x92\x42\xB1\x1A\x69\xFE\xCC\x14\xA5\x64\x9F\x51\xF0\x72\x8A\x6D\x65\xC3\xCB\x95\x14\x2D\xA9\x82\x1B\x3F\xD2\xEB\x8F\x57\xC5\x76\xD3\x92\x72\x65\x4B\x9A\x58\x6A\x53\xEB\x4A\x8A\xA4\x99\x13\x4D\xED\x65\x3C\xC8\xDA\x82\x16\xAF\x61\xDA\xAC\xEB\x11\x35\x29\xA1\xC6\x1A\xA3\xC3\x1C\x9F\xD1\xF0\x7E\x16\xB7\xD1\xCA\xB8\x33\x1A\xB2\x76\x81\x2A\x12\x9E\xD5\x49\x16\x53\xFB\xFD\x2C\xB2\x02\x71\x05\xAD\x40\xD6\x26\x3C\xA3\x93\x3C\x7D\x29\x45\x6A\x53\xFA\xFF\x19\x71\xC2\xCE\xEF\x33\x47\xF3\x96\x94\x7D\xFA\xD3\xDB\xA1\x3D\x8D\x38\xF2\xB8\x11\x3C\xC3\x9A\xFB\x55\x86\x4B\xCC\xE3\x5D\xA7\x87\x83\xD3\xCA\x64\x8A\x89\x2F\x21\x1E\xB2\x5E\xCE\x03\x38\x7D\x91\x0D\xF3\x3C\x20\x3F\x98\x27\xB5\x0C\x2F\xB1\x66\x84\xC6\xAF\x15\x14\x54\x41\x61\xDE\x3A\xB0\xDF\x47\xC5\x99\x97\x67\x71\x45\x4C\x52\x4C\x5A\xB9\x78\x95\xE7\xBA\xE8\x22\x83\x10\xE8\xA3\xA7\x8F\x9E\xBE\x66\xFA\x48\x3A\xD0\x47\x4F\x5F\x57\xF4\x3F\xF5\xE6\x7B\x05\xD5\x48\xBE\x65\x50\x9A\x8C\x6C\x35\x36\x0F\x58\x49\xF5\x04\x9D\x4C\x0B\x99\x0C\x48\x0B\xC9\x14\xDA\xC6\x5A\x82\x14\x3D\xCD\x15\x36\x1F\x10\xEB\x62\xB4\x6E\xA2\x97\x34\x50\x2D\xC5\x52\x22\xD6\x8A\x18\x9E\x57\x55\xB2\x12\xE0\x3A\xAD\x60\xDE\x33\xC8\x22\x2F\xE3\xC1\xC7\xA0\x2A\xA3\xED\xCF\x51\xA9\x11\x1E\x16\x2A\x14\x5C\x0B\x64\xB5\x47\x56\x33\xB2\x8A\xF9\xC0\x09\x3E\xF8\x11\xD6\x32\xBC\x16\xFB\x13\xBF\x96\xDF\xBB\x64\xAA\xB5\x4A\xB0\xBC\x40\x0C\xEC\x47\x41\x84\x02\x91\xB6\x1E\x9A\x80\x4C\xDB\x63\x34\x2E\x32\x29\x7B\x1E\x71\x54\x18\x6E\x50\x82\xE8\xD1\xE1\x20\x03\xEF\x0A\x30\x05\x4B\x5E\xE4\x86\x4C\x4E\xC2\xD3\xD8\x2B\xA8\x46\x01\x6B\x11\x1B\x9D\x59\x0D\x8A\x44\x46\x00\x09\x3D\x02\x3C\x2B\x08\x52\xB7\x18\x1E\xA9\x97\x48\xF1\x6A\x75\x63\x21\x65\xE7\xC1\x7B\xE5\x75\xFE\xB8\xB6\x60\x7F\x88\x1A\x47\xEA\x40\x1D\x06\x27\x59\x73\xB6\xDE\x5D\x02\x01\xEF\x62\x5D\xEE\x6A\x37\x22\x2D\x3B\x3B\x37\xEB\xA4\xC9\x78\x47\x79\x46\x27\x35\x7F\xEA\x77\xBE\xA1\xE8\xFD\x4C\x33\x44\xAC\x93\xF7\x33\x63\x65\x6E\x31\x2D\x83\x36\xB2\x1F\x00\xF6\xBD\xBC\x27\x62\xB6\x8D\x88\x62\xF1\x03\xF6\x25\x01\xE1\x13\xBA\x60\x38\xDE\x05\x83\xB8\x60\x6F\xC4\xFF\x8E\xB1\x91\xE0\x84\x87\x6A\xE4\x77\x3F\x83\x13\x30\xF2\x44\x73\xB3\x90\x47\x2C\x77\x74\x37\x8B\xBD\x57\x6E\x38\x93\x45\xCB\x98\x25\xA4\xB3\xA6\x5A\x12\xBF\xDC\x2A\x71\x4A\xD8\x2F\x5B\x6A\xDE\x7D\x39\x05\xB2\xD4\xA8\xFC\x70\xE1\xA4\x23\x71\xA0\xAD\xD0\x29\x7E\xB9\x55\xFA\xE5\xA8\xE6\x97\x5B\xBC\x8F\xB4\xA5\x56\xE9\x97\x5B\xA9\xB2\x94\x50\x7C\x25\x55\x14\x31\x2F\x11\xB1\x75\x67\x49\xE9\x97\x9B\xEC\x97\xA9\xC5\x2E\x95\x83\x27\xA9\xB5\x05\xFB\x6F\x06\xD3\x11\x41\xE7\x0F\xC4\x8A\x55\xB1\x6F\xFD\x7B\xAE\x59\x58\x43\x8A\x1F\x11\x19\x8A\xD6\x16\xF2\x58\xE2\x90\x33\x2C\x2C\x64\x89\x5E\xA2\x06\x33\x97\x38\x3D\xCC\x9B\x03\xF1\x4D\xCE\x0C\xF2\x16\x37\xCA\x7B\xE0\xCF\xA6\xCA\x41\xD6\x76\x90\xA5\x95\xE1\x04\xF0\x2D\xD3\x6C\x94\x33\xB2\x39\xBD\x44\x96\x5F\xE7\x2A\x4F\x66\x99\x09\x9B\x82\x65\x0A\xB6\xD4\x9B\xF5\x00\x5A\x9E\x96\x56\xAD\xE2\x93\x5A\x35\x8A\xAC\x8C\x09\x9A\x58\xD2\x6C\x2B\x1B\x48\xB4\x39\x26\xB6\x2B\x96\x29\x71\x38\xC8\x13\x8A\xD7\x64\x5B\x18\x12\x4F\x5D\x8A\x6F\xD9\x93\x31\xE3\xF9\x9C\x77\xEB\xCA\x25\x83\xBC\x79\x5A\x49\xFE\x32\xC7\xFA\xFA\x2E\x93\x52\x4B\xC4\x8D\x4E\xAD\x15\xE0\x25\x2E\xC9\xE6\x1C\x66\x4D\xBD\x44\x89\xB0\x35\x28\x47\xD3\x1C\xEB\x6F\x8E\x9A\xBC\x76\x53\x68\x9E\x56\xE0\x39\xE0\xC5\xB2\x39\x4B\x73\x7E\xA8\x25\x55\xAE\xA0\xD9\xDE\x03\x1F\xFA\x13\xF0\xA1\x3F\x25\x1F\xBA\xCE\x87\xF6\x48\x8F\xB1\xA2\xC3\x34\xCB\x26\xE6\x90\xD1\xBA\xBC\x60\xFF\x3B\x42\xCB\x71\x16\x0B\x47\x2D\xDB\x72\xCA\xF9\x88\xAD\x69\x89\x54\x9D\xCE\x6F\x0A\xFE\xC6\x45\x5F\x05\x20\x43\x6A\x51\xB5\xC9\x2C\xC3\xB3\xCA\x04\xCB\x91\x6C\xC8\xB8\xF6\x80\xA7\x0F\x17\x95\xA0\x3A\xF4\xF3\x32\x54\x4B\x64\x18\x80\xC8\x03\x20\xFE\xD0\xB0\xB4\xB1\x83\x8C\xC3\x1B\x92\x1A\x50\xCC\xE2\xAA\xC1\x19\x05\xCF\xCA\xFC\x44\xD8\x60\x59\x35\x45\xA5\xA0\x3C\x2F\x57\x15\x26\x8A\x90\x97\x8B\xC4\xAB\x73\x12\x56\x5A\x71\x43\x4C\xD1\x4D\x35\x8B\x2D\x32\xAF\x59\x22\x6C\x65\xBC\x95\xF4\x20\x8B\xC3\xDE\x61\x4F\x41\x4A\xD0\xCF\x39\xA5\xA5\x84\xB9\x18\xBA\x16\xE9\x85\x5C\x79\xD9\x74\x21\x1B\xAB\xB0\x26\x56\x90\x07\x3E\xB9\x3C\x71\x25\x4A\x4C\x50\x89\x62\x29\x12\x55\x2C\x2A\x28\x15\xA0\x49\xDD\x7D\x56\x33\x3B\xFA\x79\x04\x99\x9A\xA3\x70\x33\xC8\x15\x69\x61\x8D\x9A\x8B\xCA\x04\x1A\x2C\x4E\x92\x37\x78\x1D\xD9\x0B\x62\x66\x02\x3C\xB8\x24\x6B\x39\x14\x7F\x81\xDE\x59\x28\xAF\x9F\xCC\x4A\x83\xA4\xEB\x3E\xA5\x61\x62\x26\x6F\x86\xC8\xEA\x03\x68\x1E\x91\x1A\x64\xB1\xF7\x4E\x0E\x2F\xA5\x40\xAD\x4C\x51\x92\xA1\x5E\xA2\x98\x59\x1F\xB8\x37\x0B\xD1\xC1\x03\xE1\x24\x7C\x53\x41\xA8\x51\x10\x92\x75\xB2\x98\x94\x7B\x73\x20\x81\x72\x34\xC8\x50\x4E\x0C\x99\xA6\x86\x83\x6F\x71\xBC\xF5\xEB\x71\x48\x4A\x32\xB4\x14\x93\x15\xA8\x08\x79\xB2\x97\x39\xB8\x06\xE3\xB7\xC5\x77\x52\xA4\x16\x63\xDE\xF2\x11\x1F\xC5\xDB\x1A\xFB\x7D\x44\x33\x16\xE1\x6B\xB9\x0F\xD6\x37\x26\xBB\x69\x31\x34\x0F\x1B\x9B\x4F\xA6\x5D\x92\xA1\x43\xCE\xF0\x79\x83\x00\x61\x6D\x77\x4A\xC4\x41\x32\xCC\x86\x38\xE9\xA0\xE1\x60\x78\xCC\x36\x16\x5B\x33\xCC\x91\x23\x5C\x91\xB4\x38\xB8\x68\xFF\xA7\xE0\x8E\x93\xA3\xD1\x20\xC7\x70\x64\xF2\x40\xCF\xC5\x00\x00\xA8\x2A\x02\xF3\xF6\xFF\x8A\x28\xF5\x34\xA6\xC8\xFD\x93\xDC\x84\x34\x38\xD0\x35\x39\x16\x59\x94\x09\x6E\x7E\x8C\x98\x5D\x2B\x73\x87\x49\x4A\x38\x4E\x09\x73\x95\x81\x8F\x84\x58\x52\x52\xA5\xFB\xB5\x65\x4A\xC6\xCF\x33\xDA\xA7\x68\x67\x75\x52\xA6\x6C\x27\xF9\xB4\x81\x61\x63\x81\x43\xF6\x6C\x60\x1F\xE2\xD6\x24\xB4\x22\xEF\x47\x43\xE8\x14\x77\xA2\xEF\xEC\x54\x9D\xBC\x15\xD1\x6F\x45\xB4\x0F\x4F\x74\x3E\x27\x9D\xDF\x55\xBE\xF7\x31\xEE\x6D\x87\x5E\x71\x59\x86\x3D\x24\xD3\x55\xBC\x4A\x22\xA9\xA7\xAA\xC6\xA0\xEF\x84\xCE\x9F\xF9\x4C\x80\xD0\x0F\x43\x76\xBE\x86\xD0\xFE\x16\x5B\x0A\x89\x30\xED\x1C\xBD\x7F\xC2\x85\x1C\xD9\x35\x63\xE5\x9F\x54\x65\x13\x4E\x36\xAF\xD7\x3D\x88\xDE\x15\xE1\xA0\x50\x7E\xD8\xAB\xE3\x9A\x87\x7A\x88\xE1\xA9\x39\xD6\x8C\x84\x59\x01\x2F\x5A\xA7\x04\x52\x42\x04\x38\x60\x3E\x40\x42\x04\x81\x7D\x17\x31\xAA\xA1\xE3\x07\x89\x44\x6D\xC2\x10\x47\xC2\x1D\x03\xC7\x10\x08\x31\x04\xAA\x18\xA2\xC4\xD1\x24\x03\xCE\x81\x82\xB3\xF5\xC9\x62\x66\x1C\x64\x91\x08\x03\x03\xE2\x3C\x9E\xA0\x74\x3C\x2C\x0C\x50\xC4\xC2\x44\x22\xA8\x97\x84\xE7\xE5\x50\x49\x02\x5E\x12\x42\x6F\x15\x49\x29\x4C\xEC\xAD\xE2\x17\xCC\x7E\x61\x88\xCA\x43\xAE\xFD\x4E\x6A\x0F\x3E\x22\x38\x98\x62\xEF\xD5\x03\x1F\xD4\x03\x9F\xF9\xE4\x81\x02\xA6\x02\x9F\xA9\x47\x0B\x5E\xE7\xE3\x02\x46\x91\xD6\x16\x11\xC3\x9B\xA1\x04\x0E\xFB\x7B\x0A\x6D\x21\x63\xCD\x4D\x48\x44\x0B\x6E\x42\x79\x7F\x6D\x38\x5A\xC4\x83\x5C\x62\x65\x42\xDC\x30\x1A\xE4\xCD\x22\x5A\x34\x78\xEF\xB6\x08\x4F\xC8\x35\x8E\x5C\xD6\x48\xD8\xA5\xB8\x08\x18\x91\x0F\x18\x9A\x03\x6F\x66\x25\x60\x34\x28\x2A\xE5\xF7\xB9\xB5\x3B\x10\x56\x6B\x84\xDA\x05\xA1\x66\x08\x18\x11\x07\x8C\x88\x2C\x47\x0A\x4B\x4D\x7E\x34\xA9\x1D\x02\x06\x53\x64\x24\x25\x66\x98\x8C\x23\x2D\x25\x0C\x58\x83\xAC\xC4\x8C\xD6\xA2\xE4\xC6\x72\x63\x32\xC3\xDC\xED\x3D\x7E\x33\x08\x0C\x84\x0B\xA7\x0B\x53\xAF\x76\x31\x78\x75\xFB\xED\x1B\xC0\xB4\x3F\x42\xD4\x9C\x5E\xE9\xEA\x02\xC4\x78\xD7\x6C\x82\x6B\x2E\x72\x0E\x31\x00\x3D\x76\x41\xE0\xF5\x9D\x6B\x3F\x82\x53\xB0\xE0\xDB\x4C\xA6\x6D\xB9\x72\x14\x52\x1E\xBF\x72\x24\x1E\x81\x73\x4C\x26\xAE\x29\x5A\xF4\x47\x4B\x66\x3B\x5A\x94\x63\xAD\x96\x21\xF6\x51\xC4\x11\x81\x84\x57\x2D\x61\x7B\x90\xF3\x21\x9C\xD1\x42\x82\x4D\xFB\xED\xEA\xF4\xAA\x82\xB7\x1D\xBF\xE8\x53\x7C\xFE\xBB\x9C\x6A\x99\x2B\xCE\x44\x4E\xC5\x8A\xF5\xAF\x8A\x4B\x0D\x37\x9A\xB7\x0E\xED\x4F\x10\x1B\x61\xB5\x22\xAB\xCC\x55\xB1\x9A\xC4\x76\x96\x5E\x8C\xDC\xB7\x16\x37\x85\x84\x9B\x1C\x73\x08\x36\xC3\x19\x27\x8F\x17\x38\x69\x2B\xF2\x53\xDE\x2A\x81\xAD\xF0\x2D\x11\xCE\xDC\xA9\xA1\x3B\x75\x9B\x12\xFF\xFC\x56\xAA\xC3\x9A\xD4\x10\x7D\xBA\x91\xE4\x25\x66\x6D\xA1\x64\x71\x8E\x6D\xFC\xC5\x01\x1F\x7D\x5D\x6B\xCD\x9E\x09\x47\x7F\xF7\x3A\xC7\xDD\xFB\x2F\xA7\xE0\x90\x70\xE8\x7E\xFB\xE8\x48\xBD\xC6\xEB\xA3\xC0\xDA\x5C\xB5\xEC\x76\xDD\xBF\xE0\x6B\xEE\x83\x91\x9F\x1D\xAF\xD5\x29\xED\x59\x7F\xA9\xD0\x91\xCC\x63\xD3\xDE\x1B\xAE\x4B\xB8\x69\x53\xDA\x06\xA1\x2D\x8C\x90\xEE\xEF\xD8\x4B\xC2\x40\xB0\x1A\xE9\xCE\x4D\x18\x4F\x8A\x77\x0C\xA7\xF0\xCA\x1F\x9D\x39\x89\xF5\xD7\x3D\xAE\x33\xC8\xCB\x1C\x8B\xED\x42\xDB\x2B\xA8\xC3\x56\xEE\xE4\x58\x5F\x42\x76\x75\x67\x50\x78\x21\x49\xBD\xCF\x01\x74\xFE\xA2\x38\x81\x29\xD9\x63\x62\x19\x4C\xAA\x98\x5C\xBA\x4E\xFB\x3D\xE4\x28\x85\x92\xD3\x57\x6C\xAA\xC0\xA6\x33\x97\xE4\x44\x8F\xA4\x98\xEC\xCF\x3C\x59\xBF\xA8\xF8\xAE\x73\x80\x55\xAB\x6F\x50\x13\xC3\x40\x5A\x75\xAD\x55\x68\x3B\x23\x9E\xCD\xA7\x0B\xF6\xBF\x8C\x58\x57\x10\x71\x51\x25\x3E\x41\xF2\x97\x78\xE7\x2F\x06\x5A\xC1\xC6\xFD\x3C\x45\xE3\x50\xF8\x1B\xA8\x4C\x59\x6F\xF0\xA2\xBB\xA7\xA6\x67\x06\xA4\xDA\x58\x8C\x13\xF3\x2A\x10\x2B\xBA\x2B\x09\x27\x00\x0F\x48\x9B\x09\xA4\xD5\x71\x48\x1B\x8F\x74\x5B\xDB\x4A\xBD\x3E\xE3\x62\xF9\x42\xCA\xEC\x15\x92\xB3\x3E\x2B\xA9\x9F\xBC\x58\x02\x5D\x71\x5F\x64\x8E\x14\x6D\xE6\x9A\x82\x01\x18\x7F\xD6\xF9\x04\xFC\xE4\x32\x4D\xCE\x19\x26\x9C\x33\xC9\xAC\xF9\xAB\x8E\xC0\x59\x5E\x31\x55\x9C\xB0\x58\x6C\x5B\xA0\x4B\xCA\xD1\x9E\x80\xFB\x21\x8C\xA3\xAB\x0B\xBD\x68\xE6\x2E\x66\xEE\xD0\xBB\x00\x16\xA8\xE1\xCE\xAF\xA6\x40\x0D\x77\xF6\xA2\xDC\x67\xFA\x69\xCA\x4F\xAB\x4D\x09\xFA\x8B\xA9\xB2\xEE\x90\xE5\xB0\xB3\xD0\x63\x32\xA2\xA4\xC5\xC5\xDA\xA4\x03\xE6\x55\x7E\xAB\x33\x6C\x63\x1D\x74\xCE\xCE\x17\x55\xDB\x45\xFE\x1A\xEA\x67\x1A\x63\x39\x3B\x17\xBB\x8B\x3D\x9A\xCB\x45\xAE\xE0\x6E\xD8\x25\x8B\x16\xC4\xE9\x4A\x7A\x27\x98\x78\x83\xC8\x23\xD1\x19\x3B\xC4\x73\x00\x79\xEC\xF2\x55\x16\x9A\x62\xF7\xCF\xC0\xE2\xC6\x8E\x2E\xA5\x9A\x62\xF7\x4B\x58\x4D\x35\xE9\x92\xA8\xC4\xFB\x4D\x8A\x3C\xF8\x01\x0F\xE9\x8C\xB9\x33\x96\x4E\x36\xEA\xF8\x1C\x80\x7B\x65\x90\xC7\xEE\x1F\xE1\xB2\x07\x94\xE9\xCF\xBD\x0C\xA6\xF6\x37\xF6\xF2\x71\x7F\x50\x7E\x42\xAD\x05\x0C\x20\xB0\x7A\xD5\x20\x5C\xA7\x7A\x96\x54\xE0\x37\x22\x33\x20\xB5\xC9\x2E\x41\x8D\xB9\x03\x35\xD3\x1D\xA8\x49\x77\x30\x36\x4C\x8F\x0F\x13\xF5\x16\x2B\x4A\xEA\x06\xFE\x52\xA2\x84\x85\x6D\x2B\xF6\xBE\xE2\x3D\xF6\x15\x95\xCB\xEA\xE4\x41\x25\x3A\xA8\xA4\x32\x9C\x28\x6C\x8E\xE9\x1F\xB0\xF8\x5F\xCB\xFE\x3F\x27\xD7\xBA\xAF\x0C\xF2\x86\xFB\x27\xB8\xCC\x16\x3A\xF7\xEB\x1A\x8A\x3F\x03\xF5\xBF\x08\x3E\xE6\x2F\x86\x46\xF1\x35\x09\xCF\xA6\x7C\xB6\xFC\x8B\x65\x47\x40\xE8\x2D\x38\xE4\xC0\xB2\x2D\x14\x47\x83\xCE\x5F\x06\x4C\x58\x38\x8A\x24\x77\xA8\x99\x4B\x21\x64\xB1\x7D\xB1\x9D\xB2\xE1\x74\xFE\xDA\x1F\xD7\xBC\x7B\xE9\xFC\x4D\x71\x78\xAB\x23\x67\x27\x1B\xDA\x93\x0D\xCD\xC9\x86\x16\x53\xE4\x73\x3B\xA9\x4D\x32\x03\x17\x5D\x4A\x8D\xFB\xE1\xDB\x92\xA9\xBA\xC8\x07\xAD\xD4\x78\x9D\x86\x7D\x18\xC9\x44\x33\x31\xD1\x5C\x4A\xB5\xFB\xDD\x30\xD1\x84\x89\x7A\x6C\xA2\x0F\x05\x6A\x7A\xA2\x72\x3F\x98\x98\xA8\x66\x4C\xD4\x93\xBC\x37\x26\x1B\x38\x81\x42\xFB\x08\x94\x69\x5D\xC8\xF5\xF8\xD1\x21\xC5\x27\x96\xC4\x76\x15\x8E\xD4\xE1\xF2\x68\x19\xFD\x71\xFD\xFC\x6A\xAE\x16\x32\xBD\xEC\xF3\x7E\x45\xF8\xFF\xBC\x5B\x90\x1F\x0E\x48\x6D\x66\x6A\xD9\x1F\xBB\xCC\x72\xEB\x90\xE0\x29\x80\x83\xE5\x17\x0F\x32\x33\x9D\x46\x91\x21\x7D\x68\x7F\xA1\x23\x1C\xA9\x03\xB9\xE5\x1F\x5D\xF0\x06\xF1\x9F\x1F\x14\x5F\x5E\x18\x5B\xF4\x42\x61\x46\xB5\xB5\x6B\x6D\x21\x6D\xCA\xA3\xB1\x60\x6F\xC2\x88\xC7\x97\xEE\x10\xF7\xB8\x17\x07\xEF\xBD\x93\x99\x22\x6F\xE2\x24\xDD\x7B\xFC\x3B\xBC\x25\x98\xEA\xAA\xBF\x63\xF0\x79\x97\x44\x9C\x6A\x91\x86\x5A\x2A\x67\xF2\xEA\x7F\xF8\x23\xFE\x7B\xFF\x85\xCC\xD4\xC6\xCC\x66\xA4\xC1\x79\x5E\x60\xE0\xCE\x3B\x59\xA3\xC6\xD9\x4F\xA7\xF8\x89\x49\x53\xE3\xCE\x3B\x59\xEC\x19\x42\xEB\x30\x4B\x8A\x0B\x6C\x3F\xB6\x29\x69\x61\xE4\xCE\x7A\x7E\x23\x9F\x11\x88\xA7\x6A\xBA\x51\x96\xF0\x94\x88\x9A\x4C\x51\xAE\x08\x51\x7E\x01\x51\x83\xBC\xC9\xAC\x8B\xD7\x6F\xB2\x37\x40\x6A\x6E\xFA\x7B\x52\xC9\x60\x5B\x5B\xC2\xE5\xC0\xBD\xC8\x19\x14\x9F\x2A\x79\x8C\x5C\xD3\xA4\x50\x50\x61\x2E\xF9\xAB\xE6\x75\x5B\x5B\xD2\xC6\x7A\xF5\xC7\x12\x2B\x49\x68\xB2\xF5\xDE\x9D\x2C\xB6\x14\xDB\xBF\x42\xA5\x46\xEA\xB0\x76\x8F\x36\x99\xE5\xBA\x27\x57\x25\xBB\xFD\x7B\xF0\xCF\x0F\xE5\x39\x06\xC9\x98\x09\xEA\x85\xCC\x2C\x43\x16\xF9\x54\xDA\x9B\xA0\x96\x7B\x35\xD2\x9B\x62\x9D\xC2\x1A\x9B\xA0\x2A\x4C\x30\x2A\x2F\x7D\xE5\xDE\x4F\xF8\x8C\xC8\x1C\x76\x7E\x52\x38\x07\xC2\xCE\x07\xFC\xBD\x73\x47\x6E\x57\x6E\xA2\x91\x30\xD8\xF9\xE3\xC2\x31\x71\x3E\x51\x9C\x79\x3A\x7F\x1B\xAE\x8B\xED\xAA\x1C\x77\x5A\x17\x25\x68\xF9\xCD\x9A\x4A\x26\x03\x64\x3A\x7F\xE7\x2F\x03\x24\x13\x24\xEC\xFC\xA9\xDC\x81\xA1\x44\x60\xB9\xB5\xE5\x80\xFB\x03\xAD\xF4\x48\x1D\x16\x3F\xA7\x13\xBA\x43\x4E\xCE\x1B\xE5\xA5\x58\xF5\x8B\xE9\x5C\x02\xA8\xB4\x89\x1A\x31\xD8\xCE\x8F\x83\x8F\xC3\xCE\x3B\x85\xB7\x2B\xBE\xF9\x28\xD0\x0E\xBF\x1C\x6D\xE6\xA6\x80\x9D\x0F\x3C\x9A\x60\xE0\x7F\xB8\x7B\x72\x35\x8D\x09\x05\x76\x7E\x7E\x28\x4F\x71\xAF\x40\xA6\x82\x5D\x07\xD8\x71\x21\x8B\x96\x41\xF6\x89\x21\xFC\xB6\x9C\xC4\x64\x57\x1B\xC2\xCD\x0C\xB9\x8B\x0F\xBA\x63\x3B\x3F\x2E\x7F\x56\x2C\x6C\x48\xCC\xA3\x06\xBB\x5C\x6F\x79\x76\x71\xB3\xF3\xD3\xA2\xB9\xF3\x73\x0F\x7B\x1B\x6D\x5D\x0B\xA2\x03\x33\xA6\x03\xED\x75\xA0\xC7\x74\x60\x0A\x1D\xE8\x8F\xD1\xC1\xBC\x25\x25\xC7\x47\x33\xA6\xFD\xDF\xC1\x42\xFD\x7F\x32\xA6\xFE\x63\x96\x4E\x3E\x6A\x69\x39\xB4\x86\x29\xAE\xED\x13\xA9\xF0\x3A\xCE\x9C\x26\xEC\xDC\x9D\x66\xCF\xFE\x0A\xE2\xE8\x31\x5F\x31\x42\x9B\xB9\x7A\xFC\xA8\xFA\x55\xA0\x5D\xDE\xAD\xF1\xE3\x94\xF8\x6D\xDD\x91\x0B\x16\xF9\xA8\x2F\x23\xFE\x97\x06\x61\x3A\x58\xFB\x04\x4A\x79\xE3\x23\x1B\x5B\x5B\xFD\x1B\xBD\x2D\x92\xF2\x31\x90\x8A\xB1\xE1\xDE\xED\x3E\x5C\xEF\x6D\xED\xF7\x01\xDE\x6D\x99\x9D\xDE\x76\x1F\x7F\xDC\x5A\x81\xAA\xF6\x12\xA7\x6B\x21\x55\xAD\xC0\x51\x97\xF5\x8D\x66\x66\x79\x63\x34\x5D\xDD\x18\x4F\x14\x37\x36\x3E\xA2\xB6\x31\x99\x55\xDA\xD8\x1C\xAF\x6C\x6C\xCD\x2E\x6C\xB4\x33\xEB\x1A\xDB\x13\x65\x8D\xE9\x71\x55\x8D\x73\xC7\x14\x35\xCE\x1F\x57\xD3\xD8\x99\x2A\x69\x3C\x71\x5C\x45\xE3\xAC\xAA\x45\x5F\x50\x75\xEF\xB1\xF5\x8C\xF7\x1D\x57\xC0\x78\xF2\xD8\xFA\xC5\xFB\x27\xCB\x17\x1F\x98\xA8\x5E\x3C\x35\xAB\x78\xF1\x33\x13\xB5\x8B\x0F\x4E\x96\x2E\x2E\x4C\x57\x2E\x3E\x34\x5E\xB8\xF8\xF0\x64\xDD\xE2\x23\x53\x65\x8B\x8F\x96\x55\x8B\x54\x2F\x5A\xCC\x26\x6A\x16\xF3\xA9\x92\xC5\xC7\x6A\x15\x8B\x8F\xCF\x2E\x58\x7C\x62\xAA\x44\xF1\xB3\x55\x85\xE2\xE7\x66\x14\x28\x9E\xAE\xD5\x27\x9E\x99\x2A\x4F\xFC\xFC\x44\x75\xE2\xD9\xC9\xE2\xC4\x73\xC7\xD5\x26\x9E\x9F\x2E\x4D\x7C\xB2\x56\x4B\xF9\x85\x7A\x85\xEA\x53\xB5\x92\xC5\x2F\xD6\x2B\x16\xBF\x54\x2F\x58\x7C\xBA\xAA\x57\xFC\xF2\xAC\x72\xC5\x67\x8E\xA9\x56\x5C\x9C\x2A\x56\x7C\xF6\x98\x5A\xC5\xAF\x1C\x5B\xAA\xF8\xDC\xF1\x95\x8A\xB3\xAA\x11\xBD\xD2\x9E\x3F\xB6\x4E\xF1\x6B\xD3\x65\x8A\x5F\x9F\xAE\x52\xFC\xC6\x8C\x22\xC5\x17\x66\xD6\x28\x2E\xCD\x2E\x51\x74\x33\x2B\x14\x97\x27\x5B\x87\xBB\x9B\xFD\x9D\x6F\xCE\x2E\x5B\xBC\x30\x51\xB5\xF8\xBF\x01\x00\x00\xFF\xFF\xC6\x17\x79\xC4\x15\x2F\x00\x00") diff --git a/internal/compiler/wasm/opa/opa.wasm b/internal/compiler/wasm/opa/opa.wasm new file mode 100755 index 0000000000..06a656d2ee Binary files /dev/null and b/internal/compiler/wasm/opa/opa.wasm differ diff --git a/main.go b/main.go index ef634c542f..5473b99ad7 100644 --- a/main.go +++ b/main.go @@ -19,3 +19,4 @@ func main() { // //go:generate pigeon -o ast/parser.go ast/rego.peg //go:generate goimports -w ast/parser.go +//go:generate go run internal/cmd/genopawasm/main.go -o internal/compiler/wasm/opa/opa.go internal/compiler/wasm/opa/opa.wasm diff --git a/wasm/Dockerfile b/wasm/Dockerfile new file mode 100644 index 0000000000..21d8da4863 --- /dev/null +++ b/wasm/Dockerfile @@ -0,0 +1,33 @@ +FROM ubuntu:18.04 + +RUN apt-get update && apt-get install -y curl git build-essential + +RUN bash -c 'echo -ne "deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic main\ndeb-src http://apt.llvm.org/bionic/ llvm-toolchain-bionic main" > /etc/apt/sources.list.d/llvm.list' + +RUN curl -L https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - + +RUN apt-get update && \ + apt-get install -y \ + cmake \ + clang-8 \ + lld-8 + +ENV CC=clang-8 +ENV CXX=clang-8 +ENV LLD=wasm-ld-8 +ENV AR=llvm-ar-8 +ENV RANLIB=llvm-ranlib-8 + +RUN ln -s /usr/bin/clang-8 /usr/bin/clang && \ + ln -s /usr/bin/clang++-8 /usr/bin/clang++ && \ + ln -s /usr/bin/clang-cpp-8 /usr/bin/clang-cpp + +RUN git clone https://github.com/WebAssembly/wabt && \ + cd wabt && \ + git checkout 1.0.5 && \ + git submodule update --init && \ + make + +ENV PATH="/wabt/out/clang/Debug:${PATH}" + +WORKDIR /src \ No newline at end of file diff --git a/wasm/Makefile b/wasm/Makefile new file mode 100644 index 0000000000..6b5f6f9e73 --- /dev/null +++ b/wasm/Makefile @@ -0,0 +1,79 @@ +DOCKER := docker + +WASM_BUILDER_REPOSITORY := openpolicyagent/opa-wasm-builder +WASM_BUILDER_VERSION := 1.0 +WASM_BUILDER_IMAGE := $(WASM_BUILDER_REPOSITORY):$(WASM_BUILDER_VERSION) +WASM_OBJ_DIR := _obj + +$(shell mkdir -p $(WASM_OBJ_DIR)) + +CFLAGS := \ + -O3 \ + -nostdinc \ + -nodefaultlibs \ + --target=wasm32-unknown-unknown-wasm + +.PHONY: all +all: build test + +.PHONY: clean +clean: + rm -fr $(WASM_OBJ_DIR) + +.PHONY: builder +builder: Dockerfile + @$(DOCKER) build -t $(WASM_BUILDER_IMAGE) -f Dockerfile . + +.PHONY: build +build: + @$(DOCKER) run -it --rm -v $(CURDIR):/src $(WASM_BUILDER_IMAGE) make $(WASM_OBJ_DIR)/opa.wasm + +.PHONY: test +test: + @$(DOCKER) run -it --rm -v $(CURDIR):/src $(WASM_BUILDER_IMAGE) make $(WASM_OBJ_DIR)/opa-test.wasm + @$(DOCKER) run -it --rm -e VERBOSE=$(VERBOSE) -v $(CURDIR):/src -w /src node:8 node test.js $(WASM_OBJ_DIR)/opa-test.wasm + +.PHONY: hack +hack: + @$(DOCKER) run -it --rm -v $(CURDIR):/src $(WASM_BUILDER_IMAGE) + +$(WASM_OBJ_DIR)/malloc.wasm: src/malloc.c + @$(CC) $(CFLAGS) -c $^ -o $@ + +$(WASM_OBJ_DIR)/string.wasm: src/string.c + @$(CC) $(CFLAGS) -c $^ -o $@ + +$(WASM_OBJ_DIR)/json.wasm: src/json.c + @$(CC) $(CFLAGS) -c $^ -o $@ + +$(WASM_OBJ_DIR)/value.wasm: src/value.c + @$(CC) $(CFLAGS) -c $^ -o $@ + +$(WASM_OBJ_DIR)/opa.wasm: $(WASM_OBJ_DIR)/malloc.wasm \ + $(WASM_OBJ_DIR)/value.wasm \ + $(WASM_OBJ_DIR)/string.wasm \ + $(WASM_OBJ_DIR)/json.wasm + @wasm-ld-8 \ + --allow-undefined-file=src/undefined.symbols \ + --import-memory \ + --no-entry \ + --export-all \ + $^ \ + -o $@ + @wasm2wat $(WASM_OBJ_DIR)/opa.wasm > $(WASM_OBJ_DIR)/opa.wast + +$(WASM_OBJ_DIR)/test.wasm: tests/test.c + @$(CC) $(CFLAGS) -I src -c $^ -o $@ + +$(WASM_OBJ_DIR)/opa-test.wasm: $(WASM_OBJ_DIR)/test.wasm \ + $(WASM_OBJ_DIR)/malloc.wasm \ + $(WASM_OBJ_DIR)/value.wasm \ + $(WASM_OBJ_DIR)/string.wasm \ + $(WASM_OBJ_DIR)/json.wasm + @wasm-ld-8 \ + --allow-undefined-file=tests/undefined.symbols \ + --import-memory \ + --no-entry \ + --export-all \ + $^ \ + -o $@ diff --git a/wasm/README.md b/wasm/README.md new file mode 100644 index 0000000000..a490b3156d --- /dev/null +++ b/wasm/README.md @@ -0,0 +1,42 @@ +# OPA-WASM + +This directory contains a library that implements various low-level +operations for policies compiled into WebAssembly (WASM). Specifically, the +library implements: + +* JSON parsing +* JSON AST (e.g., comparison, iteration, lookup, etc.) +* String operations +* Memory allocation + +This library does not make any backwards compatibility guarantees. + +## Development + +You should have Docker installed to build and test changes to the library. We +commit the output of the build (`opa.wasm`) into the repository so it's +important for the build output to be reproducible. + +You can build the library by running `make build`. This will produce WASM +executables under the `_obj` directory. + +You can test the library by running `make test`. By default the test runner +does not print messages when tests pass. If you run `make test VERBOSE=1` it +will log all of the tests that were run. + +You can run `make hack` to start a shell inside the builder image. This is +useful if you need to interact with low-level WASM tooling like +`wasm-objdump`, `wasm2wat`, etc. or LLVM itself. + +You must manually push the builder image if you make changes to it (run `make +builder` to produce a new Docker image). + +## Vendoring + +If you make changes to the library, run the `make generate` in the parent +directory and commit the results back to the repository. The `generate` +target will: + +1. Build the OPA-WASM library +2. Copy the library into the [internal/compiler/wasm/opa](../internal/compiler/wasm/opa) directory. +3. Run the tool to generate the [internal/compiler/wasm/opa/opa.go](../internal/compiler/wasm/opa/opa.go) file. \ No newline at end of file diff --git a/wasm/src/json.c b/wasm/src/json.c new file mode 100644 index 0000000000..947a17678f --- /dev/null +++ b/wasm/src/json.c @@ -0,0 +1,497 @@ +#include "malloc.h" +#include "string.h" +#include "value.h" +#include "json.h" + +static opa_value *opa_json_parse_token(opa_json_lex *ctx, int token); + +int opa_json_lex_offset(opa_json_lex *ctx) +{ + return ctx->curr - ctx->input; +} + +int opa_json_lex_remaining(opa_json_lex *ctx) +{ + return ctx->len - opa_json_lex_offset(ctx); +} + +int opa_json_lex_eof(opa_json_lex *ctx) +{ + return (ctx->curr - ctx->input) >= ctx->len; +} + +int opa_json_lex_read_atom(opa_json_lex *ctx, const char *str, int n, int token) +{ + if (opa_json_lex_remaining(ctx) >= n) + { + if (opa_strncmp(str, ctx->curr, n) == 0) + { + ctx->curr += n; + return token; + } + } + return OPA_JSON_TOKEN_ERROR; +} + +void opa_json_lex_read_digits(opa_json_lex *ctx) +{ + while (!opa_json_lex_eof(ctx) && opa_isdigit(*ctx->curr)) + { + ctx->curr++; + } +} + +int opa_json_lex_read_unicode(opa_json_lex *ctx) +{ + if (opa_json_lex_remaining(ctx) >= 4) + { + for (int i = 0; i < 4; i++) + { + if (!opa_ishex(ctx->curr[i])) + { + return -1; + } + } + ctx->curr += 4; + return 0; + } + + return 1; +} + +int opa_json_lex_read_number(opa_json_lex *ctx) +{ + ctx->buf = ctx->curr; + + // Handle sign component. + if (*ctx->curr == '-') + { + ctx->curr++; + + if (opa_json_lex_eof(ctx)) + { + goto err; + } + } + + // Handle integer component. + if (*ctx->curr == '0') + { + ctx->curr++; + } + else if (opa_isdigit(*ctx->curr)) + { + opa_json_lex_read_digits(ctx); + } + else + { + goto err; + } + + if (opa_json_lex_eof(ctx)) + { + goto out; + } + + // Handle fraction component. + if (*ctx->curr == '.') + { + ctx->curr++; + opa_json_lex_read_digits(ctx); + + if (opa_json_lex_eof(ctx)) + { + goto out; + } + } + + // Handle exponent component. + if (*ctx->curr == 'e' || *ctx->curr == 'E') + { + ctx->curr++; + + if (opa_json_lex_eof(ctx)) + { + goto err; + } + + if (*ctx->curr == '+' || *ctx->curr == '-') + { + ctx->curr++; + + if (opa_json_lex_eof(ctx)) + { + goto err; + } + } + + opa_json_lex_read_digits(ctx); + } + +out: + ctx->buf_end = ctx->curr; + return OPA_JSON_TOKEN_NUMBER; + +err: + return OPA_JSON_TOKEN_ERROR; +} + +int opa_json_lex_read_string(opa_json_lex *ctx) +{ + if (*ctx->curr != '"') + { + goto err; + } + + ctx->buf = ++ctx->curr; + + while (1) + { + if (opa_json_lex_eof(ctx)) + { + goto err; + } + + char b = *ctx->curr; + + switch (b) + { + case '\\': + ctx->curr++; + + if (opa_json_lex_eof(ctx)) + { + goto err; + } + + b = *ctx->curr; + + switch (b) + { + case '"': + case '\\': + case '/': + case 'b': + case 'f': + case 'n': + case 'r': + case 't': + ctx->curr++; + break; + case 'u': + ctx->curr++; + if (opa_json_lex_read_unicode(ctx) != 0) + { + goto err; + } + break; + default: + goto err; + } + + case '"': + goto out; + + default: + if (b < ' ' || b > '~') + { + goto err; + } + ctx->curr++; + break; + } + } +out: + ctx->buf_end = ctx->curr++; + return OPA_JSON_TOKEN_STRING; + +err: + return OPA_JSON_TOKEN_ERROR; +} + +int opa_json_lex_read(opa_json_lex *ctx) +{ + while (!opa_json_lex_eof(ctx)) + { + char b = *ctx->curr; + switch (b) + { + case 'n': + return opa_json_lex_read_atom(ctx, "null", 4, OPA_JSON_TOKEN_NULL); + case 't': + return opa_json_lex_read_atom(ctx, "true", 4, OPA_JSON_TOKEN_TRUE); + case 'f': + return opa_json_lex_read_atom(ctx, "false", 5, OPA_JSON_TOKEN_FALSE); + case '"': + return opa_json_lex_read_string(ctx); + case '{': + ctx->curr++; + return OPA_JSON_TOKEN_OBJECT_START; + case '}': + ctx->curr++; + return OPA_JSON_TOKEN_OBJECT_END; + case '[': + ctx->curr++; + return OPA_JSON_TOKEN_ARRAY_START; + case ']': + ctx->curr++; + return OPA_JSON_TOKEN_ARRAY_END; + case ',': + ctx->curr++; + return OPA_JSON_TOKEN_COMMA; + case ':': + ctx->curr++; + return OPA_JSON_TOKEN_COLON; + default: + if (opa_isdigit(b) || b == '-') + { + return opa_json_lex_read_number(ctx); + } + else if (opa_isspace(b)) + { + ctx->curr++; + continue; + } + return OPA_JSON_TOKEN_ERROR; + } + } + + return OPA_JSON_TOKEN_EOF; +} + +void opa_json_lex_init(const char *input, size_t len, opa_json_lex *ctx) +{ + ctx->input = input; + ctx->len = len; + ctx->curr = input; + ctx->buf = NULL; + ctx->buf_end = NULL; +} + +long long opa_json_parse_int(const char *buf, int len) +{ + int i = 0; + int sign = 1; + + if (buf[i] == '-') + { + sign = -1; + i++; + } + + long long n = 0; + + for (; i < len; i++) + { + n = (n * 10) + (long long)(buf[i] - '0'); + } + + return n * sign; +} + +double opa_json_parse_float(const char *buf, int len) +{ + double sign = 1.0; + int i = 0; + + if (buf[i] == '-') + { + sign = -1.0; + i++; + } + + // Handle integer component. + double d = 0.0; + + for (; i < len && opa_isdigit(buf[i]); i++) + { + d = (10.0 * d) + (double)(buf[i] - '0'); + } + + d *= sign; + + if (i == len) + { + return d; + } + + // Handle fraction component. + if (buf[i] == '.') + { + i++; + + double b = 0.1; + double frac = 0; + + for (; i < len && opa_isdigit(buf[i]); i++) + { + frac += b * (buf[i] - '0'); + b /= 10.0; + } + + d += (frac * sign); + + if (i == len) + { + return d; + } + } + + // Handle exponent component. + i++; + int exp_sign = 1; + + if (buf[i] == '-') + { + exp_sign = -1; + i++; + } + else if (buf[i] == '+') + { + i++; + } + + int e = 0; + + for (; i < len; i++) + { + e = 10 * e + (int)(buf[i] - '0'); + } + + // Calculate pow(10, e). + int x = 1; + + for (; e > 0; e--) + { + x *= 10; + } + + return d * (double)(exp_sign * x); +} + +opa_value *opa_json_parse_number(const char *buf, int len) +{ + for (int i = 0; i < len; i++) + { + if (buf[i] == '.' || buf[i] == 'e' || buf[i] == 'E') + { + return opa_number_float(opa_json_parse_float(buf, len)); + } + } + + return opa_number_int(opa_json_parse_int(buf, len)); +} + +opa_value *opa_json_parse_array(opa_json_lex *ctx) +{ + opa_value *ret = opa_array(); + opa_array_t *arr = opa_cast_array(ret); + int sep = 0; + + while (1) + { + int token = opa_json_lex_read(ctx); + + switch (token) + { + case OPA_JSON_TOKEN_ARRAY_END: + return ret; + case OPA_JSON_TOKEN_COMMA: + if (sep) + { + sep = 0; + continue; + } + } + + opa_value *elem = opa_json_parse_token(ctx, token); + + if (elem == NULL) + { + return NULL; + } + + opa_array_append(arr, elem); + sep = 1; + } +} + +opa_value *opa_json_parse_object(opa_json_lex *ctx) +{ + opa_value *ret = opa_object(); + opa_object_t *obj = opa_cast_object(ret); + int sep = 0; + + while (1) + { + int token = opa_json_lex_read(ctx); + + switch (token) + { + case OPA_JSON_TOKEN_OBJECT_END: + return ret; + case OPA_JSON_TOKEN_COMMA: + if (sep) + { + sep = 0; + continue; + } + } + + opa_value *key = opa_json_parse_token(ctx, token); + + if (key == NULL) + { + return NULL; + } + + token = opa_json_lex_read(ctx); + + if (token != OPA_JSON_TOKEN_COLON) + { + return NULL; + } + + token = opa_json_lex_read(ctx); + opa_value *value = opa_json_parse_token(ctx, token); + + if (value == NULL) + { + return NULL; + } + + opa_object_insert(obj, key, value); + sep = 1; + } +} + +opa_value *opa_json_parse_token(opa_json_lex *ctx, int token) +{ + switch (token) + { + case OPA_JSON_TOKEN_NULL: + return opa_null(); + case OPA_JSON_TOKEN_TRUE: + return opa_boolean(TRUE); + case OPA_JSON_TOKEN_FALSE: + return opa_boolean(FALSE); + case OPA_JSON_TOKEN_NUMBER: + return opa_json_parse_number(ctx->buf, ctx->buf_end - ctx->buf); + case OPA_JSON_TOKEN_STRING: + return opa_string(ctx->buf, ctx->buf_end - ctx->buf); + case OPA_JSON_TOKEN_ARRAY_START: + return opa_json_parse_array(ctx); + case OPA_JSON_TOKEN_OBJECT_START: + return opa_json_parse_object(ctx); + default: + return NULL; + } +} + +opa_value *opa_json_parse(const char *input, size_t len) +{ + opa_json_lex ctx; + opa_json_lex_init(input, len, &ctx); + int token = opa_json_lex_read(&ctx); + return opa_json_parse_token(&ctx, token); +} \ No newline at end of file diff --git a/wasm/src/json.h b/wasm/src/json.h new file mode 100644 index 0000000000..edf03d951b --- /dev/null +++ b/wasm/src/json.h @@ -0,0 +1,34 @@ +#ifndef OPA_JSON_H +#define OPA_JSON_H + +#include "value.h" + +typedef struct +{ + const char *input; + size_t len; + const char *buf; + const char *buf_end; + const char *curr; +} opa_json_lex; + +#define OPA_JSON_TOKEN_ERROR 0 +#define OPA_JSON_TOKEN_EOF 1 +#define OPA_JSON_TOKEN_NULL 2 +#define OPA_JSON_TOKEN_TRUE 3 +#define OPA_JSON_TOKEN_FALSE 4 +#define OPA_JSON_TOKEN_NUMBER 5 +#define OPA_JSON_TOKEN_STRING 6 +#define OPA_JSON_TOKEN_OBJECT_START 7 +#define OPA_JSON_TOKEN_OBJECT_END 8 +#define OPA_JSON_TOKEN_ARRAY_START 9 +#define OPA_JSON_TOKEN_ARRAY_END 10 +#define OPA_JSON_TOKEN_COMMA 11 +#define OPA_JSON_TOKEN_COLON 12 + +void opa_json_lex_init(const char *input, size_t len, opa_json_lex *ctx); +int opa_json_lex_read(opa_json_lex *ctx); + +opa_value *opa_json_parse(const char *input, size_t len); + +#endif \ No newline at end of file diff --git a/wasm/src/malloc.c b/wasm/src/malloc.c new file mode 100644 index 0000000000..2b9a234dce --- /dev/null +++ b/wasm/src/malloc.c @@ -0,0 +1,20 @@ +#include "std.h" + +#define WASM_PAGE_SIZE (65536) + +static unsigned int heap; + +static unsigned int grow_heap(size_t size) +{ + size_t pages = (size / WASM_PAGE_SIZE) + 1; + return __builtin_wasm_grow_memory(pages) * WASM_PAGE_SIZE; +} + +void *opa_malloc(size_t size) +{ + return (void *)grow_heap(size); +} + +void opa_free(void *ptr) +{ +} \ No newline at end of file diff --git a/wasm/src/malloc.h b/wasm/src/malloc.h new file mode 100644 index 0000000000..e2738f10dc --- /dev/null +++ b/wasm/src/malloc.h @@ -0,0 +1,9 @@ +#ifndef OPA_MALLOC_H +#define OPA_MALLOC_H + +#include "std.h" + +void *opa_malloc(size_t size); +void opa_free(void *ptr); + +#endif \ No newline at end of file diff --git a/wasm/src/std.h b/wasm/src/std.h new file mode 100644 index 0000000000..5f88ae08da --- /dev/null +++ b/wasm/src/std.h @@ -0,0 +1,19 @@ +#ifndef OPA_TYPES_H +#define OPA_TYPES_H + +#define NULL (0) +#define TRUE (1) +#define FALSE (0) + +typedef unsigned long size_t; + +#define offsetof(st, member) (size_t)(&((st *)0)->member) + +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) ); }) + +void opa_abort(const char *msg); +void opa_println(const char *msg); + +#endif \ No newline at end of file diff --git a/wasm/src/string.c b/wasm/src/string.c new file mode 100644 index 0000000000..d0c32c68e2 --- /dev/null +++ b/wasm/src/string.c @@ -0,0 +1,90 @@ +#include "std.h" + +size_t opa_strlen(const char *s) +{ + const char *ptr = s; + + while (1) + { + if (*ptr == '\0') + { + return ptr - s; + } + + ptr += 1; + } +} + +int opa_strncmp(const char *a, const char *b, int num) +{ + unsigned char *a1 = (unsigned char *)a; + unsigned char *b1 = (unsigned char *)b; + + while (num--) + { + if (*a1 < *b1) + { + return -1; + } + else if (*a1 > *b1) + { + return 1; + } + a1++; + b1++; + } + + return 0; +} + +int opa_strcmp(const char *a, const char *b) +{ + size_t len_a = opa_strlen(a); + size_t len_b = opa_strlen(b); + size_t min = len_a; + + if (len_b < min) + { + min = len_b; + } + + unsigned char *a1 = (unsigned char *)a; + unsigned char *b1 = (unsigned char *)b; + + for (int i = 0; i < min; i++) + { + if (a1[i] < b1[i]) + { + return -1; + } + else if (a[i] > b[i]) + { + return 1; + } + } + + if (len_a < len_b) + { + return -1; + } + else if (len_a > len_b) + { + return 1; + } + return 0; +} + +int opa_isdigit(char b) +{ + return b >= '0' && b <= '9'; +} + +int opa_isspace(char b) +{ + return b == ' ' || b == '\r' || b == '\n' || b == '\t'; +} + +int opa_ishex(char b) +{ + return opa_isdigit(b) || (b >= 'A' && b <= 'F') || (b >= 'a' && b <= 'f'); +} \ No newline at end of file diff --git a/wasm/src/string.h b/wasm/src/string.h new file mode 100644 index 0000000000..4e3a10b6cf --- /dev/null +++ b/wasm/src/string.h @@ -0,0 +1,14 @@ +#ifndef OPA_STRING_H +#define OPA_STRING_H + +#include "std.h" + +size_t opa_strlen(const char *s); +double opa_strntod(const char *s, int len); +int opa_strncmp(const char *a, const char *b, int num); +int opa_strcmp(const char *a, const char *b); +int opa_isdigit(char b); +int opa_isspace(char b); +int opa_ishex(char b); + +#endif \ No newline at end of file diff --git a/wasm/src/undefined.symbols b/wasm/src/undefined.symbols new file mode 100644 index 0000000000..08a392b61b --- /dev/null +++ b/wasm/src/undefined.symbols @@ -0,0 +1,2 @@ +opa_abort +opa_println \ No newline at end of file diff --git a/wasm/src/value.c b/wasm/src/value.c new file mode 100644 index 0000000000..09ff357c7e --- /dev/null +++ b/wasm/src/value.c @@ -0,0 +1,689 @@ +#include "malloc.h" +#include "string.h" +#include "value.h" + +#define OPA_ARRAY_INITIAL_CAP (10) + +opa_value *opa_value_get_object(opa_object_t *obj, opa_value *key) +{ + opa_object_elem_t *elem = opa_object_get(obj, key); + + if (elem != NULL) + { + return elem->v; + } + + return NULL; +} + +opa_value *opa_value_get_array_native(opa_array_t *arr, long long i) +{ + if (i >= arr->len) + { + return NULL; + } + + return arr->elems[i].v; +} + +opa_value *opa_value_get_array(opa_array_t *arr, opa_value *key) +{ + if (key->type != OPA_NUMBER) + { + return NULL; + } + + opa_number_t *num = opa_cast_number(key); + + if (num->is_float) + { + return NULL; + } + + if (num->v.i < 0) + { + return NULL; + } + + return opa_value_get_array_native(arr, num->v.i); +} + +opa_value *opa_value_get(opa_value *node, opa_value *key) +{ + if (node != NULL) + { + switch (node->type) + { + case OPA_ARRAY: + return opa_value_get_array(opa_cast_array(node), key); + case OPA_OBJECT: + return opa_value_get_object(opa_cast_object(node), key); + } + } + return NULL; +} + +opa_value *opa_value_iter_object(opa_object_t *obj, opa_value *prev) +{ + if (prev == NULL) + { + if (obj->head == NULL) + { + return NULL; + } + + return obj->head->k; + } + + opa_object_elem_t *elem = opa_object_get(obj, prev); + + if (elem != NULL && elem->next != NULL) + { + return elem->next->k; + } + + return NULL; +} + +opa_value *opa_value_iter_array(opa_array_t *arr, opa_value *prev) +{ + if (prev == NULL) + { + if (arr->len == 0) + { + return NULL; + } + + return arr->elems[0].i; + } + + if (prev->type != OPA_NUMBER) + { + return NULL; + } + + opa_number_t *num = opa_cast_number(prev); + + if (num->is_float) + { + return NULL; + } + + long long i = num->v.i; + i++; + + if (i < 0 || i >= arr->len) + { + return NULL; + } + + return arr->elems[i].i; +} + +opa_value *opa_value_iter(opa_value *node, opa_value *prev) +{ + if (node != NULL) + { + switch (node->type) + { + case OPA_ARRAY: + return opa_value_iter_array(opa_cast_array(node), prev); + case OPA_OBJECT: + return opa_value_iter_object(opa_cast_object(node), prev); + } + } + + return NULL; +} + +size_t opa_value_length_object(opa_object_t *obj) +{ + size_t i = 0; + for (opa_object_elem_t *elem = obj->head; elem != NULL; elem = elem->next) + { + i++; + } + return i; +} + +size_t opa_value_length_array(opa_array_t *arr) +{ + return arr->len; +} + +size_t opa_value_length_string(opa_string_t *str) +{ + return str->len; +} + +size_t opa_value_length(opa_value *node) +{ + switch (node->type) + { + case OPA_ARRAY: + return opa_value_length_array(opa_cast_array(node)); + case OPA_OBJECT: + return opa_value_length_object(opa_cast_object(node)); + case OPA_STRING: + return opa_value_length_string(opa_cast_string(node)); + default: + return 0; + } +} + +int opa_value_compare_float(double a, double b) +{ + if (a < b) + { + return -1; + } + else if (a > b) + { + return 1; + } + return 0; +} + +int opa_value_compare_number(opa_number_t *a, opa_number_t *b) +{ + if (!a->is_float && !b->is_float) + { + if (a->v.i < b->v.i) + { + return -1; + } + else if (a->v.i > b->v.i) + { + return 1; + } + return 0; + } + + if (a->is_float && b->is_float) + { + return opa_value_compare_float(a->v.f, b->v.f); + } + + double a1, b1; + + if (!a->is_float) + { + a1 = (double)a->v.i; + b1 = b->v.f; + } + else + { + a1 = a->v.f; + b1 = (double)b->v.i; + } + + return opa_value_compare_float(a1, b1); +} + +int opa_value_compare_string(opa_string_t *a, opa_string_t *b) +{ + size_t min = a->len; + + if (b->len < min) + { + min = b->len; + } + + int cmp = opa_strncmp(a->v, b->v, min); + + if (cmp != 0) + { + return cmp; + } + + if (a->len < b->len) + { + return -1; + } + else if (a->len > b->len) + { + return 1; + } + return 0; +} + +int opa_value_compare_array(opa_array_t *a, opa_array_t *b) +{ + size_t a_len = opa_value_length_array(a); + size_t b_len = opa_value_length_array(b); + + size_t min = a_len; + + if (b_len < min) + { + min = b_len; + } + + for (long long i = 0; i < min; i++) + { + opa_value *e1 = opa_value_get_array_native(a, i); + opa_value *e2 = opa_value_get_array_native(b, i); + int cmp = opa_value_compare(e1, e2); + + if (cmp != 0) + { + return cmp; + } + } + + if (a_len < b_len) + { + return -1; + } + else if (a_len > b_len) + { + return 1; + } + return 0; +} + +int opa_value_compare_object(opa_object_t *a, opa_object_t *b) +{ + opa_array_t *a_keys = opa_object_keys(a); + opa_array_t *b_keys = opa_object_keys(b); + size_t a_len = opa_value_length_array(a_keys); + size_t b_len = opa_value_length_array(b_keys); + size_t min = a_len; + + if (b_len < min) + { + min = b_len; + } + + int cmp; + + for (size_t i = 0; i < min; i++) + { + cmp = opa_value_compare(a_keys->elems[i].v, b_keys->elems[i].v); + + if (cmp != 0) + { + goto finish; + } + + opa_value *a_val = opa_value_get_object(a, a_keys->elems[i].v); + opa_value *b_val = opa_value_get_object(b, b_keys->elems[i].v); + + cmp = opa_value_compare(a_val, b_val); + + if (cmp != 0) + { + goto finish; + } + } + + if (a_len < b_len) + { + return -1; + } + else if (a_len > b_len) + { + return 1; + } + +finish: + opa_array_free(a_keys); + opa_array_free(b_keys); + return cmp; +} + +int opa_value_compare(opa_value *a, opa_value *b) +{ + if (a == NULL && b == NULL) + { + return 0; + } + else if (b == NULL) + { + return 1; + } + else if (a == NULL) + { + return -1; + } + + if (a->type < b->type) + { + return -1; + } + else if (b->type < a->type) + { + return 1; + } + + switch (a->type) + { + case OPA_NULL: + return 0; + case OPA_BOOLEAN: + { + opa_boolean_t *a1 = opa_cast_boolean(a); + opa_boolean_t *b1 = opa_cast_boolean(b); + return a1->v - b1->v; + } + case OPA_NUMBER: + { + opa_number_t *a1 = opa_cast_number(a); + opa_number_t *b1 = opa_cast_number(b); + return opa_value_compare_number(a1, b1); + } + case OPA_STRING: + { + opa_string_t *a1 = opa_cast_string(a); + opa_string_t *b1 = opa_cast_string(b); + return opa_value_compare_string(a1, b1); + } + case OPA_ARRAY: + { + opa_array_t *a1 = opa_cast_array(a); + opa_array_t *b1 = opa_cast_array(b); + return opa_value_compare_array(a1, b1); + } + case OPA_OBJECT: + { + opa_object_t *a1 = opa_cast_object(a); + opa_object_t *b1 = opa_cast_object(b); + return opa_value_compare_object(a1, b1); + } + default: + { + opa_abort("illegal value"); + return 0; + } + } +} + +int opa_value_not_equal(opa_value *a, opa_value *b) +{ + return opa_value_compare(a, b) != 0; +} + +void opa_value_free(opa_value *node) +{ + switch (node->type) + { + case OPA_NULL: + opa_free(node); + return; + case OPA_BOOLEAN: + opa_free(opa_cast_boolean(node)); + return; + case OPA_NUMBER: + opa_free(opa_cast_number(node)); + return; + case OPA_STRING: + opa_free(opa_cast_string(node)); + return; + case OPA_ARRAY: + opa_array_free(opa_cast_array(node)); + return; + case OPA_OBJECT: + opa_object_free(opa_cast_object(node)); + return; + } +} + +int opa_value_boolean(opa_value *node) +{ + return opa_cast_boolean(node)->v; +} + +long long opa_value_int(opa_value *node) +{ + return opa_cast_number(node)->v.i; +} + +double opa_value_float(opa_value *node) +{ + return opa_cast_number(node)->v.f; +} + +const char *opa_value_string(opa_value *node) +{ + return opa_cast_string(node)->v; +} + +opa_value *opa_null() +{ + opa_value *ret = (opa_value *)opa_malloc(sizeof(opa_value)); + ret->type = OPA_NULL; + return ret; +} + +opa_value *opa_boolean(int v) +{ + opa_boolean_t *ret = (opa_boolean_t *)opa_malloc(sizeof(opa_boolean_t)); + ret->hdr.type = OPA_BOOLEAN; + ret->v = v; + return &ret->hdr; +} + +opa_value *opa_number_int(long long v) +{ + opa_number_t *ret = (opa_number_t *)opa_malloc(sizeof(opa_number_t)); + ret->hdr.type = OPA_NUMBER; + ret->is_float = 0; + ret->v.i = v; + return &ret->hdr; +} + +opa_value *opa_number_float(double v) +{ + opa_number_t *ret = (opa_number_t *)opa_malloc(sizeof(opa_number_t)); + ret->hdr.type = OPA_NUMBER; + ret->is_float = 1; + ret->v.f = v; + return &ret->hdr; +} + +opa_value *opa_string(const char *v, size_t len) +{ + opa_string_t *ret = (opa_string_t *)opa_malloc(sizeof(opa_string_t)); + ret->hdr.type = OPA_STRING; + ret->len = len; + ret->v = v; + return &ret->hdr; +} + +opa_value *opa_string_terminated(const char *v) +{ + opa_string_t *ret = (opa_string_t *)opa_malloc(sizeof(opa_string_t)); + ret->hdr.type = OPA_STRING; + ret->len = opa_strlen(v); + ret->v = v; + return &ret->hdr; +} + +void __opa_array_grow(opa_array_t *arr) +{ + if (arr->cap == 0) + { + arr->cap = OPA_ARRAY_INITIAL_CAP; + } + else + { + arr->cap *= 2; + } + + opa_array_elem_t *elems = (opa_array_elem_t *)opa_malloc(arr->cap * sizeof(opa_array_elem_t)); + + for (int i = 0; i < arr->len; i++) + { + elems[i] = arr->elems[i]; + } + + opa_free(arr->elems); + arr->elems = elems; +} + +opa_value *opa_array() +{ + return opa_array_with_cap(0); +} + +opa_value *opa_array_with_cap(size_t cap) +{ + opa_array_t *ret = (opa_array_t *)opa_malloc(sizeof(opa_array_t)); + ret->hdr.type = OPA_ARRAY; + ret->len = 0; + ret->cap = cap; + ret->elems = NULL; + + if (ret->cap != 0) + { + __opa_array_grow(ret); + } + + return &ret->hdr; +} + +opa_value *opa_object() +{ + opa_object_t *ret = (opa_object_t *)opa_malloc(sizeof(opa_object_t)); + ret->hdr.type = OPA_OBJECT; + return &ret->hdr; +} + +void opa_array_free(opa_array_t *arr) +{ + if (arr->elems != NULL) + { + for (size_t i = 0; i < arr->len; i++) + { + opa_free(arr->elems[i].i); + } + + opa_free(arr->elems); + } + + opa_free(arr); +} + +void opa_array_append(opa_array_t *arr, opa_value *v) +{ + if (arr->len >= arr->cap) + { + __opa_array_grow(arr); + } + + size_t i = arr->len++; + arr->elems[i].i = opa_number_int(i); + arr->elems[i].v = v; +} + +void opa_array_sort(opa_array_t *arr, opa_compare_fn cmp_fn) +{ + for (size_t i = 1; i < arr->len; i++) + { + opa_array_elem_t elem = arr->elems[i]; + size_t j = i - 1; + + while (j >= 0 && cmp_fn(arr->elems[j].v, elem.v) > 0) + { + arr->elems[j + 1] = arr->elems[j]; + j = j - 1; + } + + arr->elems[j + 1] = elem; + } +} + +void opa_object_free(opa_object_t *obj) +{ + opa_object_elem_t *prev = NULL; + + for (opa_object_elem_t *curr = obj->head; curr != NULL; curr = curr->next) + { + if (prev != NULL) + { + opa_free(prev); + } + + prev = curr; + } + + if (prev != NULL) + { + opa_free(prev); + } +} + +opa_array_t *opa_object_keys(opa_object_t *obj) +{ + opa_array_t *ret = opa_cast_array(opa_array_with_cap(opa_value_length_object(obj))); + opa_object_elem_t *elem = opa_object_iter(obj, NULL); + + while (elem != NULL) + { + opa_array_append(ret, elem->k); + elem = opa_object_iter(obj, elem); + } + + opa_array_sort(ret, opa_value_compare); + return ret; +} + +opa_object_elem_t *__opa_object_elem_alloc(opa_value *k, opa_value *v) +{ + opa_object_elem_t *elem = (opa_object_elem_t *)opa_malloc(sizeof(opa_object_elem_t)); + elem->next = NULL; + elem->k = k; + elem->v = v; + return elem; +} + +void opa_object_insert(opa_object_t *obj, opa_value *k, opa_value *v) +{ + opa_object_elem_t *curr = NULL; + + if (obj->head != NULL) + { + for (curr = obj->head; curr->next != NULL; curr = curr->next) + { + if (opa_value_compare(curr->k, k) == 0) + { + curr->v = v; + return; + } + } + } + + opa_object_elem_t *new = __opa_object_elem_alloc(k, v); + + if (curr != NULL) + { + curr->next = new; + } + else + { + obj->head = new; + } +} + +opa_object_elem_t *opa_object_get(opa_object_t *obj, opa_value *key) +{ + for (opa_object_elem_t *curr = obj->head; curr != NULL; curr = curr->next) + { + if (opa_value_compare(curr->k, key) == 0) + { + return curr; + } + } + + return NULL; +} + +opa_object_elem_t *opa_object_iter(opa_object_t *obj, opa_object_elem_t *prev) +{ + if (prev == NULL) + { + return obj->head; + } + + return prev->next; +} diff --git a/wasm/src/value.h b/wasm/src/value.h new file mode 100644 index 0000000000..fedda8ef2d --- /dev/null +++ b/wasm/src/value.h @@ -0,0 +1,115 @@ +#ifndef OPA_VALUE_H +#define OPA_VALUE_H + +#include "std.h" + +#define OPA_NULL (1) +#define OPA_BOOLEAN (2) +#define OPA_NUMBER (3) +#define OPA_STRING (4) +#define OPA_ARRAY (5) +#define OPA_OBJECT (6) + +#define OPA_EXT_INT (1) +#define OPA_EXT_FLOAT (2) + +typedef struct opa_value opa_value; + +struct opa_value +{ + unsigned char type; +}; + +typedef struct +{ + opa_value hdr; + int v; +} opa_boolean_t; + +typedef struct +{ + opa_value hdr; + unsigned char is_float; + union { + long long i; + double f; + } v; +} opa_number_t; + +typedef struct +{ + opa_value hdr; + size_t len; + const char *v; +} opa_string_t; + +typedef struct +{ + opa_value *i; + opa_value *v; +} opa_array_elem_t; + +typedef struct +{ + opa_value hdr; + opa_array_elem_t *elems; + size_t len; + size_t cap; +} opa_array_t; + +typedef struct opa_object_elem_t opa_object_elem_t; + +struct opa_object_elem_t +{ + opa_value *k; + opa_value *v; + opa_object_elem_t *next; +}; + +typedef struct +{ + opa_value hdr; + opa_object_elem_t *head; +} opa_object_t; + +typedef int (*opa_compare_fn)(opa_value *, opa_value *t); + +#define opa_cast_boolean(v) container_of(v, opa_boolean_t, hdr) +#define opa_cast_number(v) container_of(v, opa_number_t, hdr) +#define opa_cast_string(v) container_of(v, opa_string_t, hdr) +#define opa_cast_array(v) container_of(v, opa_array_t, hdr) +#define opa_cast_object(v) container_of(v, opa_object_t, hdr) + +int opa_value_compare(opa_value *a, opa_value *b); +int opa_value_not_equal(opa_value *a, opa_value *b); +opa_value *opa_value_get(opa_value *node, opa_value *key); +opa_value *opa_value_iter(opa_value *node, opa_value *prev); +size_t opa_value_length(opa_value *node); +void opa_value_free(opa_value *node); + +long long opa_value_int(opa_value *node); +double opa_value_float(opa_value *node); +const char *opa_value_string(opa_value *node); +int opa_value_boolean(opa_value *node); + +opa_value *opa_null(); +opa_value *opa_boolean(int v); +opa_value *opa_number_int(long long v); +opa_value *opa_number_float(double v); +opa_value *opa_string(const char *v, size_t len); +opa_value *opa_string_terminated(const char *v); +opa_value *opa_array(); +opa_value *opa_array_with_cap(size_t cap); +opa_value *opa_object(); + +void opa_array_free(opa_array_t *arr); +void opa_array_append(opa_array_t *arr, opa_value *v); +void opa_array_sort(opa_array_t *arr, opa_compare_fn cmp_fn); + +void opa_object_free(opa_object_t *obj); +opa_array_t *opa_object_keys(opa_object_t *obj); +void opa_object_insert(opa_object_t *obj, opa_value *k, opa_value *v); +opa_object_elem_t *opa_object_get(opa_object_t *obj, opa_value *key); +opa_object_elem_t *opa_object_iter(opa_object_t *obj, opa_object_elem_t *prev); + +#endif \ No newline at end of file diff --git a/wasm/test.js b/wasm/test.js new file mode 100644 index 0000000000..2691da063b --- /dev/null +++ b/wasm/test.js @@ -0,0 +1,113 @@ +const { readFileSync } = require('fs'); + +function stringDecoder(mem) { + return function(addr) { + const i8 = new Int8Array(mem.buffer); + const start = addr; + var s = ""; + while (i8[addr] != 0) { + s += String.fromCharCode(i8[addr++]); + } + return s; + } +} + +function red(text) { + return '\x1b[0m\x1b[31m' + text + '\x1b[0m'; +} + +function green(text) { + return '\x1b[0m\x1b[32m' + text + '\x1b[0m'; +} + +function yellow(text) { + return '\x1b[0m\x1b[33m' + text + '\x1b[0m'; +} + +function namespace(cache, func, note) +{ + let key = func+note; + if (key in cache) { + cache[key] += 1; + note = note + ' (' + cache[key] + ')' + } else { + cache[key] = 0; + } + return note; +} + +function report(passed, error, msg) { + if (passed === true) { + if (process.env.VERBOSE === '1') { + console.log(green('PASS'), msg); + } + } else if (error === undefined) { + console.log(yellow('FAIL'), msg); + } else { + console.log(red('ERROR'), msg, error); + } +} + +async function test(executable) { + + const mem = new WebAssembly.Memory({initial: 5}); + const addr2string = stringDecoder(mem); + + let cache = {}; + let failedOrErrored = 0; + let seenFuncs = {}; + + const module = await WebAssembly.instantiate(readFileSync(executable), { + env: { + memory: mem, + opa_println: (msg) => { + console.log(addr2string(msg)); + }, + opa_abort: (msg) => { + throw 'abort: ' + addr2string(msg); + }, + opa_test_pass: (note, func) => { + note = addr2string(note); + func = addr2string(func); + note = namespace(cache, func, note); + seenFuncs[func] = true; + let key = func + '/' + note + report(true, undefined, key); + }, + opa_test_fail: (note, func, file, line) => { + note = addr2string(note); + func = addr2string(func); + note = namespace(cache, func, note); + seenFuncs[func] = true; + let key = func + '/' + note; + failedOrErrored++; + report(false, undefined, key + ' ' + addr2string(file) + ':' + line); + }, + } + }); + + for (let key in module.instance.exports) { + if (key.startsWith("test_")) { + try { + module.instance.exports[key](); + if (!(key in seenFuncs)) + { + report(true, undefined, key); + } + } catch(e) { + report(false, e, key) + } + } + } + + if (failedOrErrored > 0) { + process.exit(1); + } +} + +if (process.argv.length != 3) { + console.log(process.argv[1] + " "); + process.exit(1); +} + +test(process.argv[2]); diff --git a/wasm/tests/test.c b/wasm/tests/test.c new file mode 100644 index 0000000000..7baa348d3c --- /dev/null +++ b/wasm/tests/test.c @@ -0,0 +1,493 @@ +#include "string.h" +#include "json.h" + +void opa_test_fail(const char *note, const char *func, const char *file, int line); +void opa_test_pass(const char *note, const char *func); + +#define test_fatal(note) \ + { \ + opa_test_fail(note, __func__, __FILE__, __LINE__); \ + return; \ + } + +#define test(note, expr) \ + if (!(expr)) \ + { \ + opa_test_fail(note, __func__, __FILE__, __LINE__); \ + } \ + else \ + { \ + opa_test_pass(note, __func__); \ + } + +void test_opa_strlen() +{ + test("empty", opa_strlen("") == 0); + test("non-empty", opa_strlen("1234") == 4); +} + +void test_opa_strncmp() +{ + test("empty", opa_strncmp("", "", 0) == 0); + test("equal", opa_strncmp("1234", "1234", 4) == 0); + test("less than", opa_strncmp("1234", "1243", 4) < 0); + test("greater than", opa_strncmp("1243", "1234", 4) > 0); +} + +void test_opa_strcmp() +{ + test("empty", opa_strcmp("", "") == 0); + test("equal", opa_strcmp("abcd", "abcd") == 0); + test("less than", opa_strcmp("1234", "1243") < 0); + test("greater than", opa_strcmp("1243", "1234") > 0); + test("shorter", opa_strcmp("123", "1234") < 0); + test("longer", opa_strcmp("1234", "123") > 0); +} + +int lex_crunch(const char *s) +{ + opa_json_lex ctx; + opa_json_lex_init(s, opa_strlen(s), &ctx); + return opa_json_lex_read(&ctx); +} + +void test_opa_lex_tokens() +{ + test("empty", lex_crunch("") == OPA_JSON_TOKEN_EOF); + test("space", lex_crunch(" ") == OPA_JSON_TOKEN_EOF); + test("tab", lex_crunch("\t") == OPA_JSON_TOKEN_EOF); + test("newline", lex_crunch("\n") == OPA_JSON_TOKEN_EOF); + test("carriage return", lex_crunch("\r") == OPA_JSON_TOKEN_EOF); + test("null", lex_crunch("null") == OPA_JSON_TOKEN_NULL); + test("true", lex_crunch("true") == OPA_JSON_TOKEN_TRUE); + test("false", lex_crunch("false") == OPA_JSON_TOKEN_FALSE); + + test("bad unicode", lex_crunch("\" \\uabcx \"") == OPA_JSON_TOKEN_ERROR); // not hex + test("object start", lex_crunch(" { ") == OPA_JSON_TOKEN_OBJECT_START); + test("object end", lex_crunch(" } ") == OPA_JSON_TOKEN_OBJECT_END); + test("array start", lex_crunch(" [ ") == OPA_JSON_TOKEN_ARRAY_START); + test("array end", lex_crunch(" ] ") == OPA_JSON_TOKEN_ARRAY_END); + test("element separator", lex_crunch(" , ") == OPA_JSON_TOKEN_COMMA); + test("item separator", lex_crunch(" : ") == OPA_JSON_TOKEN_COLON); +} + +int lex_buffer_crunch(const char *s, const char *exp, int token) +{ + opa_json_lex ctx; + opa_json_lex_init(s, opa_strlen(s), &ctx); + + if (opa_json_lex_read(&ctx) != token) + { + return -1; + } + + size_t exp_len = opa_strlen(exp); + size_t buf_len = ctx.buf_end - ctx.buf; + + if (exp_len != buf_len) + { + return -2; + } + + if (opa_strncmp(ctx.buf, exp, buf_len) != 0) + { + return -3; + } + + return 0; +} + +#define test_lex_buffer(note, s, exp, token) test(note, (lex_buffer_crunch(s, exp, token) == 0)) + +void test_opa_lex_buffer() +{ + test_lex_buffer("zero", "0", "0", OPA_JSON_TOKEN_NUMBER); + test_lex_buffer("signed zero", "-0", "-0", OPA_JSON_TOKEN_NUMBER); + test_lex_buffer("integers", "1234567890", "1234567890", OPA_JSON_TOKEN_NUMBER); + test_lex_buffer("signed integers", "-1234567890", "-1234567890", OPA_JSON_TOKEN_NUMBER); + test_lex_buffer("floats", "0.1234567890", "0.1234567890", OPA_JSON_TOKEN_NUMBER); + test_lex_buffer("signed floats", "-0.1234567890", "-0.1234567890", OPA_JSON_TOKEN_NUMBER); + test_lex_buffer("exponents", "-0.1234567890e0", "-0.1234567890e0", OPA_JSON_TOKEN_NUMBER); + test_lex_buffer("exponents", "-0.1234567890E+1000", "-0.1234567890E+1000", OPA_JSON_TOKEN_NUMBER); + test_lex_buffer("exponents", "-0.1234567890E-1000", "-0.1234567890E-1000", OPA_JSON_TOKEN_NUMBER); + test_lex_buffer("empty string", "\"\"", "", OPA_JSON_TOKEN_STRING); + test_lex_buffer("escaped quote", "\"\\\"\"", "\\\"", OPA_JSON_TOKEN_STRING); + test_lex_buffer("escaped reverse solidus", "\"\\\\\"", "\\\\", OPA_JSON_TOKEN_STRING); + test_lex_buffer("escaped solidus", "\"\\/\"", "\\/", OPA_JSON_TOKEN_STRING); + test_lex_buffer("escaped backspace", "\"\\b\"", "\\b", OPA_JSON_TOKEN_STRING); + test_lex_buffer("escaped feed forward", "\"\\f\"", "\\f", OPA_JSON_TOKEN_STRING); + test_lex_buffer("escaped line feed", "\"\\n\"", "\\n", OPA_JSON_TOKEN_STRING); + test_lex_buffer("escaped carriage return", "\"\\r\"", "\\r", OPA_JSON_TOKEN_STRING); + test_lex_buffer("escaped tab", "\"\\t\"", "\\t", OPA_JSON_TOKEN_STRING); + test_lex_buffer("plain", "\"abcdefg\"", "abcdefg", OPA_JSON_TOKEN_STRING); +} + +void test_opa_value_compare() +{ + test("none", opa_value_compare(NULL, NULL) == 0); + test("none/some", opa_value_compare(NULL, opa_null()) < 0); + test("some/none", opa_value_compare(opa_null(), NULL) > 0); + test("null", opa_value_compare(opa_null(), opa_null()) == 0); + test("null/boolean", opa_value_compare(opa_boolean(TRUE), opa_null()) > 0); + test("true/true", opa_value_compare(opa_boolean(TRUE), opa_boolean(TRUE)) == 0); + test("true/false", opa_value_compare(opa_boolean(TRUE), opa_boolean(FALSE)) > 0); + test("false/true", opa_value_compare(opa_boolean(FALSE), opa_boolean(TRUE)) < 0); + test("false/false", opa_value_compare(opa_boolean(FALSE), opa_boolean(FALSE)) == 0); + test("number/boolean", opa_value_compare(opa_number_int(100), opa_boolean(TRUE)) > 0); + test("integers", opa_value_compare(opa_number_int(100), opa_number_int(99)) > 0); + test("integers", opa_value_compare(opa_number_int(100), opa_number_int(101)) < 0); + test("integers", opa_value_compare(opa_number_int(100), opa_number_int(100)) == 0); + test("integers", opa_value_compare(opa_number_int(-100), opa_number_int(100)) < 0); + test("integers", opa_value_compare(opa_number_int(-100), opa_number_int(-101)) > 0); + test("integer/float", opa_value_compare(opa_number_int(100), opa_number_float(100.1)) < 0); + test("floats", opa_value_compare(opa_number_float(100.2), opa_number_float(100.1)) > 0); + test("floats", opa_value_compare(opa_number_float(100.2), opa_number_float(100.3)) < 0); + test("floats", opa_value_compare(opa_number_float(100.3), opa_number_float(100.3)) == 0); + test("string/number", opa_value_compare(opa_string_terminated("foo"), opa_number_float(100)) > 0); + test("strings", opa_value_compare(opa_string_terminated("foo"), opa_string_terminated("foo")) == 0); + test("strings", opa_value_compare(opa_string_terminated("foo"), opa_string_terminated("bar")) > 0); + test("strings", opa_value_compare(opa_string_terminated("bar"), opa_string_terminated("baz")) < 0); + test("strings", opa_value_compare(opa_string_terminated("foobar"), opa_string_terminated("foo")) > 0); + test("strings", opa_value_compare(opa_string_terminated("foo"), opa_string_terminated("foobar")) < 0); + + opa_array_t *arr1 = opa_cast_array(opa_array()); + opa_array_append(arr1, opa_number_int(1)); + opa_array_append(arr1, opa_number_int(2)); + opa_array_append(arr1, opa_number_int(3)); + + opa_array_t *arr2 = opa_cast_array(opa_array()); + opa_array_append(arr2, opa_number_int(1)); + opa_array_append(arr2, opa_number_int(3)); + opa_array_append(arr2, opa_number_int(2)); + + opa_array_t *arr3 = opa_cast_array(opa_array()); + opa_array_append(arr2, opa_number_int(1)); + opa_array_append(arr2, opa_number_int(3)); + + opa_value *v1, *v2, *v3; + v1 = &arr1->hdr; + v2 = &arr2->hdr; + v3 = &arr3->hdr; + + test("array/string", opa_value_compare(v1, opa_string_terminated("a")) > 0); + test("arrays", opa_value_compare(v1, v1) == 0); + test("arrays", opa_value_compare(v1, v2) < 0); + test("arrays", opa_value_compare(v2, v1) > 0); + test("arrays", opa_value_compare(v3, v2) < 0); + test("arrays", opa_value_compare(v2, v3) > 0); + + opa_object_t *obj1 = opa_cast_object(opa_object()); + opa_object_insert(obj1, opa_string_terminated("a"), opa_number_int(1)); + opa_object_insert(obj1, opa_string_terminated("b"), opa_number_int(2)); + + opa_object_t *obj2 = opa_cast_object(opa_object()); + opa_object_insert(obj2, opa_string_terminated("a"), opa_number_int(1)); + opa_object_insert(obj2, opa_string_terminated("b"), opa_number_int(3)); + + opa_object_t *obj3 = opa_cast_object(opa_object()); + opa_object_insert(obj3, opa_string_terminated("a"), opa_number_int(1)); + opa_object_insert(obj3, opa_string_terminated("c"), opa_number_int(3)); + + opa_object_t *obj4 = opa_cast_object(opa_object()); + opa_object_insert(obj4, opa_string_terminated("a"), opa_number_int(1)); + opa_object_insert(obj4, opa_string_terminated("b"), opa_number_int(2)); + opa_object_insert(obj4, opa_string_terminated("c"), opa_number_int(3)); + + v1 = &obj1->hdr; + v2 = &obj2->hdr; + v3 = &obj3->hdr; + opa_value *v4 = &obj4->hdr; + + test("object/array", opa_value_compare(v1, opa_array()) > 0); + test("objects", opa_value_compare(v1, v1) == 0); + test("objects", opa_value_compare(v1, v2) < 0); + test("objects", opa_value_compare(v2, v3) < 0); + test("objects", opa_value_compare(v4, v1) > 0); + test("objects", opa_value_compare(v4, v2) < 0); +} + +int parse_crunch(const char *s, opa_value *exp) +{ + opa_value *ret = opa_json_parse(s, opa_strlen(s)); + if (ret == NULL) + { + return 0; + } + return opa_value_compare(exp, ret) == 0; +} + +void test_opa_json_parse_scalar() +{ + test("null", parse_crunch("null", opa_null())); + test("true", parse_crunch("true", opa_boolean(TRUE))); + test("false", parse_crunch("false", opa_boolean(FALSE))); + test("strings", parse_crunch("\"hello\"", opa_string_terminated("hello"))); + test("integers", parse_crunch("0", opa_number_int(0))); + test("integers", parse_crunch("123456789", opa_number_int(123456789))); + test("signed integers", parse_crunch("-0", opa_number_int(0))); + test("signed integers", parse_crunch("-123456789", opa_number_int(-123456789))); + test("floats", parse_crunch("16.7", opa_number_float(16.7))); + test("signed floats", parse_crunch("-16.7", opa_number_float(-16.7))); + test("exponents", parse_crunch("6e7", opa_number_float(6e7))); +} + +opa_array_t *fixture_array1() +{ + opa_array_t *arr = opa_cast_array(opa_array()); + opa_array_append(arr, opa_number_int(1)); + opa_array_append(arr, opa_number_int(2)); + opa_array_append(arr, opa_number_int(3)); + opa_array_append(arr, opa_number_int(4)); + return arr; +} + +opa_array_t *fixture_array2() +{ + opa_array_t *arr1 = opa_cast_array(opa_array()); + opa_array_append(arr1, opa_number_int(1)); + opa_array_append(arr1, opa_number_int(2)); + opa_array_append(arr1, opa_number_int(3)); + opa_array_append(arr1, opa_number_int(4)); + + opa_array_t *arr2 = opa_cast_array(opa_array()); + opa_array_append(arr2, opa_number_int(5)); + opa_array_append(arr2, opa_number_int(6)); + opa_array_append(arr2, opa_number_int(7)); + opa_array_append(arr2, opa_number_int(8)); + + opa_array_t *arr = opa_cast_array(opa_array()); + opa_array_append(arr, &arr1->hdr); + opa_array_append(arr, &arr2->hdr); + + return arr; +} + +opa_object_t *fixture_object1() +{ + opa_object_t *obj = opa_cast_object(opa_object()); + opa_object_insert(obj, opa_string_terminated("a"), opa_number_int(1)); + opa_object_insert(obj, opa_string_terminated("b"), opa_number_int(2)); + return obj; +} + +opa_object_t *fixture_object2() +{ + opa_object_t *obj1 = opa_cast_object(opa_object()); + opa_object_insert(obj1, opa_string_terminated("c"), opa_number_int(1)); + opa_object_insert(obj1, opa_string_terminated("d"), opa_number_int(2)); + + opa_object_t *obj2 = opa_cast_object(opa_object()); + opa_object_insert(obj2, opa_string_terminated("e"), opa_number_int(3)); + opa_object_insert(obj2, opa_string_terminated("f"), opa_number_int(4)); + + opa_object_t *obj = opa_cast_object(opa_object()); + opa_object_insert(obj, opa_string_terminated("a"), &obj1->hdr); + opa_object_insert(obj, opa_string_terminated("b"), &obj2->hdr); + return obj; +} + +void test_opa_value_length() +{ + opa_array_t *arr = fixture_array1(); + opa_object_t *obj = fixture_object1(); + + test("arrays", opa_value_length(&arr->hdr) == 4); + test("objects", opa_value_length(&obj->hdr) == 2); +} + +void test_opa_value_get_array() +{ + opa_array_t *arr = fixture_array1(); + + for (int i = 0; i < 4; i++) + { + opa_value *result = opa_value_get(&arr->hdr, opa_number_int(i)); + + if (result == NULL) + { + test_fatal("array get failed"); + } + + if (opa_value_compare(result, opa_number_int(i + 1)) != 0) + { + test_fatal("array get returned bad value"); + } + } + + opa_value *result = opa_value_get(&arr->hdr, opa_string_terminated("foo")); + + if (result != NULL) + { + test_fatal("array get returned unexpected result"); + } + + result = opa_value_get(&arr->hdr, opa_number_float(3.14)); + + if (result != NULL) + { + test_fatal("array get returned unexpected result"); + } + + result = opa_value_get(&arr->hdr, opa_number_int(-1)); + + if (result != NULL) + { + test_fatal("array get returned unexpected result"); + } + + result = opa_value_get(&arr->hdr, opa_number_int(4)); + + if (result != NULL) + { + test_fatal("array get returned unexpected result"); + } +} + +void test_opa_array_sort() +{ + opa_array_t *arr = opa_cast_array(opa_array()); + + opa_array_append(arr, opa_number_int(4)); + opa_array_append(arr, opa_number_int(3)); + opa_array_append(arr, opa_number_int(2)); + opa_array_append(arr, opa_number_int(1)); + + opa_array_sort(arr, opa_value_compare); + + opa_value *res = &arr->hdr; + opa_value *exp = &fixture_array1()->hdr; + + if (opa_value_compare(res, exp) != 0) + { + test_fatal("array sort returned unexpected result"); + } +} + +void test_opa_value_get_object() +{ + opa_object_t *obj = fixture_object1(); + + const char *keys[2] = { + "a", + "b", + }; + + long long values[2] = { + 1, + 2, + }; + + for (int i = 0; i < sizeof(keys) / sizeof(const char *); i++) + { + opa_value *result = opa_value_get(&obj->hdr, opa_string_terminated(keys[i])); + + if (result == NULL) + { + test_fatal("object get failed"); + } + + if (opa_value_compare(result, opa_number_int(values[i])) != 0) + { + test_fatal("object get returned bad value"); + } + } + + opa_value *result = opa_value_get(&obj->hdr, opa_string_terminated("non-existent")); + + if (result != NULL) + { + test_fatal("object get returned unexpected result"); + } +} + +void test_opa_json_parse_composites() +{ + + opa_value *empty_arr = opa_array(); + + test("empty array", parse_crunch("[]", empty_arr)); + test("array", parse_crunch("[1,2,3,4]", &fixture_array1()->hdr)); + test("array nested", parse_crunch("[[1,2,3,4],[5,6,7,8]]", &fixture_array2()->hdr)); + + opa_value *empty_obj = opa_object(); + + test("empty object", parse_crunch("{}", empty_obj)); + test("object", parse_crunch("{\"a\": 1, \"b\": 2}", &fixture_object1()->hdr)); + test("object nested", parse_crunch("{\"a\": {\"c\": 1, \"d\": 2}, \"b\": {\"e\": 3, \"f\": 4}}", &fixture_object2()->hdr)); +} + +void test_opa_object_insert() +{ + + opa_object_t *obj = opa_cast_object(opa_object()); + + opa_object_insert(obj, opa_string_terminated("a"), opa_number_int(1)); + opa_object_insert(obj, opa_string_terminated("b"), opa_number_int(2)); + opa_object_insert(obj, opa_string_terminated("a"), opa_number_int(3)); + + opa_value *v1 = opa_value_get(&obj->hdr, opa_string_terminated("a")); + + if (opa_value_compare(v1, opa_number_int(3)) != 0) + { + test_fatal("object insert did not replace value") + } +} + +void test_opa_value_iter_object() +{ + opa_object_t *obj = fixture_object1(); + + opa_value *k1 = opa_value_iter(&obj->hdr, NULL); + opa_value *k2 = opa_value_iter(&obj->hdr, k1); + opa_value *k3 = opa_value_iter(&obj->hdr, k2); + + opa_value *exp1 = opa_string_terminated("a"); + opa_value *exp2 = opa_string_terminated("b"); + opa_value *exp3 = NULL; + + if (opa_value_not_equal(k1, exp1)) + { + test_fatal("object iter start did not return expected value"); + } + + if (opa_value_not_equal(k2, exp2)) + { + test_fatal("object iter second did not return expected value"); + } + + if (opa_value_not_equal(k3, exp3)) + { + test_fatal("object iter third did not return expected value"); + } +} + +void test_opa_value_iter_array() +{ + opa_array_t *arr = opa_cast_array(opa_array()); + + opa_array_append(arr, opa_number_int(1)); + opa_array_append(arr, opa_number_int(2)); + + opa_value *k1 = opa_value_iter(&arr->hdr, NULL); + opa_value *k2 = opa_value_iter(&arr->hdr, k1); + opa_value *k3 = opa_value_iter(&arr->hdr, k2); + + opa_value *exp1 = opa_number_int(0); + opa_value *exp2 = opa_number_int(1); + opa_value *exp3 = NULL; + + if (opa_value_not_equal(k1, exp1)) + { + test_fatal("array iter start did not return expected value"); + } + + if (opa_value_not_equal(k2, exp2)) + { + test_fatal("array iter second did not return expected value"); + } + + if (opa_value_not_equal(k3, exp3)) + { + test_fatal("array iter third did not return expected value"); + } +} diff --git a/wasm/tests/undefined.symbols b/wasm/tests/undefined.symbols new file mode 100644 index 0000000000..506ba2c60d --- /dev/null +++ b/wasm/tests/undefined.symbols @@ -0,0 +1,4 @@ +opa_test_pass +opa_test_fail +opa_abort +opa_println \ No newline at end of file